[Breaking] Should `isequal(convert(T, x), x)` always be true?

In Julia 1.x, it seems like isequal(convert(T, x), x) is usually true for Base and standard library types. However, there is one notable exception: Char to Int and Int to Char. It is a little odd that this works:

julia> struct A

julia> A('a')

Dict and Set have an explicit workaround for cases where isequal(convert(T, x), x) is not true:

function setindex!(h::Dict{K,V}, v0, key0) where V where K
    if key0 isa K
        key = key0
        key = convert(K, key0)::K
        if !(isequal(key, key0)::Bool)
            throw(ArgumentError("$(limitrepr(key0)) is not a valid key for type $K"))
    setindex!(h, v0, key)

As you can see, the setindex! method tests to see whether the converted key isequal to the input key. You can see this check in action here:

julia> push!(Set('a'), 99)
ERROR: ArgumentError: 99 is not a valid key for type Char
 [1] setindex!(h::Dict{Char, Nothing}, v0::Nothing, key0::Int64)
   @ Base ./dict.jl:376
 [2] push!(s::Set{Char}, x::Int64)
   @ Base ./set.jl:67
 [3] top-level scope
   @ REPL[21]:1

Would it make sense in Julia 2.0 to add a note to the convert docstring that says that isequal(convert(T, x), x) should always be true? Are there any other examples of Base or standard library types for which isequal(convert(T, x), x) is not true?

The other main example is floating point types where we allow rounding in convert

julia> convert(Char, 100)
'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase)

julia> isequal('d', 100)

I would be quite surprised if isequal('d', 100) evaluated true

I can see the desire, but I would rather obtain it by making convert less permissive rather than making isequal more permissive. but even then, I think the same case is more strongly made for reinterpret than it is for convert

1 Like

I suppose I was not clear enough in my original post. My fix would be to remove the convert methods for Int -> Char and Char -> Int, not to make isequal more permissive.