Constructors for `UnionAll` types

I have a simple type with two parameters, something like

julia> struct Thingy{N, T}
           a::NTuple{N, T}
       end

and I try to define the following constructors for it, where the second parameter is fixed:

julia> (::Type{(Thingy{N, Float64} where N)})(n::NTuple{N, Float64}) where {N} = Thingy{N, Float64}(n)
ERROR: syntax: invalid variable expression in "where"

On the other hand, if I just take that UnionAll expression from within Type{} and attach it to a variable:

julia> FloatThingy{N} = Thingy{N, Float64} where N
Thingy{N,Float64} where N

julia> (Thingy{N, Float64} where N) === FloatThingy
true

then the same kind of constructor works just fine:

julia> (::Type{FloatThingy})(n::NTuple{N, Float64}) where {N} = Thingy{N, Float64}(n)

julia> FloatThingy((0.1, 0.2))
Thingy{2,Float64}((0.1, 0.2))

This leaves me with the impression that constructors are really attached to names like FloatThingy, rather than the type itself. But on the other hand, the whole (::Type{T})(blahblah) syntax for constructors seems to suggest otherwise. So how do I understand what’s happening here?

Also, the reason I ran into this is that I’m trying to create constructors of the form

(::Type{(Thingy{N, T} where N)})(n::NTuple{N, T}) where {N} 

for all T, so that I can later define things like FloatThingy and similarly IntThingy and SymbolThingy and whatnot, and have the constructors work without defining them for each subtype individually. If someone can direct me towards a way of achieving this, then gratefulness is on offer.

This impression is mistaken — the problem is that you are using N for two things in a nested way, which is not allowed (how would Julia know which is which?). Try eg (note the K)

(::Type{(Thingy{K, Float64} where K)})(n::NTuple{N, Float64}) where {N} = Thingy{N, Float64}(n)

That said, I would just reverse the order of type parameters, and then you could shorten this code to something like

Thingy{Float64}(n::NTuple{N, Float64}) where N = ...

The order of type parameters is very useful for specifying unionall types like this, to the extend that it is worth defining aliases with different orders — NTuple is one of those.

1 Like

Thanks! I guess I should have paid more attention to the actual error message Julia gave me, rather than assume there was something deeper going on.