Questions on Advanced Type Manipulation

I have a few questions on manipulating types:

  1. What is the difference between how promote_type and typejoin work?

  2. Isn’t it inaccurate to interpret typejoin as a set union, as it is described in the documentation, when the result of typejoin(Int8,Float64) is Real? I mean clearly Int64 is not a subtype of Float64 or Int8 but is a subtype of Real, which is a contradiction to the set analogy.

  3. What is the correct use of a lower bound type in type variables? As I read in the documentation, only leaf (non-abstract) types can be implemented, the rest are all abstract. So is there any benefit to setting an abstract type as a lower bound to a type parameter/variable, when in fact that eliminates all implementable types from the feasible set of types satisfying this condition, hence no argument can possibly match the parametric signature of the function having this type parameter/variable!? Is there another use case I am missing? And if we limit ourselves to a leaf lower bound type, can’t we just use that leaf type straight away as the type of the argument, because all its supertypes cannot be implemented anyway? Similarly, using multiple leaf types in a Union as a lower bound type, e.g. Union{Int,Float64} is not very useful; because we can just use the Union{Int, Float64} directly as the type of the argument.

  4. Just to confirm my understanding, does the bound field printed when running xdump on a type’s parameters, e.g. xdump(Array.parameters), mean whether the type variable refers to a specific type to be determined based on the argument, or if it is just an arbitrary/abstract/anonymous type parameter, and all arbitrary type parameters are assumed equal? Consider the following example:
    S = TypeVar(:S, false); T = TypeVar(:T, false); Array{S} == Array{T}
    returns true, because S and T are both arbitrary and hence they are treated to be equal when comparing types (including type comparison to find a suitable method for the arguments). However, S == T returns false, which means that they are only treated as equal when used as type parameters. Someone please confirm my understanding if my explanation was clear enough.

Good questions.

  1. promote_type’s behavior is determined by the promote_rules that have been set up. In contrast, typejoin works abstractly, widening types using supertype until it contains all the input types. typejoin is a pretty small function, it might be instructive to inspect the source (edit(typejoin, (Any, Any))).

  2. Which documentation are you looking at? Union{Int8,Float64} is genuinely a set union, but I don’t see a claim that typejoin is. When I type ?typejoin I get

  Compute a type that contains both T and S.
  1. Lower bounds are rarely used in regular code. They can be useful internally in type intersection and subtyping. But here’s an example that may help:
julia> foo{Int<:T<:Real}(::Type{T}) = 1
foo (generic function with 1 method)

julia> foo(Int)
1

julia> foo(Int8)
ERROR: MethodError: no method matching foo(::Type{Int8})
Closest candidates are:
  foo(::Type{Int64<:T<:Real}) where Int64<:T<:Real at REPL[1]:1

julia> foo(Signed)
1
  1. Yes, I think that’s correct.
2 Likes

Interesting example.

This is the documentation I was talking about https://docs.julialang.org/en/stable/devdocs/types/

Thanks alot for clarifying the rest.

I can see that might be confusing, but it does say “a type that contains their union”. Since Union{Int8,Float64} <: Real the documentation is actually correct.

Yes, I realize now it’s my misunderstanding.