Inconsistency with `in(x, ::Interval)` and `iterate(::Interval)`
(originally from https://github.com/JuliaReach/ReachabilityAnalysis.jl/pull/373#issuecomment-733224612)
From doing ?in
in(item, collection) -> Bool
∈(item, collection) -> Bool
∋(collection, item) -> Bool
Determine whether an item is in the given collection, in the sense that it is == to one of the values generated by iterating over the collection. Returns a Bool value,
except if item is missing or collection contains missing but not item, in which case missing is returned (three-valued logic
(https://en.wikipedia.org/wiki/Three-valued_logic), matching the behavior of any and ==).
Some collections follow a slightly different definition. For example, Sets check whether the item isequal to one of the elements. Dicts look for key=>value pairs, and the
key is compared using isequal. To test for the presence of a key in a dictionary, use haskey or k in keys(dict). For these collections, the result is always a Bool and
never missing.
To help explain, I'll define
my_in(element, collection) = any(==(element), collection)
which perhaps is more clear as
my_in(element, collection) = any( element == x for x in collection)
and "consistency", and following the documentation of in, means that one of two should be the case:
my_inerrorsmy_inreturns the same answer asin
Interval of IntervalArithmetic.jl wants to both be set and a <:Number:
julia> supertypes(typeof(Interval(0,1)))
(Interval{Float64}, AbstractInterval{Float64}, Real, Number, Any)
and it's a fact of life (unfortunately? fortunately?) that <:Numbers are singleton collections of themselves: https://github.com/JuliaLang/julia/blob/6614645892f03915e4f10b051df9a228c980abc8/base/number.jl#L233 and so
julia> in(0.5, Interval(0, 1))
true
julia> my_in(0.5, Interval(0, 1))
false
One possibility is to just document this. I'd be curious to see if it wreaks any havoc to define new methods on Interval (iterate, length, first, last, maybe copy, ...) that error. This is safer and stricter, and ensures consistency with in.
OK, fair point I guess.
In some sense we're punning on the meaning of in. But clearly anybody would expect 0.5 ∈ (0..1) to be true.
This is another instantiation of #2 I guess. See also https://github.com/gwater/NumberIntervals.jl
That is the only reasonable definition of in, I agree, and furthermore it's consistent with a bunch of other set operations defined on Interval, like intersect (and to a lesser extent union and setdiff).
I don't propose another definition of in, just possibly another definition of iterate, etc. so that those methods error.
Thanks for pointing me to https://github.com/gwater/NumberIntervals.jl . I suspected it would have the same inconsistency since NumberInterval <: Number (by way of <: AbstractFloat), and indeed:
julia> using NumberIntervals: NumberInterval
julia> in(0.5, NumberInterval(0.0, 1.0))
true
julia> my_in(element, collection) = any(==(element), collection)
my_in (generic function with 1 method)
julia> my_in(0.5, NumberInterval(0.0, 1.0))
missing
I was curious about other interval packages, and it seems like the three others I found are not <:Number and so do not run into this inconsistency.
script:
import Intervals
import IntervalSets
import ClosedIntervals
import NumberIntervals
import IntervalArithmetic
my_in(element, collection) = any(==(element), collection)
function exception_wrap(f)
function wrapped(args...)
try
return (Some(f(args...)), nothing)
catch e
return (nothing, e)
end
end
return wrapped
end
function test_in(needle, haystack)
_my_in = exception_wrap(my_in)
mi = _my_in(needle, haystack)
i = in(needle, haystack)
return (i, mi)
end
intervals = Any[ # avoid https://github.com/JuliaIntervals/IntervalArithmetic.jl/issues/426
Intervals.Interval(0, 1),
IntervalSets.Interval(0, 1),
ClosedIntervals.ClosedInterval(0, 1),
NumberIntervals.NumberInterval(0, 1),
IntervalArithmetic.Interval(0, 1),
]
println(stdout, "\n-----\n")
show(stdout, "text/plain", test_in.(Ref(0.5), intervals))
println(stdout, "\n-----\n")
show(stdout, "text/plain", test_in.(Ref(1.5), intervals))