# Arithmetic for parametric type values in structs e.g., N+1

How can I do something like:

``````struct Foo{N}
arr1::Array{Float64, N}
arr2::Array{Float64, N+1}
end
``````

i.e., I want arr2 to be always of 1 higher rank than arr1. How to do this?

Maybe thereâ€™s a fancier way but this seems to work fine.

``````julia> struct Foo{A,B}
a::A
b::B
Foo{A,B}(a,b) where {A,B} = ndims(a)+1 == ndims(b) ? new{A,B}(a,b) : error("wrong dims")
end

julia> (Foo(a::A,b::B) where {A,B}) = Foo{A,B}(a,b)
Foo

julia> Foo([1], [1;;2])
Foo{Vector{Int64}, Matrix{Int64}}([1], [1 2])

julia> Foo([1], [1])
ERROR: wrong dims
``````
1 Like

It is impossible for the composite type to do parameter constraints other than `<:` in the implicit `where` clause, you need to enforce it in an inner constructor call like jar1 demonstrated. The reason is that generic functions like `+` can depend on input types and can be changed interactively, so a constraint based on a generic method could also change interactively, but a composite type CANNOT change because of existing instances. Thatâ€™s also why you need the â€średundantâ€ť parameter `B==A+1`; after a constraint change, the type system must distinguish previous `Foo{1, 2}` instances from newer `Foo{1, 42}` instances.

The inner constructor should be enough, it is unsafe (in other words, user beware) to bypass inner constructors with `reinterpret` or `@eval @inline outnew(T, args...) = \$(Expr(:splatnew, :T, :args)) #:new for an unsplatted number of separate arguments`. I am uncertain if it is hypothetically possible to extend constraints to intrinsic and built-in functions, which I donâ€™t think can change.

1 Like

I would do the following.

``````julia> struct Foo{N,M}
arr1::Array{Float64, N}
arr2::Array{Float64, M}
Foo{N}(a,b) where N= new{N, N+1}(a,b)
end

julia> Foo{1}(rand(2), rand(2,3))
Foo{1, 2}([0.9165330134722103, 0.8133055136507046], [0.15120619990261142 0.8543356012671888 0.5239972992036701; 0.12074486400173157 0.6817097391005016 0.3327645622931258])
``````
3 Likes

Thatâ€™s even neater and closer matches the OP, and Iâ€™d suggest rounding it out with an outer constructor like jar1 did for convenience:

``````julia> Foo(a, b) = Foo{ndims(a)}(a, b)
Foo

julia> Foo(rand(2), rand(2,3))
Foo{1, 2}([0.7928217615317612, 0.27627055084273566], [0.8947120194686449 0.7069448391898014 0.628333742859724; 0.3193547716322238 0.7802926781126777 0.9817486584424423])
``````

and incorporating a more descriptive error message in the inner `Foo{N}` than a conversion error in `new`. Note this approach consciously sacrifices a `Foo{N, M}` constructor, so the output of `typeof` canâ€™t instantiate.

2 Likes

I like `ConstructionBase.constructorof` for getting constructors instead of `typeof`.