Notice: a subtle footgun: UnionAll type variable order + method static parameter normalization

nsajko
Fri, 03 May 2024 08:33:29 +0000
General Usage

Consider this snippet:

id(::Type{T}) where {T} = T
some_type{7,Int} == id(some_type){7,Int}

Naively, it seems like the comparison on the second line should return true for any type some_type, assuming neither type application throws. After all, id is the identity function, so the second line should be equivalent to the obvious tautology some_type{7,Int} == some_type{7,Int}, right?

Well, id is the identity in the ==-sense, but not in the ===-sense. And == between UnionAll types isn’t affected by type variable order. Thus:

julia> some_type = AbstractArray{T,N} where {N,T}  # note the reversed type variable order
AbstractArray{T, N} where {N, T}

julia> id(::Type{T}) where {T} = T
id (generic function with 1 method)

julia> some_type{7,Int} == id(some_type){7,Int}
false

julia> some_type == id(some_type)
true

julia> some_type === id(some_type)
false

julia> AbstractArray === id(some_type)
true

julia> some_type === identity(some_type)
true

So id(some_unionall) effectively normalizes the type, and thus the unionall type variable order. I guess my conclusion here is to take care to do the normalization as soon as possible, to prevent shooting myself in the foot.

EDIT: a similar example:

julia> T = AbstractArray{T,N} where {N,T}
AbstractArray{T, N} where {N, T}

julia> f(::Type{S}) where {S<:T} = S === T
f (generic function with 1 method)

julia> f(T)
false

3 posts - 2 participants