Parametric Outer Constructor

#1

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.

1 Like

#2

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.

1 Like

#3

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

0 Likes

#4

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 https://docs.julialang.org/en/v1/manual/constructors/#Parametric-Constructors-1 :

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.

2 Likes