Limitations of `Base.convert` for the construction of parametric types

This issue doesn’t really involve convert or nested types, that part is actually fine here. Parametric type definitions just don’t automatically make the parameter-less constructor methods that many people want. You have to define them yourself, which you did.

julia> struct MyType
           x::Float64
       end

julia> methods(MyType) # one for known field, one for conversions
# 2 methods for type constructor:
 [1] MyType(x::Float64)
     @ REPL[13]:2
 [2] MyType(x)
     @ REPL[13]:2

julia> struct MyType2{T} # useless parameter
           x::Float64
       end

julia> methods(MyType2) # all gone!
# 0 methods for type constructor

julia> methods(MyType2.body) # parametric constructor still here
# 1 method for type constructor:
 [1] (var"#ctor-self#"::Type{MyType2{T}} where T)(x)
     @ REPL[16]:2

The equivalent approach to what you demonstrated would be:

MyType2(x::T) where T = MyType{T}(x)

You might spot the problem here: T is a useless parameter and x would be converted to Float64 prior to successful instantiation, so there’s no reason for T to match typeof(x). We don’t need MyType2{Int64}(1.0), in fact we’d have more type stability if we just instantiated MyType{Float64}(x). There isn’t a good automatic way to compute type parameters entirely from inputs. A more practical but complex example would be inputs with different types for fields that share only 1 parameter; it’s not clear if promotion is desired, and you’d still have to implement promote yourself. I think there is an issue about adding these default constructors anyway, letting us opt out manually like usual, that ended up saying it’d be breaking.

1 Like