Parametric inner constructor inherits parameter from global scope

There are three things going on here:

  • The name T is just like any other name, like TT or even Int. When you define a method, the types need to be identified by something — and that thing can be (and almost always is) a global name. Typically it’s a constant global, but it can be non-constant, too. It’ll just define the method based upon the value when the method was defined.

  • The syntax Point{Int}(...) = ... is defining a constructor for Point{Int}. Before the where revolution, this syntax meant something completely different — it was how we introduced local type variables for a method signature. So yes, the meaning here changed (and did so dramatically) and it was very intentional — the disambiguation between defining a method on Point{Int}() vs. a method on Point() is a huge benefit from the where clause.

  • We introduce type parameters for structs in their definition: the syntax struct Point{T} is how we introduce a local name T that can be used in field definitions and the parameters of supertypes. Now here’s the confusing part — that T isn’t available to inner constructors, even though it’s written within the same indented block. In fact it cannot be because when you call an inner constructor the Point hasn’t be constructed yet so it doesn’t know what that T is! The inner constructor can even change what the parameter is compared to how it was called:

    julia> struct Point{T}
               x::T
               y::T
               Point{Int}(x,y) = new{Float64}(x,y)
           end
    
    julia> Point{Int}(1,2)
    Point{Float64}(1.0, 2.0)
    

    Ok, so that case is crazy and of course you wouldn’t want to do that in real code. But in real code you do often want to compute some additional parameter that cannot be derived automatically. And in such cases, you “get into” the inner constructor without knowing what the type parameters of the struct are.

    So, yeah, it’s confusing that the type parameters of a struct aren’t available to inner constructors. But it is simply because they’re not known yet. I suppose it’d be nice if the way we spelled out struct definitions would make that more obvious, but there’s a value to the simplicity of where we put our inner constructors.

6 Likes