# Enforcing higher-order type parameter constraints

**URL:** https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435
**Category:** General Usage
**Created:** [June 19, 2019, 8:40am UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435 "2019-06-19T08:40:12Z")
**Posts on this page:** 14
**Page:** 1

<div class="post-metadata">

### Author: ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)
#### Post date: [June 19, 2019, 8:40am UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/1 "2019-06-19T08:40:12Z")

</div>

One design pattern I keep stumbling upon is that I want to generate parametric types based on existing parametric types, but with certain constraints among parameters, such that inappropriate combinations are prevented from ever being defined by the user (and causing further trouble).

For example, let’s say I have types `Type1{S}` and `Type2{S,T}` and I want to define a new type `Type3{U,V}` such that `U<:Type1{S}` and `V<:Type2{S,T}`, but in addition I want to enforce that the parameter `S` in both parameters is the same. I.e. trying to call or define a `Type3{U,V}` for some `U<:Type1{S}` and `V<:Type2{S2,T}` will produce an error whenever `S` and `S2` are different.

The problem I have is that I do not really know how to create such types in a Julia-paradigmatic way, or even whether this is the best way to solve the problem I have (i.e. excluding types with inconsistent parameters to exist).

When I want `Type3` to be an abstract type, I can do

```julia
# correct syntax, but not the desired result
abstract type Type3{U<:Type1{S} where S, V<:Type2{S,T} where {S, T}} end

```

but this does not force the `S` in both `U` and `V` to be the same.  
The following does _not_ work, but should give a flavor of what I’m trying to do:

```julia
# throws error `syntax: invalid type signature`
abstract type Type3{U<:Type1{S}, V<:Type2{S,T}} where {S,T} end

```

The following works for structs, but is probably a bit too convoluted (especially if the pattern keeps coming up), and cannot be done for abstract types because they lack constructors:

```julia
# correct syntax and outcome
struct Type3{S, T, U, V}
    x::U
    y::V
    Type3(x::Type1{S}, y::Type2{S,T}) where {S, T} = new{S, T, typeof(x), typeof(y)}(x, y)
end

```

Slightly simpler would be the following (which again does _not_ work):

```julia
# throws error `syntax: invalid type signature`
struct Type3{U<:Type1{S}, V<:Type2{S, T} where {S, T}
    x::U
    y::V
end

```

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 19, 2019, 8:58am UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/2 "2019-06-19T08:58:39Z")

</div>

