# Moving constants to compile-time "lookup" functions with Val

**URL:** <https://discourse.julialang.org/t/moving-constants-to-compile-time-lookup-functions-with-val/90314>\
**Category:** General Usage\
**Tags:** question\
**Created:** [November 15, 2022, 8:13pm UTC](https://discourse.julialang.org/t/moving-constants-to-compile-time-lookup-functions-with-val/90314 "2022-11-15T20:13:10Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![jacobusmmsmit](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacobusmmsmit/32/217669_2.png) [@jacobusmmsmit](https://discourse.julialang.org/u/jacobusmmsmit)\
**Post date:** [November 15, 2022, 8:13pm UTC](https://discourse.julialang.org/t/moving-constants-to-compile-time-lookup-functions-with-val/90314/1 "2022-11-15T20:13:10Z")

</div>

Hi everyone,

This may be my most ??? question yet. I read [this blog](https://ericphanson.com/blog/2019/another-example-of-using-type-domain-information-in-julia/) about using `Val` to use arguments at compile time, and I want to take this a step further by using `Val` to elide a long string of `const` declarations.

My MWE below is very close to my actual use case, that just has _another_ boolean-like abstract type to contend with. The MWE in question splits a population into 4 categories depending on two boolean conditions: their `Reputation` and their `Colour`. I want to calculate the proportion of each category in the population, each time filtering by either `Colour`, or `Reputation` _or both_ and I want the calculation of the indices used in the summation to be done at compile time.

e.g.

```julia
p = Population(MVector{4, Float64}([0.1, 0.2, 0.3, 0.4]))
p(Red) == 0.3 # 1 and 2 are Red
p(Bad) == 0.6 # 1 and 3 are Bad
p(Bad, Red) == 0.2 # 2 is both Bad and Red
# Calculating the indices used in the last one should be done at
# compile time, the values not.

```

MWE:

```julia
using StaticArrays

abstract type Reputation end
struct Good <: Reputation end
struct Bad <: Reputation end

abstract type Colour end
struct Red <: Colour end
struct Blue <: Colour end

const all_indices = 1:4
const goods = (1, 3)
const reds = (1, 2)

struct Population 
    proportion::MVector{4, Float64}
end

Base.getindex(p::Population, i) = getindex(p.proportion, i)
sum_indices(object, indices) = sum(getindex(object, i) for i in indices)

indices(::Type{Good}) = goods
indices(::Type{Bad}) = Tuple(setdiff(all_indices, indices(Good))) 
indices(::Type{Red}) = reds
indices(::Type{Blue}) = Tuple(setdiff(all_indices, indices(Red)))

indices(conditions...) = indices(conditions)
indices(conditions) = indices(Val(conditions))
function indices(::Val{conditions}) where {conditions}
    return Tuple(Intersect(indices(cond) for cond in conditions))
end

(p::Population)(conditions...) = sum_indices(p, indices(conditions))

p = Population(MVector{4, Float64}(0.1, 0.2, 0.3, 0.4))

p(Red, Good)
p(Good) + p(Bad)

```

This fails with the stacktrace:

```julia
ERROR: TypeError: in Type, in parameter, expected Type, got a value of type Tuple{DataType, DataType}
Stacktrace:
 [1] Val(x::Tuple{DataType, DataType})
   @ Base ./essentials.jl:714
 [2] indices(conditions::Tuple{DataType, DataType})
   @ Main ~/.julia/dev/Socioevolutionary/scripts/mwe.jl:28
 [3] (::Population)(::Type, ::Vararg{Type})
   @ Main ~/.julia/dev/Socioevolutionary/scripts/mwe.jl:33
 [4] top-level scope
   @ ~/.julia/dev/Socioevolutionary/scripts/mwe.jl:37

```

Is what I’m doing really dumb?  
Is there a simpler way (please I hope so)?  
Could I have just written out all of the indices myself? (Yes, I did, but then I nerdsniped myself into this question.)

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [November 15, 2022, 9:57pm UTC](https://discourse.julialang.org/t/moving-constants-to-compile-time-lookup-functions-with-val/90314/2 "2022-11-15T21:57:49Z")

</div>

Mh, if `proportion` is a `MVector` then it seems impossible to do it at compile time since the value might change at runtime.

It looks clearly like totally unneeded optimisation 😛 which you seem aware of. So I assume we are here for the fun…

Here is my attempt

```julia
red = Val( (:reds, true) )
blue = Val( (:reds, false) )
good = Val( (:goods, true) )
bad = Val( (:goods, false) )

p = Val( (; population = (0.1, 0.2, 0.3, 0.4), reds = (1,2), goods = (1,3)) )

indices(::Val{p}) where {p} = eachindex(p.population)
function indices(val_p::Val{p}, ::Val{cond}, conditions... ) where {p, cond}
    cond_indices = getproperty(p, cond[1])
    other_indices = indices(val_p, conditions...)
    if cond[2] == true
        return intersect( cond_indices, other_indices)
    else 
        return setdiff( cond_indices, other_indices )
    end
end

function ratio(val_p::Val{p}, conditions...) where {p} 
    return sum( p.population[i] for i in indices(val_p, conditions...) )
end

ratio(p, red)
ratio(p, red, good)

```

---

<div class="post-metadata">

**Author:** ![jacobusmmsmit](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jacobusmmsmit/32/217669_2.png) [@jacobusmmsmit](https://discourse.julialang.org/u/jacobusmmsmit)\
**Post date:** [November 15, 2022, 9:59pm UTC](https://discourse.julialang.org/t/moving-constants-to-compile-time-lookup-functions-with-val/90314/3 "2022-11-15T21:59:39Z")

</div>

Oh my bad, I just mean that the indices (and intersections thereof) returned should be done at compile time! I’ll update the question.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [November 15, 2022, 10:18pm UTC](https://discourse.julialang.org/t/moving-constants-to-compile-time-lookup-functions-with-val/90314/4 "2022-11-15T22:18:28Z")

</div>

Beyond the thing about `MVectors`, there’s also the issue that for `T::Type; U::Type` `typeof((T, U))==Tuple{DataType, DataType}` rather than `Tuple{Type{T}, Type{U}}`.

So one thing you could do is work directly with `Tuple{T, U}` instead of `(T, U)`.

---

<div class="post-metadata">

**Author:** ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)\
**Post date:** [November 15, 2022, 10:51pm UTC](https://discourse.julialang.org/t/moving-constants-to-compile-time-lookup-functions-with-val/90314/5 "2022-11-15T22:51:29Z")

</div>

Attempt #2 😉

```julia
using StaticArrays

struct Population{N, Red, Good}
    proportion::MVector{N, Float64}
end

red = Val(:red)
blue = Val(:blue)
good = Val(:good)
bad = Val(:bad)

indices(::Population{N,Red,Good}, ::Val{:red} ) where {N,Red,Good} = Red 
indices(::Population{N,Red,Good}, ::Val{:blue} ) where {N,Red,Good} = setdiff(Red, 1:N)
indices(::Population{N,Red,Good}, ::Val{:good} ) where {N,Red,Good} = Good 
indices(::Population{N,Red,Good}, ::Val{:bad} ) where {N,Red,Good} = setdiff(Good, 1:N)

function indices(p::Population{N,Red,Good}) where {N,Red,Good}
    return 1:N
end

function indices(p::Population{N,Red,Good}, cond, conditions... ) where {N,Red,Good}
    return intersect( indices(p, cond), indices(p, conditions...))
end

function ratio(p, conditions...)
    return sum( p.proportion[i] for i in indices(p, conditions...) )
end

p = Population{4,(1,2),(1,3)}([0.1, 0.2, 0.3, 0.4])

ratio(p, red)
ratio(p, red, good)

```

Of course, you can change the function names to make the operator call style possible… I just prefer to start with plain syntax when drafting stuff like this.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [November 15, 2022, 11:45pm UTC](https://discourse.julialang.org/t/moving-constants-to-compile-time-lookup-functions-with-val/90314/6 "2022-11-15T23:45:09Z")

</div>

If you _really_ need those values computed at compile time, maybe you need to use generated functions, or store the result of every possible input as fields of `Population`, you are already using functors anyway.
