TL;DR:
- what's the accurate definition of inner constructors? In Julia-v0.6+, is it right to say "any constructor that can be called with the signature
typename{...}(...)
(note the{}
part) is an inner constructor"? - As discussed in the comment below, is the outer-only constructor actually an
explicit inner constructor
? - Is it right to use
methods
for checking whether a method is an inner/outer constructor? - What's the difference between the default constructors that automatically defined by Julia and the corresponding ones explicitly defined by users?
BTW, I know how to use and when to use an inner constructor. I knew what an inner constructor is until the outer-only constructors come in and muddied the waters. :(
Let's recall some statements from the doc:
1. Outer Constructor Methods
A constructor is just like any other function in Julia in that its overall behavior is defined by the combined behavior of its methods.
2. Inner Constructor Methods
An inner constructor method is much like an outer constructor method, with two differences: 1. It is declared inside the block of a type declaration, rather than outside of it like normal methods. 2. It has access to a special locally existent function called
new
that creates objects of the block's type.3. Parametric Constructors
Without any explicitly provided inner constructors, the declaration of the composite type
Point{T<:Real}
automatically provides an inner constructor,Point{T}
, for each possible typeT<:Real
, that behaves just like non-parametric default inner constructors do. It also provides a single general outer Point constructor that takes pairs of real arguments, which must be of the same type.
I found inner constructor methods
can't be directly observed by methods
, even methods(Foo{Int})
works, it's actually not "just like any other function", common generic functions cannot be methods
ed in this way.
julia> struct Foo{T}
x::T
end
julia> methods(Foo)
# 2 methods for generic function "(::Type)":
(::Type{Foo})(x::T) where T in Main at REPL[1]:2 # outer ctor 「1」
(::Type{T})(arg) where T in Base at sysimg.jl:24 # default convertion method「2」
julia> @which Foo{Int}(1) # or methods(Foo{Int})
(::Type{Foo{T}})(x) where T in Main at REPL[1]:2 # inner ctor 「3」
However, the outer-only constructors adds another wrinkle to the constructor story:
julia> struct SummedArray{T<:Number,S<:Number}
data::Vector{T}
sum::S
function SummedArray(a::Vector{T}) where T
S = widen(T)
new{T,S}(a, sum(S, a))
end
end
julia> methods(SummedArray)
# 2 methods for generic function "(::Type)":
(::Type{SummedArray})(a::Array{T,1}) where T in Main at REPL[1]:5 # outer ctor「4」
(::Type{T})(arg) where T in Base at sysimg.jl:24
Hmmm, an outer constructor
IN a type declaration block, and it calls new
as well. I guess the purpose here is just to prevent Julia from defining the default inner-outer constructor pair for us, but is the second statement from the documentation still true in this case? It's confusing to new users.
Here, I read another form of inner constructors:
julia> struct Foo{T}
x::T
(::Type{Foo{T}})(x::T) = new{T}(x)
end
julia> methods(Foo)
# 1 method for generic function "(::Type)":
(::Type{T})(arg) where T in Base at sysimg.jl:24
julia> methods(Foo{Int})
# 2 methods for generic function "(::Type)":
(::Type{Foo{T}})(x::T) where T in Main at REPL[2]:3 「5」
(::Type{T})(arg) where T in Base at sysimg.jl:24
It's far from the canonical form Foo{T}(x::T) where {T} = new(x)
but it seems the results are quite the same.
So my question is what's the accurate definition of inner constructors? In Julia-v0.6+, is it right to say "any constructor that can be called with the signature typename{...}(...)
(note the {}
part) is an inner constructor"?