# How to know what types will the type signature match?

When writing some function signatures, I sometimes want to know what types will match them.
For example,

``````f(::AbstractVector{Real}) = 1
f(::AbstractVector{<: Real}) = 2
``````

They are quite similar and sometimes could confuse. Is there a way for me to examine what types in the runtime will be sent to `1` and what will be sent to `2`? That’s really great help!

is this what you’re looking for?

``````julia> f(x::Integer) = x+1
f (generic function with 1 method)

julia> f(x::AbstractFloat) = x+2
f (generic function with 2 methods)

julia> methods(f(0.1))
# 0 methods for generic function "(::Float64)":

julia> methods(f(2))
# 0 methods for generic function "(::Int64)":
``````

I am afraid not. I do not quite understand what

``````julia> methods(f(0.1))
# 0 methods for generic function "(::Float64)":

julia> methods(f(2))
# 0 methods for generic function "(::Int64)":
``````

stand for.
In your example, `methods(f(0.1))` first evaluates `f(0.1)` then evaluates `methods((::Float64))` so I do not see their meaning.
I actually want to know the set of all types of `x` that can satisfy `f(x::Integer)` (of course it is straightforward in your example). Besides, you are assuming `f`'s output has the same type as the input type, but it is often hard to do that for a general `f` in practice.

Yes, the examples should probably have read something like:

``````julia> methods(f, (Float64,))
# 1 method for generic function "f":
[1] f(x::AbstractFloat) in Main at REPL[3]:1

julia> methods(f, (Int,))
# 1 method for generic function "f":
[1] f(x::Integer) in Main at REPL[2]:1
``````

But I think it does not really solve your problem: it tells you which method is used for a particular set of arguments types, rather than telling you the list of all possible argument types for a given method.

I don’t know how to solve your problem directly, but I would perhaps simplify it to the following one, which might be easier to solve: given a type T, is it possible to list all subtyes of T?

A good start for this simplified question would be to use `subtypes`:

``````julia> subtypes(AbstractVector{Real})
10-element Array{Any,1}:
AbstractRange{Real}
Base.LogicalIndex{Real,A} where A<:(AbstractArray{Bool,N} where N)
Base.ReinterpretArray{Real,1,S,A} where A<:AbstractArray{S,1} where S
Base.ReshapedArray{Real,1,P,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where P<:AbstractArray
Core.Compiler.AbstractRange{Real}
DenseArray{Real,1}
PermutedDimsArray{Real,1,perm,iperm,AA} where AA<:AbstractArray where iperm where perm
SparseArrays.AbstractSparseArray{Real,Ti,1} where Ti
SubArray{Real,1,P,I,L} where L where I where P
Test.GenericArray{Real,1}
``````
``````julia> subtypes(AbstractVector{<: Real})
14-element Array{Any,1}:
AbstractRange{T} where T<:Real
Base.LogicalIndex{T,A} where A<:(AbstractArray{Bool,N} where N) where T<:Real
Base.ReshapedArray{T,1,P,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where P<:AbstractArray where T<:Real
BitArray{1}
Core.Compiler.AbstractRange{T} where T<:Real
Core.Compiler.BitArray{1}
Core.Compiler.LinearIndices{1,R} where R<:Tuple{Core.Compiler.AbstractUnitRange{Int64}}
DenseArray{T,1} where T<:Real
LinearIndices{1,R} where R<:Tuple{AbstractUnitRange{Int64}}
PermutedDimsArray{T,1,perm,iperm,AA} where AA<:AbstractArray where iperm where perm where T<:Real
SparseArrays.AbstractSparseArray{Tv,Ti,1} where Ti where Tv<:Real
SubArray{T,1,P,I,L} where L where I where P where T<:Real
Test.GenericArray{T,1} where T<:Real
Union{ReinterpretArray{T,1,S,A} where A<:AbstractArray{S,1} where S, ReinterpretArray{T,1,S,A} where A<:AbstractArray{S,1} where S} where T<:Real
``````

(you could also apply it recursively to replace all abstract types in the list with the list of their own subtypes, but I would tend to think that this would make the list grow qhickly to unmanageable length)

1 Like

``````julia> subtypes(Vector{Integer})
0-element Array{Type,1}

julia> subtypes(Vector{<: Integer})
0-element Array{Type,1}
``````

They both give an empty array. However,

``````julia> subtypes(Integer)
3-element Array{Any,1}:
Bool
Signed
Unsigned

julia> Vector{Bool} <: Vector{<: Integer}
true

julia> Vector{Signed} <: Vector{<: Integer}
true

julia> Vector{Unsigned} <: Vector{<: Integer}
true
``````

Aren’t `Vector{Bool}`, `Vector{Signed}` and `Vector{Unsigned}` and their subtypes (if exist) the subtypes of `Vector{<: Integer}`? So, is this method incomplete or somewhere I made a mistake?

Ah, yes, that makes sense: `T1 <: T2` is true if all values of type `T1` are also of type `T2`. But this does not mean that `T1` has to be a subtype of `T2` (in the sense that the supertype of `T1` would be `T2`): there are many ways that `T1 <: T2` without `T1` and `T2` being related in the type hierarchy.

Besides the examples you gave, I can think of:

``````julia> subtypes(Union{Int32, Float32})
0-element Array{Type,1}

julia> Int32 <: Union{Int32, Float32}
true
``````

looking only at the type hierarchy, `Int32` has supertype `Signed`, which itself has supertype `Integer`, which itself has supertype `Real` and so on. Nowhere in the hierarchy does `Union{Int32, ...}` appear, yet obviously any `Int32` value `isa Union{Int32, Float32}`.

So you’re right: using `subtypes` is not going to help you with your problem. Hopefully someone more knowledgeable will chime in with another idea…

1 Like

this example can help a little:

``````abstract type AbstractExample{T} end

struct ConcreteExample{T} <: AbstractExample{T}
value::T
end

function test_x(x::AbstractExample{Real})
return 1
end
function test_x(x::AbstractExample{<:Real})
return 2
end

a = ConcreteExample{Real}(2)
#a have the parametric type as Real, not using the dispatch
#a = ConcreteExample{Real}(2)

b = ConcreteExample(2)
#b is using the type of 2 (Int64)
#b = ConcreteExample{Int64}(2)

test_x(a) ##1
test_x(b) ##2
``````

Real is a container (abstract) type, so only when you create an struct with the type Real, the first function it’s called. in any other case, a concrete implementation will be called, in this case a int64. the Real acts as a union of all subtypes (Integers,Rationals,Floats,etc)
here are some examples:

``````Vector{Real}<:AbstractVector{Real}  #true
Vector{Float64}<:AbstractVector{<:Real} #also true

``````

Edit: added some dank images to try to explain, please correct me if i’m wrong (i ommited some details, like the parameters of ForwardDiff.Dual, but this is what i can deduce)

3 Likes