Type compare

As shown blow:

(Int,10) isa Tuple{Type{Int},Int}
False

So, how can I define a T,

function f(x::T) where T <: Tuple{Type{Int},Int}
end

Of course, this would work T <: Tuple{DataType,Int}, but it is limited. Is there other ways?

1 Like

but it is limited

I’m not sure what you mean by “limited”. In what way is it limited, what would you rather have it “include”?

I’m curious, what do you need this for? Is this for specifying the signature of some function? Seems dubious to include the type of one of the arguments as an argument itself (i.e. 10 is an Int, why include Int as an argument then?).

Maybe the following can be helpful / insightful:

julia> f(::Type{T}, x::T) where {T<:Signed} = 2x
f (generic function with 1 method)

julia> f(Int, 3)
6

julia> f(Int, 3.0)
ERROR: MethodError: no method matching f(::Type{Int64}, ::Float64)
Closest candidates are:
  f(::Type{T}, ::T) where T<:Signed at REPL[4]:1
Stacktrace:
 [1] top-level scope
   @ REPL[6]:1

julia> f(Float64, 3)
ERROR: MethodError: no method matching f(::Type{Float64}, ::Int64)
Closest candidates are:
  f(::Type{T}, ::T) where T<:Signed at REPL[4]:1
Stacktrace:
 [1] top-level scope
   @ REPL[7]:1

julia> f(Float64, 3.0)
ERROR: MethodError: no method matching f(::Type{Float64}, ::Float64)
Stacktrace:
 [1] top-level scope
   @ REPL[8]:1

DataType is too general, that would make the dispatch inconvinient.
For example:

f(x::T) where T <: Tuple{Type{Int},Int}
    println(1)
end

f(x::T) where T <: Tuple{Type{Float64},Int}
   println(2)
end

While when using DataType, I can not do it.

I might misunderstand what you mean, but the following would be the right way to make use of type dispatch to achieve what you want:

julia> f(x::Int) = println(1)
f (generic function with 1 method)

julia> f(x::Float64) = println(2)
f (generic function with 2 methods)

julia> f(1)
1

julia> f(1.0)
2
1 Like

The situation I met is a little more complicated. To copy all codes here may be unrelizable, but in simple words, I am finding a way to define T <: Tuple{some_custom_struct_name, some_instance_of_other_struct}

Without any context, DataType would work:

julia> struct A
       x
       end

julia> struct B
       x
       end

julia> typeof((A, B(1)))
Tuple{DataType, B}

julia> (A, B(1)) isa Tuple{DataType, B}
true

But I have no idea what this would serve or how exactly this is too “limited” (I think you mean the opposite, permissive).

It does sound a lot like a question about multiple dispatching. Try to extract a MWE from your full case. Take your time to read Please read: make it easier to help you and I’m sure we’d be able to help you.

I think people have tried hard enough to find the XY problem here. The author insists that this is useful to them. I am also stumped by this behavior:

julia> Int isa Type{Int}
true

julia> (Int,) isa Tuple{Type}
true

julia> (Int,) isa Tuple{Type{Int}}
false

Why is the last line false?

2 Likes

typeof(Int) is DataType, and its supertype hierarchy goes:

julia> typeof(Int) |> supertypes
(DataType, Type{T}, Any)

And even though Int isa Type{Int}, checking whether typeof(Int) <: Type{Int} returns false. I don’t really understand the relationship between DataType and Type{Int}, especially in the context of putting it to use here.

Jeff Bezanson in a 2018 comment says:

types like DataType and Type{Int} have an overly-complex subtype and specificity relationship: one is not a subtype of the other, but their intersection is non-empty, and we’re not able to accurately represent that intersection. For various reasons (including backwards compatibility) subtyping currently gets this wrong on purpose, making Type{Int} <: DataType true.

which at least assures me that it really is (known to be) a complex relationship. I also read through the Julia Manual’s Types page to figure out this relationship, but it doesn’t go into much detail about it either.

1 Like

The impression I get from issues like #11535, #10947, #29368 , and this one is that it’s not possible to dispatch on tuples containings types as elements, with the kind of specificity OP asks for. (The first two are closed issues from Julia 0.4/0.5 days, but the latter two issues refer to the same problem, and it seems like the first two were closed in a WONTFIX sense rather than as SOLVED, and the behaviour carried over into Julia 1.x versions too.)

Taking a hint from the suggestion in #11535, we can work around this using a helper method, where the tuple members are passed in as individual arguments rather than in a tuple.

julia> f(t::Tuple{DataType, Int}) = f(t[1], t[2])
f (generic function with 1 method)

julia> f(x::Type{Int}, y::Int) = @show(x, y)
f (generic function with 2 methods)

julia> f((Int, 10));
x = Int64
y = 10

julia> f((Float64, 10))
ERROR: MethodError: no method matching f(::Type{Float64}, ::Int64)
Closest candidates are:
  f(::Type{Int64}, ::Int64) at REPL[92]:1
...
1 Like

Other way around. This is the one example of an abstract type subtyping (and being more specific than) a concrete type I can think of.

julia> Type{Int} <: typeof(Int) # DataType
true
julia> Tuple{Type{Int}} <: Tuple{DataType}
true

As you quoted, it’s weird and doesn’t fit the wider subtyping rules, but this “hack” allows dispatch on specific types (as long as they’re not packaged in tuples). It would be more consistent with the rules if types were more like functions i.e. typeof(T) is a concrete parametric or singleton type that subtypes an abstract Type, but Julia v0 / v1 didn’t turn out that way.

The confusing part for me is I had always assumed that dispatch was done on a tuple of the function and the arguments, e.g. f(Int, 1.0) dispatches on Tuple{typeof(f), DataType, Float64}. And that’s perfectly capable of being dispatched to a method function f(T::Type{Int}, x::AbstractFloat). So there should be some explanation of how the dispatch to Type{Int} is really handled and why it doesn’t work when there’s an extra level of nested Tuple e.g. f( (Int, 1.0) ) dispatching on Tuple{typeof(f),Tuple{DataType,Float64}}.

1 Like