Well, it does have the slight problem:
d=Dict(); d[1]=1; d[nothing]=2;
d
#Dict{Any,Any} with 2 entries:
# nothing => 2
# 1 => 1
@show findfirst(equalto(0), d)
#findfirst(equalto(0), d) = nothing
@show findfirst(equalto(1), d)
#findfirst(equalto(1), d) = 1
@show findfirst(equalto(2), d)
#findfirst(equalto(2), d) = nothing
2 Likes
That’s not true, if nothing
can be an index (key) into the collection.
All the change did was hurt performance and move the place where things break down from one place (where 0 might be a valid index [now possible with OffsetArrays
, or other AbstractArrays
)
I think a better interface, that would not lose any performance, and would be able to handle the case of nothing
being an index (where I worked we actually used that, to store JSON
null
as a key in AssociativeArrays
s), would be to add two functions, found(collection, retval)
and found_key(collection, retval)
(and maybe a macro to make life a bit easier).
If a particular type can use something like 0
, or typemin(Int)
as a in-band sentinel value for a not-found index/key, for example with String
, then you’d have the definition: found(::Type{String}, val) = val != 0
and found_key(::Type{String}, val) = val
.
For something where no in-band sentinel value is possible (i.e. the key is <: Any
, or any union that includes Nothing
), then the return value can simply be a tuple, i.e. (true, value)
or (false, nothing)
. If nothing
is found, it would return (true, nothing)
, and everything works correctly, no funky special cases. The definitions then would be found(::Type{MyType}, ret) = ret[1]
, and found_key(::Type{MyType}, ret) = ret[2]
.