# Simple compile-time evaluation for struct types

**URL:** https://discourse.julialang.org/t/simple-compile-time-evaluation-for-struct-types/103326
**Category:** New to Julia
**Created:** [August 29, 2023, 11:45am UTC](https://discourse.julialang.org/t/simple-compile-time-evaluation-for-struct-types/103326 "2023-08-29T11:45:16Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![RPS](https://avatars.discourse-cdn.com/v4/letter/r/9e8a1a/32.png) [@RPS](https://discourse.julialang.org/u/RPS)
#### Post date: [August 29, 2023, 11:45am UTC](https://discourse.julialang.org/t/simple-compile-time-evaluation-for-struct-types/103326/1 "2023-08-29T11:45:16Z")

</div>

I have a nested struct similar to the following:

```julia
struct A{N1, N2, T}
  a1::SVector{N1, T}
  a2::SVector{N2, T}
  b::B{N1, N2, T}
end

```

N1 and N2 are integers satisfying some simple (known) relationship, e.g. N2=N1+1. Therefore, I would like to ideally eliminate N2 as a parameter and replace it with a compile-time evaluation. How could I achieve something like the following invalid example?

```julia
struct A2{N1, T}
  a1::SVector{N1, T}
  a2::SVector{N1+1, T}
  b::B2{N1, T}
end

```

---

<div class="post-metadata">

### Author: ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)
#### Post date: [August 29, 2023, 2:38pm UTC](https://discourse.julialang.org/t/simple-compile-time-evaluation-for-struct-types/103326/2 "2023-08-29T14:38:07Z")

</div>

Unfortunately, you can’t. The parameter has to be there to be used as part of a field type. The best you can do is use an inner constructor to validate that invariants like this are respected.

```julia
struct A{N1, N2, T}
  a1::SVector{N1, T}
  a2::SVector{N2, T}
  b::B{N1, N2, T}
  function A(a1::SVector{N1,T},a2::SVector{N2,T},b::B{N1,N2,T}) where {N1,N2,T}
    N1+1 == N2 || error("a2 must be 1 longer than a1")
    return new{N1,N2,T}(a1,a2,b)
  end
end

# may want some outer constructors for promoting the element types, converting to SVector, etc.

```

However, there are packages that streamline or hide some of this. I don’t have experience with any of them, but it looks like [`ComputedFieldTypes.jl`](https://github.com/vtjnash/ComputedFieldTypes.jl) fits the bill.

---

<div class="post-metadata">

### Author: ![RPS](https://avatars.discourse-cdn.com/v4/letter/r/9e8a1a/32.png) [@RPS](https://discourse.julialang.org/u/RPS)
#### Post date: [August 30, 2023, 7:40am UTC](https://discourse.julialang.org/t/simple-compile-time-evaluation-for-struct-types/103326/3 "2023-08-30T07:40:35Z")

</div>

Thank you! I really wished that there was something in base that would allow for this.

For now I will try to use the ComputedFieldTypes.jl package or else employ a consistency check of the provided values as you suggested.

---

<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: [August 30, 2023, 11:58am UTC](https://discourse.julialang.org/t/simple-compile-time-evaluation-for-struct-types/103326/4 "2023-08-30T11:58:38Z")

</div>

> [@RPS](#):
>
> ` a2::SVector{N1+1, T}`

Thing is, what is `N1+1`? The language doesn’t know the type of `N1`, so it doesn’t know what `+` does. We could constrain `N1` to a concrete type, and in that case there is potential to infer `N1+1`. But a consistent structure needs the `+` method to be pure or never change, which is not guaranteed. Types cannot change structure because there’s no good way to handle existing instances and compiled code. The straightforward rule is requiring every parameter to be provided rather than computed from others.

ComputedFieldTypes doesn’t have this issue because the `@computed struct` header is only a shorthand inner constructor, not an actual type.

```julia
julia> c::Int = -1; function impure_add(a, b) a+b+c end
impure_add (generic function with 1 method)

julia> @computed struct B{N1}
         a::NTuple{impure_add(N1, 1), Int}
       end

julia> fulltype(B{1})
B{1, 1}

julia> c::Int = 0; fulltype(B{1}) # now specifies another type
B{1, 2}

```

Since there isn’t an inner constructor with all the parameters, you must use the shorthand and be careful about what it means at a given moment, or else you get cryptic errors:

```julia
julia> B{1}((1,2))
B{1, 2}((1, 2))

julia> c::Int = 1; B{1}((1,2)) # if you don't adjust properly
ERROR: MethodError: Cannot `convert` an object of type 
  Tuple{Int64{},Int64{}} to an object of type 
  Tuple{Int64{},Int64{},Int64}

```
