Hi everyone,
This may be my most ??? question yet. I read this blog 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.
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:
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:
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.)