# How to use a numerical type parameters in declaration

I’m trying to do something about physics, and I’ve written a struct like

``````struct System
charge_density
end
``````

now there is something called “spin” that may, but not must be considered in physics, and the data structure will be different. It looks something like

``````struct SystemNoSpin
charge_density::Array{Float64, 3}
end

struct SystemWithSpin
charge_density::Array{Float64, 4}
end
``````

conventionally, we will set a variable called “SPIN” = 1 when not considering it, and 2 when considering. So I try to use parametric types. I found this is a practical way:

``````struct System{SPIN}
charge_density::Array{Float64, SPIN+2}
end
``````

but this is not:

``````charge_type = Dict{Int, Type}([(1, Array{Float64, 3}), (2, Array{Float64, 4})])
struct System{SPIN}
charge_density::charge_type[SPIN]
end
``````

However, I feel that the second way, if available, seems to be more readable. Any way to do it?

Maybe I should make it more clear what I want. In physics, there are many properties, and they may be categorized into many kinds. For example, all space-related properties are Array{T, 3} without spin and Array{T, 4} with spin, while all band-related properties are Array{T, 2} without spin and Array{T, 3} with spin. T maybe either Float64 or ComplexF64. That’s why I think the second way mentioned above is more readable. In that way I can define some type alias, some dictionaries, then write

``````struct System{SPIN}
property1::space_related[SPIN]{Float64}
property2::space_related[SPIN]{ComplexF64}
property3::band_related[SPIN]{Float64}
end
``````

On the contrary, in the first way I mentioned above, it can only be written as

``````struct System{SPIN}
property1::Array{Float64, 2 + SPIN}
property2::Array{ComplexF64, 2 + SPIN}
property3::Array{Float64, 1 + SPIN}
end
``````

And now I’ve turned to a third solution:

``````struct SpaceRelatedProperties{SPIN}
property1::Array{Float64, 2 + SPIN}
property2::Array{ComplexF64, 2 + SPIN}
end
struct BandRelatedProperties{SPIN}
property3::Array{Float64, 1 + SPIN}
end
struct System{SPIN}
s_p::SpaceRelatedProperties{SPIN}
b_p::BandRelatedProperties{SPIN}
end
``````

Any better ways?

I think your type declaration could be:

``````julia> struct System{SPIN,N}
charge_density::Array{Float64}
end
``````

and then one or other option lies in the constructors:

``````julia> function System(spin,n) # not sure what is the size  of the array you want
if spin == 1
return System{spin,n}(zeros(Float64,n,n,n))
elseif spin == 2
return System{spin,n}(zeros(Float64,n,n,n,n))
else
error(" spin must be 1 or 2 ")
end
end
System

julia> System(1,4)
System{1,4}([0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0]

[0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0]

[0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0]

[0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0])

julia> System(2,3)
System{2,3}([0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]

[0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]

[0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]

[0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]

[0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]

[0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]

[0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]

[0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]

[0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0])

``````
1 Like

I think your point is, not specifying the concrete dimensions in type declaration, but during construction. It is a nice suggestion, but I think it differs not much from the first way I’ve discussed.

I don’t think you can do this, don’t you get this error?

``````julia> struct System{SPIN}
charge_density::Array{Float64, SPIN+2}
end
ERROR: MethodError: no method matching +(::TypeVar, ::Int64)

julia> methods(System)
# 0 methods for type constructor:
``````

The normal way, as @lmiq says, is to make the constructor enforce it. If you do this as an inner constructor, then there will be no method to construct a mismatched object:

``````julia> struct System2{SPIN, N}
charge_density::Array{Float64, N}
System2(x::Array) = new{ndims(x)-2, ndims(x)}(x)
end

julia> System2(ones(2,2)) isa System2{0}
true
``````

You can also use GitHub - vtjnash/ComputedFieldTypes.jl: Build types in Julia where some fields have computed types to write and handle the extra type parameters for you:

``````julia> using ComputedFieldTypes

julia> @macroexpand @computed struct SystemC{SPIN}
charge_density::Array{Float64, SPIN+2}
end
quote
struct SystemC{SPIN, var"##272"}
#= REPL[8]:2 =#
charge_density::Array{Float64, var"##272"}
function (::Base.Type{SystemC{SPIN}})(charge_density) where SPIN
return new{SPIN, SPIN + 2}(charge_density)
end
end
function ComputedFieldTypes.fulltype(::Base.Type{SystemC{SPIN}}) where SPIN
return SystemC{SPIN, SPIN + 2}
end
end
``````
1 Like

when I test the code on my machine I only use a simplified version without +2 and
so the problem is that it is a typevar, not the Int passed in. I will try your ways.

Another question. I can add an additional dimension to non-spin case, in which the size is always 1, so that the dimension in both cases is the same. Will this additional dimension introduce much overhead? It seems much easier to complement.