Initializing an empty array in an inner constructor with new

Consider the following struct MyAType

julia> abstract type AbstractType end

julia> mutable struct MyAType <: AbstractType
          a::Int64
       end

When having an Array field of type MyAType in a struct, it can be initialized to an empty array within an inner constructor.

mutable struct MyArrType
    intArr::Array{MyAType,1}
    MyArrType() = new(MyAType[ ]) 
end

It works:

julia> a = MyAType(1) 
MyAType(1)

julia> push!(A.intArr,a)
1-element Vector{MyAType}:
 MyAType(1)

In contrary, when having a parametric struct:

mutable struct MyArrType2{T <: AbstractA} 
    intArr::Array{T,1}
    MyArrType2() = new(T[]) 
end
ERROR: syntax: too few type parameters specified in "new{...}" around REPL[4]:1
Stacktrace:
 [1] top-level scope
   @ REPL[4]:1

The above struct can is functioning by defining an outer constructor:

MyArrType2{T}() where T <: AbstractType = MyArrayType2(T[])

julia> A2 = MyArrType2{MyAType}()
MyArrType2{MyAType}(MyAType)

julia> push!(A2.intArr,a)
1-element Vector{MyAType}:
MyAType(1)

However, I am wondering why the inner constructor is not working and if there is a possibility to define an inner constructor with the new operator?

When you write MyArrType() = new(T[]), what is the compiler supposed to initialise? An empty array of type T? But you haven’t told it what T is so it doesn’t know what to do.

The outer one works because you have told it what T is explicitly. Perhaps your inner constructor should be MyArrType{T}() = new(T[]) (but note I haven’t tested this).

3 Likes

I did not either tell the compiler what T is when declaring intArr::Array{T,1}.

Is not that one of the purposes of parametric types? to be able to write generic code that works for many arbitrary subtypes. I am still assuming that there is a good reason not to allow this with the operator new and there could be a hack to do it in a better way.

intArr::Array{T, 1} is fine because if you define your struct to be parametric, then as long as you tell the constructor what type you want it to construct then it will be able to do so. The problem with MyArrType2() is that you’re not telling the parametric type what T is.

MyArrType2{T}()? Compiler says "Ok, here is a intArr::Array{T,1}"
MyArrType2()? Compiler says “what is T?”

2 Likes

Yes Indeed. I have tried:

ulia> mutable struct MyArrType2{T <: AbstractType} 
         intArr::Array{T,1}
         MyArrType2{T}() = new(T[])  
       end
ERROR: UndefVarError: T not defined

Edit: However, the following seems to work

julia> mutable struct MyArrType{T <: AbstractType} 
         intArr::Array{T,1}
         MyArrType{T}() where T <: AbstractType = new(T[])  
       end

Yes, see Constructors · The Julia Language for more information. But basically, inner constructors are just normal function definitions. You need to write them as you would do if you were outside the struct definition.

The only difference in terms of syntax between inner and outer constructors is that inner constructors have access to the special new() function. Otherwise, they use the exact same syntax.

2 Likes

This should be

mutable struct MyArrType2{T <: AbstractA} 
    intArr::Array{T,1}
    MyArrType2{T}() where T = new{T}(T[]) 
end

Because the type parameters are for the struct, not its constructor.

2 Likes

This is probably what I was missing. I pre-assumed that it is part of the struct.

This code indeed looks precise. Still the code below worked (despite T is not being qualified with the operator new)

julia> mutable struct MyArrType{T <: AbstractType} 
         intArr::Array{T,1}
         MyArrType{T}() where T <: AbstractType = new(T[])  
       end
julia> mutable struct MyArrType{T <: AbstractType} 
         intArr::Array{T,1}
         MyArrType{T}() where T = new(T[])  
       end

julia> MyArrType2{MyAType}()
MyArrType2{MyAType}(MyAType[])

should just work

1 Like

Now you came out with the code with the least redundancies. Now I understand that
where T
is less redundant than
where T <: AbstractType
and the operator new does not need to be qualified with the type T

right, because T<:AbstractType is specified in your type definition MyArrType{T <: AbstractType} already.

in this case it can be inferred because the only field is intArr so Julia would know that T[] must have type Array{T, 1}

1 Like