# How to constraint a outer type of a variable in a struct to be the same, but allow the inner type to be different?

**URL:** https://discourse.julialang.org/t/how-to-constraint-a-outer-type-of-a-variable-in-a-struct-to-be-the-same-but-allow-the-inner-type-to-be-different/57565
**Category:** General Usage
**Created:** [March 19, 2021, 8:27pm UTC](https://discourse.julialang.org/t/how-to-constraint-a-outer-type-of-a-variable-in-a-struct-to-be-the-same-but-allow-the-inner-type-to-be-different/57565 "2021-03-19T20:27:55Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![zsoerenm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zsoerenm/32/664_2.png) [@zsoerenm](https://discourse.julialang.org/u/zsoerenm)
#### Post date: [March 19, 2021, 8:27pm UTC](https://discourse.julialang.org/t/how-to-constraint-a-outer-type-of-a-variable-in-a-struct-to-be-the-same-but-allow-the-inner-type-to-be-different/57565/1 "2021-03-19T20:27:55Z")

</div>

How to constraint a outer type of a variable in a struct to be the same, but allow the inner type to be different?  
e.g.

```julia
struct Foo{A<:Union{<:AbstractMatrix, Cholesky}}
    a::A
    b::A
end

```

In this example `a` & `b` can be either an `AbstractMatrix` or a `Cholesky`. Both have to be of the same type. However, in the case of Cholesky I would like allow, that `a` is a `Cholesky` with an inner type of a `Matrix` and that `b` is a `Cholesky` with an inner type of a `Diagonal` or vice versa.

So `a` and `b` can both be either be an `AbstractMatrix` or a `Cholesky`. In the case of `Cholesky` `a` and `b` can differ, in the sense that the inner type is either a `Diagonal` or a `Matrix`.

How would I do that?

---

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [March 19, 2021, 11:21pm UTC](https://discourse.julialang.org/t/how-to-constraint-a-outer-type-of-a-variable-in-a-struct-to-be-the-same-but-allow-the-inner-type-to-be-different/57565/2 "2021-03-19T23:21:51Z")

</div>

Interpreting your request literally, I defined Foo and its constructor like this:

```julia
# Copyright © 2021: Neven Sajko
#
# Licensed under the MIT license.

using LinearAlgebra

struct Foo{
        S <: Number,
        Ta <: Union{<:AbstractMatrix, <:Cholesky},
        Tb <: Union{<:AbstractMatrix, <:Cholesky},
}
        a :: Ta
        b :: Tb

        # NOTE: the inner constructor methods are not meant to be used directly,
        # use the outer constructor methods instead!

        function Foo{S, Ta, Tb}(
                a :: AbstractMatrix{S},
                b :: AbstractMatrix{S},
        ) where {
                S <: Number,
                Ta <: AbstractMatrix{S},
                Tb <: AbstractMatrix{S},
        }
                typeof(a) === Ta || error("type parameter 1 disagrees with the argument type")
                typeof(b) === Tb || error("type parameter 2 disagrees with the argument type")
                new{S, Ta, Tb}(a, b)
        end

        function Foo{S, Ta, Tb}(
                a :: Cholesky{S, A1},
                b :: Cholesky{S, A2},
        ) where {
                S <: Number,
                A1 <: AbstractMatrix{S},
                A2 <: AbstractMatrix{S},
                Ta <: Cholesky{S, A1},
                Tb <: Cholesky{S, A2},
        }
                typeof(a) === Ta || error("type parameter 1 disagrees with the argument type")
                typeof(b) === Tb || error("type parameter 2 disagrees with the argument type")
                new{S, Ta, Tb}(a, b)
        end
end

function Foo(
        a :: AbstractMatrix{S},
        b :: AbstractMatrix{S},
) where {
        S <: Number,
}
        Foo{S, typeof(a), typeof(b)}(a, b)
end

function Foo(
        a :: Cholesky{S, A1},
        b :: Cholesky{S, A2},
) where {
        S <: Number,
        A1 <: AbstractMatrix{S},
        A2 <: AbstractMatrix{S},
}
        Foo{S, Cholesky{S, A1}, Cholesky{S, A2}}(a, b)
end

```

Example REPL session:

```julia
               _
   _ _ _(_)_ | Documentation: https://docs.julialang.org
  (_) | (_) (_) |
   _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` | |
  | | |_| | | | (_| | | Version 1.5.4 (2021-03-11)
 _/ |\ __'_|_|_|\__'_| |
|__/ |

julia> include("zsoerenm.jl")
Foo

julia> A = [4. 12. -16.; 12. 37. -43.; -16. -43. 98.]
3×3 Array{Float64,2}:
   4.0 12.0 -16.0
  12.0 37.0 -43.0
 -16.0 -43.0 98.0

julia> Foo(A, A)
Foo{Float64,Array{Float64,2},Array{Float64,2}}([4.0 12.0 -16.0; 12.0 37.0 -43.0; -16.0 -43.0 98.0], [4.0 12.0 -16.0; 12.0 37.0 -43.0; -16.0 -43.0 98.0])

julia> D = Diagonal(A)
3×3 Diagonal{Float64,Array{Float64,1}}:
 4.0 ⋅ ⋅
  ⋅ 37.0 ⋅
  ⋅ ⋅ 98.0

julia> Foo(D, D)
Foo{Float64,Diagonal{Float64,Array{Float64,1}},Diagonal{Float64,Array{Float64,1}}}([4.0 0.0 0.0; 0.0 37.0 0.0; 0.0 0.0 98.0], [4.0 0.0 0.0; 0.0 37.0 0.0; 0.0 0.0 98.0])

julia> CA = cholesky(A)
Cholesky{Float64,Array{Float64,2}}
U factor:
3×3 UpperTriangular{Float64,Array{Float64,2}}:
 2.0 6.0 -8.0
  ⋅ 1.0 5.0
  ⋅ ⋅ 3.0

julia> CD = cholesky(D)
Cholesky{Float64,Diagonal{Float64,Array{Float64,1}}}
U factor:
3×3 Diagonal{Float64,Array{Float64,1}}:
 2.0 ⋅ ⋅
  ⋅ 6.08276 ⋅
  ⋅ ⋅ 9.89949

julia> Foo(CA, CD)
Foo{Float64,Cholesky{Float64,Array{Float64,2}},Cholesky{Float64,Diagonal{Float64,Array{Float64,1}}}}(Cholesky{Float64,Array{Float64,2}}([2.0 6.0 -8.0; 12.0 1.0 5.0; -16.0 -43.0 3.0], 'U', 0), Cholesky{Float64,Diagonal{Float64,Array{Float64,1}}}([2.0 0.0 0.0; 0.0 6.082762530298219 0.0; 0.0 0.0 9.899494936611665], 'U', 0))

```

Keep in mind that:

1. You may have asked an overly specific question. That is, while I think that I answered your question, your question may have been the wrong question to ask.

2. Someone who isn’t as new to Julia as I am may be able to improve on my code.

3. Your question was actually also somewhat ambiguous regarding the Cholesky types, so my interpretation of the question may not align completely with what you thought you wanted.

---

<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: [March 20, 2021, 8:03am UTC](https://discourse.julialang.org/t/how-to-constraint-a-outer-type-of-a-variable-in-a-struct-to-be-the-same-but-allow-the-inner-type-to-be-different/57565/3 "2021-03-20T08:03:59Z")

</div>

I am not sure I understand the specs fully, but generally you do validation in the [inner constructor](https://docs.julialang.org/en/v1/manual/constructors/#man-inner-constructor-methods).

Eg

```julia
using ArgCheck
struct Foo{TA,TB}
    a::TA
    b::TB
    function Foo(a::TA, b::TB) where {TA,TB}
        @argcheck (TA <: Cholesky && TB <: Cholesky) || (TA == TB && TB <: AbstractMatrix)
        new{TA,TB}(a, b)
    end
end

```
