# Relation between type parameter

**URL:** https://discourse.julialang.org/t/relation-between-type-parameter/127664
**Category:** General Usage
**Tags:** parametric-types
**Created:** [April 3, 2025, 9:03am UTC](https://discourse.julialang.org/t/relation-between-type-parameter/127664 "2025-04-03T09:03:10Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![FerreolS](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ferreols/32/216854_2.png) [@FerreolS](https://discourse.julialang.org/u/FerreolS)
#### Post date: [April 3, 2025, 9:03am UTC](https://discourse.julialang.org/t/relation-between-type-parameter/127664/1 "2025-04-03T09:03:10Z")

</div>

Hi,  
I was wondering if there was a mean to enforce some kind of relation between parameter types.  
Here is an example:  
I can define the structure:

```julia
struct mystruct{N,M}
              A::Array{Float64,N}
              B::Array{Float64,M}
 end

```

but not

```julia
struct mystruct2{N}
       A::Array{Float64,N}
       B::Array{Float64,N+1}
end

```

neither `myalias{N} = mystruct{N,N+1}`

Is there an elegant way to do that? Is a trait adapted to specialize method when M == N+1?  
Thanks

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [April 3, 2025, 9:38am UTC](https://discourse.julialang.org/t/relation-between-type-parameter/127664/2 "2025-04-03T09:38:43Z")

</div>

Not directly in type parameters because while a `struct` type’s structure cannot change, `N+1` depends on what the dispatched method of `+` does, which is allowed to change at runtime. Even though changing the behavior of `+(::Int, ::Int)` specifically would break Julia, it can’t recognize an exception for a particular call signature. This would be hypothetically possible for a function that can’t change, but we can only define the changing generic functions, and none of the built-ins are supported. For now (and probably very far into the future because of practical issues I haven’t mentioned), the language doesn’t do type parameter computation.

However, you can enforce a relation between type parameters at runtime by checking it in all constructor methods (or rather the inner ones that all the others use), throw an (informative) error instead of allowing instantiation to complete. (EDIT: see next commenter’s example.) There are unsafe ways to get around it to make invalid instances, but undefined behavior is not your problem.

If you don’t enforce that relation, you can also indirectly dispatch to a different method given that relation by mapping subsets of the parametric type to Holy traits. I don’t know a good name so I’ll just go for a typical stand-in:

```julia
struct Foo{d} end

isfoo(::mystruct{N,M}) where {N,M} = Foo{M==N+1}()

bar(x::mystruct) = bar(isfoo(x), x)
bar(::Foo{true}, x) = "M==N+1 here!"
bar(::Foo{false}, x) = "uh oh"

```

So all `mystruct{N,M}` where `M==N+1` are mapped to `Foo{true}()`, and the rest are mapped to `Foo{false}()`. We can dispatch over those values’ different types to different methods:

```julia
julia> bar(mystruct(zeros(3), zeros(2,3)))
"M==N+1 here!"

julia> bar(mystruct(zeros(3,3), zeros(2,3)))
"uh oh"

```

---

<div class="post-metadata">

### Author: ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)
#### Post date: [April 3, 2025, 9:48am UTC](https://discourse.julialang.org/t/relation-between-type-parameter/127664/3 "2025-04-03T09:48:49Z")

</div>

Although you cannot constrain the parametric types, note that you can constrain construction:

```julia-repl
julia> struct MyStruct{M, N}
           A::Array{Float64, M}
           B::Array{Float64, N}
           function MyStruct(A::Array{Float64, M}, B::Array{Float64, N}) where {M, N}
               if M + 1 != N
                   throw(ArgumentError("Wrong M/N"))
               end
               new{M, N}(A, B)
           end
       end

julia> MyStruct(rand(10), rand(10, 20))
MyStruct{1, 2}([0.6004011295966778, 0.12740683358215132, 0.3939894763092303, 0.07413103266456234, 0.5847503911474503, 0.18333009710427606, 0.3778337994193758, 0.2640461333323526, 0.4879893271040746, 0.8580042570348061], [0.8406780616356467 0.49054628365561614 … 0.5381138878408204 0.056638061595590705; 0.9641422719834767 0.3323178717886266 … 0.16255734342665917 0.9266963224391359; … ; 0.4946863219330464 0.49174041417955083 … 0.6696892296071885 0.9457106674902207; 0.7516719986066737 0.3836054641885388 … 0.5334842120015687 0.8908046734756528])

julia> MyStruct(rand(10), rand(10, 20, 30))
ERROR: ArgumentError: Wrong M/N
Stacktrace:
 [1] MyStruct(A::Vector{Float64}, B::Array{Float64, 3})
   @ Main ./REPL[1]:6
 [2] top-level scope
   @ REPL[3]:1

```

---

<div class="post-metadata">

### Author: ![FerreolS](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ferreols/32/216854_2.png) [@FerreolS](https://discourse.julialang.org/u/FerreolS)
#### Post date: [April 3, 2025, 12:04pm UTC](https://discourse.julialang.org/t/relation-between-type-parameter/127664/4 "2025-04-03T12:04:30Z")

</div>

Thanks,  
I was already using Holy traits but I didn’t realized that one can perform operations on parameters of a parametrized method as in ` Foo{M==N+1}()` .

I found a bit annoying to add the `bar(x::mystruct) = bar(isfoo(x), x)` kind function for every methods and I was looking for some kind of alias. But I guess I have my answer. I’ll mark this answer as a solution noting that @barucden message is a valid solution either.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [April 3, 2025, 1:06pm UTC](https://discourse.julialang.org/t/relation-between-type-parameter/127664/5 "2025-04-03T13:06:35Z")

</div>

FWIW, I would do what barucden posted. I don’t use Holy traits much and when I do, it’s not to specialize behavior for a specific relationship among type parameters. I either always enforce a relation or I don’t, but that’s just my experience, and I have very little.

---

<div class="post-metadata">

### Author: ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)
#### Post date: [April 3, 2025, 1:08pm UTC](https://discourse.julialang.org/t/relation-between-type-parameter/127664/6 "2025-04-03T13:08:04Z")

</div>

Let me add that Benny’s response and mine complement each other.

If an instance of `mystruct{M, N}` is valid even in the case of M + 1 \neq N and you are just looking for a way to clearly dispatch on the two cases, M + 1=N and M + 1\neq N, then Benny showed how to do it.

If, on the other hand, it is only allowed to have `mystruct{M, N}` such that M + 1 = N, then my answer showed how to disallow construction of invalid instances.

---

<div class="post-metadata">

### Author: ![FerreolS](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ferreols/32/216854_2.png) [@FerreolS](https://discourse.julialang.org/u/FerreolS)
#### Post date: [April 3, 2025, 1:23pm UTC](https://discourse.julialang.org/t/relation-between-type-parameter/127664/7 "2025-04-03T13:23:31Z")

</div>

Indeed, narrowing the span of possible parameters using inner constructor is quite important and should probably highlighted somewhere in the doc, especially when the structure tends to have a large number of parameters to stay as concrete as possible.

---

<div class="post-metadata">

### Author: ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)
#### Post date: [April 3, 2025, 1:37pm UTC](https://discourse.julialang.org/t/relation-between-type-parameter/127664/8 "2025-04-03T13:37:54Z")

</div>

Narrowing parameters specifically isn’t described in the documentation for inner constructors, but _enforcing invariants_ in general is. The doc’s current example enforces the inputs `x, y` to obey `x <= y` for instantiation, so it’s a small hop to doing that for the method’s static parameters, as barucden demonstrated.
