Parametric Outer Constructor

Looking at #24909, I’m a little confused by constructor for PushVector. Here is simple example:

struct Foo{T}
  a::Int
end

function Foo{T}() where {T}  # what is this?
  return Foo{T}(1)
end

The function on the 4th line is unusual because it has the Foo{T}. Is it:

  • An inner constructor? It can be called like an inner constructor Foo{Int}(), but it is not defined inside the type defintion.
  • An outer constructor? I thought outer constructors are ordinary functions, and I don’t think functions can have free parameters (perhaps I am wrong about that?)

I have looked over the documentation for constructors and can’t find a case like this.

It’s an outer constructor, since as you’ve correctly noted it’s not defined inside the struct body. More precisely, it is the function which is called when you call Foo{X}() (with an X specified).

For example:

julia> struct Foo{T}
         a::Int
       end

julia> function Foo{T}() where {T}
         println("called with {T}")
         Foo{T}(1)
       end

julia> function Foo()
         println("called without {T}")
         Foo{Int}(1)
       end
Foo

julia> Foo{Int}()
called with {T}
Foo{Int64}(1)

julia> Foo()
called without {T}
Foo{Int64}(1)

The first definition is a method for Foo{T}, while the second definition is a method for Foo.

Interesting, so Foo{T} is distinct from Foo. Taking this one step further, the following works:

struct Foo{T, N}
  a::Int
end

function Foo{T}() where {T}

  return Foo{T, 1}(1)
end

obj = Foo{Int}()

In general, the outer constructor can specify the leading M the static parameters, it appears.

Because this behavior is not documented, I want to make sure this is a) intended and b) will be supported in future versions

This is definitely intentional and is used all over the place in Julia itself. For example, see:

It is also alluded to in the docs, in Constructors · The Julia Language :

What’s really going on here is that Point , Point{Float64} and Point{Int64} are all different constructor functions. In fact, Point{T} is a distinct constructor function for each type T .

but I agree that an example like yours would help clarify that section of the documentation a lot.