ERROR: MethodError: no method matching keys(::Base.EachLine{IOStream})
The function `keys` exists, but no method is defined for this combination of argument types.
I think this is a good example of where traits or interfaces would make things nicer. There could be separate implementations of findfirst for simple iterable sequences and ones that are maps of key=>value pairs (e.g. IsIterable and IsMap).
It’s not clear what findfirst should even mean for non-indexable collections, in general. For example, what should it do for an unordered collection where iteration order is arbitrary? Like:
findfirst(iszero, Set(0:10))
I think it is better in such cases for the caller to say explicitly what they want, e.g. have an Iterators.Enumerable(itr) wrapper that defines pairs for any collection in terms of enumeration, like:
struct Enumerable{I}
itr::I
end
Base.pairs(e::Enumerable) = enumerate(e.itr)
# plus other methods to make iterate(e::Enumerable) --> iterate(e.itr), etcetera
The orderedness question is not specific to non-indexable collections. Maps can be unordered too, such as Dict. Currently findfirst doesn’t care whether the order of the keys has any significance, it just returns the first match when iterating over the keys. Unless there is motivation to change that, the same behavior could be extended to non-indexable collections.
Admittedly, since findfirst returns an index, it perhaps doesn’t make a lot of sense to use it to a non-indexable collection.
Sometimes it’d be more convenient to have function that returns the first element that satisfies the predicate (instead of its index). In that case, indexability would not be a requirement.