# Restricted parametric type

**URL:** <https://discourse.julialang.org/t/restricted-parametric-type/15653>\
**Category:** New to Julia\
**Tags:** parametric-types\
**Created:** [September 29, 2018, 4:08am UTC](https://discourse.julialang.org/t/restricted-parametric-type/15653 "2018-09-29T04:08:58Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [September 29, 2018, 4:08am UTC](https://discourse.julialang.org/t/restricted-parametric-type/15653/1 "2018-09-29T04:08:58Z")

</div>

hi, how could I define a parametric type where only a subset of types are allowed? e.g.

```julia
struct MyType{S, T}
    content::Int64
end

```

how could I put some arbitrary restrictions like:

```julia
MyType{Float64, Int64}(123)
MyType{Int64, Float64}(123)

```

are **valid** , and

```julia
MyType{Float64, Float64}(123)
MyType{Int64, Int64}(123)

```

are **invalid**?

thanks.

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [September 29, 2018, 4:29am UTC](https://discourse.julialang.org/t/restricted-parametric-type/15653/2 "2018-09-29T04:29:59Z")

</div>

There are two approaches to constraining a struct’s field’s types. One involves constraining struct parameters and using those parameters for field types. The other involves constraining the types that struct constructors accept. They may be used separately or together.

If you have two fields and one should be a floating point value and the other should be an integer value:

```julia
struct Demo{A<:AbstractFloat, B<:Integer}
    floatfield::A
    intfield::B
end

Demo(12.0, 5) # works
Demo(12, 5) # does not work

```

If you want one field to hold a Float64 value and the other field to hold an Int64 value, and sometimes you want the first field to be one type and other times you want the first field to be the other type

```julia
const IntFloat64 = Union{Int64, Float64}

struct Demo{A, B}
    field1::A
    field2::B

    function Demo(field1::A, field2::B) where {A<:IntFloat64, B<:IntFloat64}
        # ensure the types differ
        if A == B
           throw(ErrorException("types must differ"))
        end
        return new{A,B}(field1, field2)
    end
end

```

When the constructor is inside the struct (an _inner constructor_), and there are no outer constructors defined, then all construction will use the inner constructor.

---

<div class="post-metadata">

**Author:** ![thofma](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thofma/32/1691_2.png) [@thofma](https://discourse.julialang.org/u/thofma)\
**Post date:** [September 29, 2018, 4:46am UTC](https://discourse.julialang.org/t/restricted-parametric-type/15653/3 "2018-09-29T04:46:55Z")

</div>

It does not look like he wants to restrict the structs field types.

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [September 29, 2018, 5:04am UTC](https://discourse.julialang.org/t/restricted-parametric-type/15653/4 "2018-09-29T05:04:26Z")

</div>

```julia
struct MyType{S, T}
    content::Int64
end

function MyType(::Type{S}, ::Type{T}, content::Int) where {S, T}
    constraintypes(S, T)
    return MyType{S, T}(content)
end

function constraintypes(::Type{S}, ::Type{T}) where {S, T}
      s = supertype(S)
      t = supertype(T)
      if s<:t || t<:s
          throw(ErrorException("types must differ more"))
     end
     return nothing
end

```

```julia
julia>MyType(Int, Float64, 5)
MyType{Int64,Float64}(5)

julia> MyType(Float32, Int16, 5)
MyType{Float32,Int16}(5)

julia> MyType(Float32, Float64, 5)
ERROR: types must differ more

```

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [September 29, 2018, 5:17am UTC](https://discourse.julialang.org/t/restricted-parametric-type/15653/5 "2018-09-29T05:17:05Z")

</div>

oic: the trick is to use `::Type{}` ?

actually I was looking into the code of `StaticArray`. Seems like it **does not restrict the definition of nonsense types** , e.g.:

```julia
julia> SMatrix{2, 3}
SArray{Tuple{2,3},T,2,L} where L where T

julia> SMatrix{-1, 3}
SArray{Tuple{-1,3},T,2,L} where L where T

```

noted that the 2nd type **throws no error nor warning** although it’s nonsense (to have negative dimension).

```julia
julia> SMatrix{2, 3}(11.0, 12.0, 13.0, 14.0, 15.0, 16.0)
2×3 SArray{Tuple{2,3},Float64,2,6}:
 11.0 13.0 15.0
 12.0 14.0 16.0

julia> SMatrix{-2, 3}(11.0, 12.0, 13.0, 14.0, 15.0, 16.0)
ERROR: ArgumentError: Size mismatch in Static Array parameters. Got size Tuple{-2,3}, dimension 2 and length 6.

```

error is given only when calling constructors.

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [September 29, 2018, 5:42am UTC](https://discourse.julialang.org/t/restricted-parametric-type/15653/6 "2018-09-29T05:42:49Z")

</div>

I understand your perspective and have no quarrel with it. We do it like this a lot:

When used, argument validation tends to be encapsulated in a validation function  
or given as an inline test: `arg1 > 0 || throw(DomainError("$arg1"))`.

Type signatures are used to guide multidispatch and also to gate arg types.

Mostly, Julia code is written to the type system rather than to defend it.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [September 29, 2018, 9:24am UTC](https://discourse.julialang.org/t/restricted-parametric-type/15653/7 "2018-09-29T09:24:28Z")

</div>

inspecting `SMatrix` gives me a big surprise (due to my ignorance): the “parameter” of a parametric type can be **anything** , i.e. **no** need to be a `Type` !!!

```julia
struct MyStruct1{T}
    content::Int64
end

function f(x::MyStruct1{T}) where {T}
    println("T is ", T, " content is: ", x.content)
end

julia> f(MyStruct1{1}(123) )
T is 1 content is: 123

julia> f(MyStruct1{2.2}(222) )
T is 2.2 content is: 222

julia> f(MyStruct1{:sym}(333) )
T is sym content is: 333

julia> typeof(MyStruct1{1}(123) )
MyStruct1{1}

julia> typeof(MyStruct1{2.2}(222) )
MyStruct1{2.2}

julia> typeof(MyStruct1{:sym}(333) )
MyStruct1{:sym}

```

so, I do not understand **why we need `Val{}` at all**???

---

<div class="post-metadata">

**Author:** ![mohamed82008](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mohamed82008/32/18171_2.png) [@mohamed82008](https://discourse.julialang.org/u/mohamed82008)\
**Post date:** [September 29, 2018, 9:59am UTC](https://discourse.julialang.org/t/restricted-parametric-type/15653/8 "2018-09-29T09:59:26Z")

</div>

> [@tomtom](#):
>
> the “parameter” of a parametric type can be **anything** , i.e. **no** need to be a `Type` !!!

Not quite, fourth bullet point in [Types · The Julia Language](https://docs.julialang.org/en/v1/manual/types/index.html#man-types-1).

> [@tomtom](#):
>
> **why we need `Val{}` at all**???

For convenience! Of course, you can define your own `Val`-like type, but if you only want to dispatch on values, nothing more, someone already defined the type for you.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [September 29, 2018, 2:10pm UTC](https://discourse.julialang.org/t/restricted-parametric-type/15653/9 "2018-09-29T14:10:07Z")

</div>

> if you only want to dispatch on values, nothing more

I don’t understand. For example, the following use the **value** directly to dispatch and it’s all fine:

```julia
struct MyStruct1{T}
    content::Int64
end

function f1(x::MyStruct1{1})
    println("T is 1")
end

function f1(x::MyStruct1{2})
    println("T is 2")
end

julia> f1(MyStruct1{1}(123) )
T is 1

julia> f1(MyStruct1{2}(222) )
T is 2

```

could you give an example like `Val{1234}` is **necessary** rather then the value `1234`?

---

<div class="post-metadata">

**Author:** ![mohamed82008](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mohamed82008/32/18171_2.png) [@mohamed82008](https://discourse.julialang.org/u/mohamed82008)\
**Post date:** [September 29, 2018, 2:33pm UTC](https://discourse.julialang.org/t/restricted-parametric-type/15653/10 "2018-09-29T14:33:29Z")

</div>

It is mostly for type stability. Some types have meaningful numbers in their parameters for example `NTuple{N, T}` where `N` is the number of elements in the tuple. Let’s say I want to generate 5 3-tuples and perhaps return a `Scatter` struct with points field.

```julia
julia> struct Scatter{N, T}
           points::Vector{NTuple{N, T}}
       end

julia> Scatter(N, n) = Scatter([ntuple(i->rand(), N) for i in 1:n])
Scatter

julia> Scatter(::Val{N}, n) where {N} = Scatter([ntuple(i->rand(), N) for i in 1:n])
Scatter

julia> Scatter(3, 5)
Scatter{3,Float64}(Tuple{Float64,Float64,Float64}[(0.599768, 0.863335, 0.0522125), (0.461746, 0.991876, 0.349595), (0.976401, 0.146929, 0.393038), (0.570525, 0.752721, 0.00339558), (0.0793879, 0.612754, 0.00491688)])

julia> @code_warntype Scatter(3, 5)
Body::Scatter{_1,_2} where _2 where _1
1 1 ─ %1 = %new(getfield(Main, Symbol("##3#5")){Int64}, N)::getfield(Main, Symbol("##3#5")){Int64} │
  │ %2 = (Base.sle_int)(1, n)::Bool │╻╷╷╷ Colon
  │ (Base.sub_int)(n, 1) ││╻ Type
  │ %4 = (Base.ifelse)(%2, n, 0)::Int64 │││┃ unitrange_last
  │ %5 = %new(UnitRange{Int64}, 1, %4)::UnitRange{Int64} │││
  │ %6 = %new(Base.Generator{UnitRange{Int64},getfield(Main, Symbol("##3#5")){Int64}}, %1, %5)::Base.Generator{UnitRange{Int64},getfield(Main, Symbol("##3#5")){Int64}}
  │ %7 = invoke Base.collect(%6::Base.Generator{UnitRange{Int64},getfield(Main, Symbol("##3#5")){Int64}})::Array{_1,1} where _1
  │ %8 = (Main.Scatter)(%7)::Scatter{_1,_2} where _2 where _1 │
  └── return %8 │

julia> Scatter(Val{3}(), 5)
Scatter{3,Float64}(Tuple{Float64,Float64,Float64}[(0.414659, 0.267734, 0.0170115), (0.763122, 0.858378, 0.45464), (0.0388137, 0.489361, 0.0495321), (0.0409206, 0.00572096, 0.924081), (0.350058, 0.133318, 0.890979)])

julia> @code_warntype Scatter(Val{3}(), 5)
Body::Scatter{3,Float64}
1 1 ─ %1 = (Base.sle_int)(1, n)::Bool │╻╷╷╷╷ Colon
  │ (Base.sub_int)(n, 1) ││╻ Type
  │ %3 = (Base.ifelse)(%1, n, 0)::Int64 │││┃ unitrange_last
  │ %4 = %new(UnitRange{Int64}, 1, %3)::UnitRange{Int64} │││
  │ %5 = %new(Base.Generator{UnitRange{Int64},getfield(Main, Symbol("##7#9")){3}}, getfield(Main, Symbol("##7#9")){3}(), %4)::Base.Generator{UnitRange{Int64},getfield(Main, Symbol("##7#9")){3}}
  │ %6 = invoke Base.collect(%5::Base.Generator{UnitRange{Int64},getfield(Main, Symbol("##7#9")){3}})::Array{Tuple{Float64,Float64,Float64},1}
  │ %7 = %new(Scatter{3,Float64}, %6)::Scatter{3,Float64} ││╻ Type
  └── return %7 │

```

If this code is in a hot part of the program, this type instability will propagate and slow down your whole program.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [September 29, 2018, 3:14pm UTC](https://discourse.julialang.org/t/restricted-parametric-type/15653/11 "2018-09-29T15:14:26Z")

</div>

thanks.

[here](https://docs.julialang.org/en/v1/manual/performance-tips/#Types-with-values-as-parameters-1) I finally found the documentation also. It’s kind of difficult to understand though 😅