> [@simsurace](#):
>
> inappropriate combinations are prevented from ever being defined

Use an [inner constructor](https://docs.julialang.org/en/v1/manual/constructors/#Inner-Constructor-Methods-1) to validate.

---

<div class="post-metadata">

### Author: ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)
#### Post date: [June 19, 2019, 9:02am UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/3 "2019-06-19T09:02:02Z")

</div>

Thanks. Yes that’s what I’m doing in my third code example. But this cannot be replicated for abstract types.

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 19, 2019, 9:16am UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/4 "2019-06-19T09:16:22Z")

</div>

The type system is not a programming language. Its primary use is for dispatch. You cannot enforce arbitrary constraints for abstract types, but since every object eventually has concrete type, this should not matter.

If you are computing on types for some reason, you can wrap them in a type and use the constructor of that. But generally, this may not be an idiomatic solution.

If you described your actual problem with an MWE, it would be possible to give more specific help.

---

<div class="post-metadata">

### Author: ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)
#### Post date: [June 19, 2019, 9:57am UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/5 "2019-06-19T09:57:45Z")

</div>

Right, dispatch is the primary use, but I’m also looking to set up a hierarchy of abstract types that guides the user in reasoning about the problem and prevents from doing thinks that do not make sense in the given domain.

My use case is the following. I start with arbitrary type parameters `{S,T}` for the state spaces of models. based on that, I have abstract types for the models `AbstractModel1{S}` and `AbstractModel2{S,T}`. There are concrete types of models that will be defined as structs, e.g.

```julia
struct ConcreteModel1{S} <: AbstractModel1{S} end
struct ConcreteModel2{S,T} <: AbstractModel2{S,T} end

```

which will contain some fields that describe the model, e.g. distributions or functions etc.

Then I want to define problems that consist of a pair of models `AbstractModel1{S}` and `AbstractModel2{S,T}`. But the domain requires the `S` to be consistent in order for the problem to make sense. I could define

```julia
abstract type AbstractProblem{S,T} end

```

and then when there is a concrete type of problem I can use an inner constructor as you suggest

```julia
struct ConcreteProblem{S,T,U,V} <: AbstractProblem{S,T}
    model1::U 
    model2::V
    function ConcreteProblem(model1::AbstractModel1{S}, model2::AbstractModel2{S,T}) where {S,T}
        new{S,T,typeof(model1),typeof(model2)}(model1,model2)
    end
end

```

preventing invalid combinations of models to be defined.  
However, this does not allow me to dispatch on model types for abstract problem types. Let’s say I want to define a function

```julia
Foo(problem::AbstractProblem)

```

but I want to dispatch Foo based on the types of models that define `problem`. With the solution above, I can only dispatch on `S` and `T`. Thus I cannot do

```julia
Foo(problem::AbstractProblem{ConcreteModel1{S},ConcreteModel2{S,T}}) where {S,T}

```

because models were not parameters of `AbstractProblem`.  
On the other hand, if I define

```julia
abstract type NewAbstractProblem{U<:AbstractModel1, V<:AbstractModel2} end

```

I can do the above dispatch, but this allows `NewAbstractProblem` to be defined even if the parameters `U` and `V` are inconsistent. It also complicates the definition of concrete types.

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [June 19, 2019, 10:02am UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/6 "2019-06-19T10:02:35Z")

</div>

> [@simsurace](#):
>
> when there is a concrete type of problem I can use an inner constructor

Yes, I think this is the right solution. Note that using type parameters which are more or less ignorable by the user is a common practice, eg

```julia
julia> typeof(@view ones(1,2)[1, :])
SubArray{Float64,1,Array{Float64,2},Tuple{Int64,Base.Slice{Base.OneTo{Int64}}},true}

```

> [@simsurace](#):
>
> a hierarchy of abstract types that guides the user in reasoning about the problem

I am not sure that’s a good use of abstract types in Julia. IMO ideally, your interface should work via functions that do things, and the user should not care much about types (except when extending your code, of course).

---

<div class="post-metadata">

### Author: ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)
#### Post date: [June 19, 2019, 10:26am UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/7 "2019-06-19T10:26:04Z")

</div>

Thanks. BTW, I added some more paragraphs after you replied. I hit the Reply button prematurely.

> [@Tamas\_Papp](#):
>
> I am not sure that’s a good use of abstract types in Julia. IMO ideally, your interface should work via functions that do things, and the user should not care much about types (except when extending your code, of course).

Thanks, I have to think about this.  
My thinking behind having a good hierarchy of abstract types was that it will lead to more generic code. In the example above, by having model types as parameters to problem types, I do not have to define a new type of problem for each combination of model types. The user interface (through functions) should also reflect this.

---

<div class="post-metadata">

### Author: ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)
#### Post date: [June 19, 2019, 10:49am UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/8 "2019-06-19T10:49:15Z")

</div>

Maybe I should define `AbstractProblem{S,T,U<:AbstractModel1,V<:AbstractModel2}` with all parameters explicit and then have all functions that act on this type to be defined only for valid combinations of `{S,T,U,V}`. So the type hierarchy is not safe, but a MethodError will be thrown whenever the user tries to use something unreasonable.

---

<div class="post-metadata">

### Author: ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)
#### Post date: [June 19, 2019, 11:38pm UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/9 "2019-06-19T23:38:51Z")

</div>

> [@simsurace](#):
>
> Slightly simpler would be the following (which again does _not_ work):
> 
> ```julia
> # throws error `syntax: invalid type signature`
> struct Type3{U<:Type1{S}, V<:Type2{S, T} where {S, T}
> x::U
> y::V
> end
> 
> ```

Why not something like this:

```julia
struct Type3{S, T}
  x::Type1{S}
  y::Type2{S, T}
end

```

With a design like this, you don’t have more parameters than necessary and constraints between them: you have just as many parameters as needed, and you build the necessary derived types from them.

---

<div class="post-metadata">

### Author: ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)
#### Post date: [June 20, 2019, 11:57am UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/10 "2019-06-20T11:57:10Z")

</div>

> [@ffevotte](#):
>
> Why not something like this:
> 
> ```julia
> struct Type3{S, T}
> x::Type1{S}
> y::Type2{S, T}
> end
> 
> ```
> 
> With a design like this, you don’t have more parameters than necessary and constraints between them: you have just as many parameters as needed, and you build the necessary derived types from them.

Translating this to my domain would mean either this:

```julia
struct ConcreteProblem{S, T}
  x::AbstractModel1{S}
  y::AbstractModel2{S, T}
end

```

The problem of this solution is that it will create type instabilities because the fields of `ConcreteProblem` are abstract types. A better solution would be to define a concrete type for each combination of concrete models, like this:

```julia
struct ConcreteModel1{S} <: AbstractModel1{S} end
struct ConcreteModel2{S,T} <: AbstractModel2{S,T} end

struct ConcreteProblem{S, T}
  model1::ConcreteModel1{S}
  model2::ConcreteModel2{S, T}
end

```

This will be slightly cumbersome if I have a lot of different concrete models.  
In addition, since the types of the fields of `ConcreteProblems` are not parameters, I will not be able to dispatch on them, i.e. the following will not work:

```julia
function DoSomething(problem::AbstractProblem{ConcreteModel1{S}, M2}) where {S,M<:AbstractModel2} 
# do something that only works for problems that involve ConcreteModel1.
end

```

To my current knowledge the only way to combine these different requirements is to have four parameters as mentioned in my last post, i.e. define

```julia
abstract type AbstractProblem{S,T,U<:AbstractModel1,V<:AbstractModel2} end

```

and then concrete problems with inner constructors that enforce the type constraints, i.e.

```julia
struct ConcreteProblem{S,T,U<:AbstractModel1,V<:AbstractModel2} <: AbstractProblem{S,T,U,V}
     model1::U 
     model2::V 
     function ConcreteProblem(x::AbstractModel1{S}, y::AbstractModel2{S,T}) where {S,T}
        new{S,T,typeof(x),typeof(y)}(x,y)
     end
end

```

Then this becomes possible

```julia
function DoSomething(problem::AbstractProblem{S,T,ConcreteModel1{S},M2}) where {S,T,M<:AbstractModel2} 
# do something that only works for problems that involve ConcreteModel1.
end

```

Is there an easier way?

---

<div class="post-metadata">

### Author: ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)
#### Post date: [June 20, 2019, 12:54pm UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/11 "2019-06-20T12:54:37Z")

</div>

Have you tried [ComputedFieldTypes.jl](https://github.com/vtjnash/ComputedFieldTypes.jl)? I upgraded it to 1.0 half a year ago and used it in [Grassmann.jl](https://github.com/chakravala/Grassmann.jl) to make types which error with the wrong array sizes. The array size is parametrically computed based on the type parameters.

---

<div class="post-metadata">

### Author: ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)
#### Post date: [June 20, 2019, 8:08pm UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/12 "2019-06-20T20:08:11Z")

</div>

> [@simsurace](#):
>
> Translating this to my domain would mean …

That does make sense, thanks! Then I do not see any way to achieve what you want other than what you posted in the end.

---

<div class="post-metadata">

### Author: ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)
#### Post date: [June 20, 2019, 11:10pm UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/13 "2019-06-20T23:10:40Z")

</div>

I found another way of enforcing type constraints in abstract types using type aliases.  
After having defined

```julia
abstract type AbstractProblem{S,T,U<:AbstractModel1,V<:AbstractModel2} end

```

I can define

```julia
const ConsistentProblem = AbstractProblem{S,T,<:AbstractModel1{S},<:AbstractModel2{S,T}} where {S,T}

```

The `struct`s that are subtypes of `ConsistentProblem` still have to be protected by custom constructors. But this abstract type allows to define functions that can safely assume that the types in models are consistent:

```julia
function DoSomething(prob::ConsistentProblem)
    # do something that relies on consistent models in prob
end

```

---

<div class="post-metadata">

### Author: ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)
#### Post date: [June 20, 2019, 11:11pm UTC](https://discourse.julialang.org/t/enforcing-higher-order-type-parameter-constraints/25435/14 "2019-06-20T23:11:24Z")

</div>

> [@chakravala](#):
>
> Have you tried [ComputedFieldTypes.jl](https://github.com/vtjnash/ComputedFieldTypes.jl)? I upgraded it to 1.0 half a year ago and used it in [Grassmann.jl](https://github.com/chakravala/Grassmann.jl) to make types which error with the wrong array sizes. The array size is parametrically computed based on the type parameters.

Thanks for pointing me to these packages. They are interesting!
