How to add constraints to parametric arguments


Still trying to wrap my head around the type system of Julia.

Any explanation why this is not allowed, and what would be the best way to achieve something similar?

struct State{T::Int}

Is the only way to achieve that through conditions in the inner constructor? If yes, why is that because it would let users “materialize” type that are not constructible. It might be better to raise the error earlier.

Thank you for your help!

1 Like

I am not sure if you mean:

struct State{T<:Int}


achieve what?

No I don’t mean that. I want the parametric parameter T to be an Int not a DataType that is a subset of Int.

1 Like

If you want it to always be an Int, just specify that it is an Int – no need to parameterize.

struct State

I know that. But what if my struct needs a field that is a SVector of size T?

I was trying to make my question as simple as possible. I just want to understand better the type system.

1 Like

You can look at how they do it for SArray in their source code. Like you suggested they just use a check in the inner constructor (check_array_parameters), which is defined here.

I’m not sure of an alternative way of doing this.

Thanks! Yeah I realized when I tried to do:

T = SVector{pi}

And it worked.

Strangely though

T2 = SVector{"Test"}


The reason is for this behavior is described in Manual > Types > “Value Types”:

In Julia, you can’t dispatch on a value such as true or false . However, you can dispatch on parametric types, and Julia allows you to include “plain bits” values (Types, Symbols, Integers, floating-point numbers, tuples, etc.) as type parameters. A common example is the dimensionality parameter in Array{T,N} , where T is a type (e.g., Float64 ) but N is just an Int .

You can use Symbols but not Strings. None of the two are a “plain bits” type, Symbol is kinda of the only exception to this rule, but String follows the rule: it is not a “plain bits” type and therefore its values cannot be used as type parameters.

1 Like

The constraint you want must be expressed with an inner constructor, it can’t be imposed on a type level.

julia> struct State{T}
           State{T}() where {T} = T isa Int ? new{T}() : error("Only ints allowed")

julia> State{1}()

This has the consequence that users can always refer to the hypothetical type State{1.0}, but not to it’s instances:

julia> State{1.0}

julia> State{1.0}()
ERROR: Only ints allowed

I do sometimes wish we could do the constraints you’re asking for. I remember @jameson explained to me once that it’s a good thing you can create these hypothetical types. I’m not sure I found the explanation very satisfying, but I do place a lot of trust in his insight.


From your other thread, isn’t this what you want?

julia> using StaticArrays

julia> struct Test{N,T} 
         x :: SVector{N,T}

julia> Test(SVector{3,Int}(0,0,0))
Test{3,Int64}([0, 0, 0])

That will fail if N is not an integer, because of the constructor for the SVector. (But as pointed above the constraint, if you want to anticipate it, has to be written in the inner constructor).

It felt natural to me that we could add these constraints too.

Having types there would allow unpacking too maybe: Something like this:

struct{A::Tuple{U, U}} where {U}

Or something along those lines

Yes this is exactly what I wanted. I was just wondering if there was a way to write it in a more “Strongly typed” manner.