Confused about type declarations in Julia
Solution 1:
It appears that you are using the @with_kw
macro from the Parameters package, so let's start by loading that package:
julia> using Parameters
Now, copying your definition
julia> @with_kw mutable struct NodeLocator{T<:Union{Int64, Vector{Int64}}}
length::T = 0
value::T = [0] # changed type declaration here
maxtreelength::T = 0 end
NodeLocator
we can see that this works fine if we construct a struct with either all ints or all vectors
julia> NodeLocator(0,0,0)
NodeLocator{Int64}
length: Int64 0
value: Int64 0
maxtreelength: Int64 0
julia> NodeLocator([0],[0],[0])
NodeLocator{Vector{Int64}}
length: Array{Int64}((1,)) [0]
value: Array{Int64}((1,)) [0]
maxtreelength: Array{Int64}((1,)) [0]
but fails for mixtures, including the default you have specified (effectively, (0, [0], 0)
)
julia> NodeLocator([0],[0],0)
ERROR: MethodError: no method matching NodeLocator(::Vector{Int64}, ::Vector{Int64}, ::Int64)
Closest candidates are:
NodeLocator(::T, ::T, ::T) where T<:Union{Int64, Vector{Int64}} at ~/.julia/packages/Parameters/MK0O4/src/Parameters.jl:526
Stacktrace:
[1] top-level scope
@ REPL[38]:1
julia> NodeLocator() # use defaults
ERROR: MethodError: no method matching NodeLocator(::Int64, ::Vector{Int64}, ::Int64)
Closest candidates are:
NodeLocator(::T, ::T, ::T) where T<:Union{Int64, Vector{Int64}} at ~/.julia/packages/Parameters/MK0O4/src/Parameters.jl:526
Stacktrace:
[1] NodeLocator(; length::Int64, value::Vector{Int64}, maxtreelength::Int64)
@ Main ~/.julia/packages/Parameters/MK0O4/src/Parameters.jl:545
[2] NodeLocator()
@ Main ~/.julia/packages/Parameters/MK0O4/src/Parameters.jl:545
[3] top-level scope
@ REPL[39]:1
This is because as written, you have forced each field of the struct to be the same type T
which is in turn a subtype of the union.
If you really want each type to be allowed to be a Union
, you could instead write something along the lines of:
julia> @with_kw mutable struct NodeLocator{T<:Int64}
length::Union{T,Vector{T}} = 0
value::Union{T,Vector{T}} = [0] # changed type declaration here
maxtreelength::Union{T,Vector{T}} = 0 end
julia> NodeLocator() # use defaults
NodeLocator{Int64}
length: Int64 0
value: Array{Int64}((1,)) [0]
maxtreelength: Int64 0
However, unless you really need the flexibility for all three fields to be unions, your original definition is actually much preferable. Adding Union
s where you don't need them can only slow you down.
One thing that would make the code cleaner and faster would be if you could use a non-mutable struct. This is often possible even when you might not realize it at first, since an Array
within a non-mutable struct
is still itself mutable, so you can swap out elements within that array without needing the whole struct to be mutable. But there are certainly cases where you do indeed need the mutability -- and if you do need that, go for it.
Finally, I should note that there is not necessarily much point in constraining the numeric type to be T<:Int64
unless that is really the only numeric type you'll ever need. You could easily make your code more general by instead writing something like T<:Integer
or T<:Number
, which should not generally have any adverse performance costs.