(Warning: Larger wall of text than intended, with some ideas in it. Maybe they’ll serve as inspiration, or more likely I’ve gotten something completely wrong.)
I’m having trouble understanding why this makes Max3 a kind (in the Haskell definition of “kind”). It seems to me that Max3 has the same kind as a trait/typeclass - say, std::cmp::Eq. That is, I think the statement T: Eq (the type T is a subtype of the typeclass Eq) is analogous to the statement const N: Max3 (the type const N is a subtype of the typeclass (type) Max3).
(From a purely set-theoretic standpoint, these things are all the same - a value Max3::One is an element of a single-valued set {Max3::One}, which is a subset of Max3 = {Max3::One, Max3::Two, Max3::Three}, which could be a subset of Eq. In that model, n: Max3 is an element n, while const N: Max3 is a single-valued set {N}.)
BTW, in Haskell, the kind of various typeclasses (Eq, Num, etc.) is * -> Constraint (where * is the kind of a type). But since the Constraint type is sort of opaque and I don’t understand it, I don’t know if these really are the same kind.
One observation from the above is that there is a one-to-one-mapping between values n: Max3 (n) and their lowest types (consts) const n: Max3 ({n}). We could say that there is an implicit conversion from the type const n to the value n because const n is a subset of {n}. We could also say that a const declaration like const N: Max3 = Max3::One is actually a type declaration, and by extension that a literal Max3::One is actually a type regardless of context.
Given all of this (just when you thought I had long gone off the rails, this is where I tie it back into the original conversation): We could say we are actually already putting types (consts) in value position: f(Max3::One). it seems entirely reasonable, then, to actually constrain an argument to be a type (a further constraint than currently possible):
fn f<const N: Max3>(n: N);
Breaking down the type constraints here:
-
const N is a subset of Max3
-
n is a subset of const N
GIven an invocation f(Max3::One), the type N could therefore be inferred:
-
n is a subset of const N
-
n is single-valued
-
N is single-valued
Therefore, n == N == Max3::One, so the inferred invocation is: f<Max3::One>(Max3::One).
My conclusion is basically that the function declaration syntax I used seems sensible, and it seems (to me) to tie well back into RFC2000. (Although I’m actually having trouble deciding which of the following makes more sense. It’s late and I have confused myself.)
fn f<const N: Max3>(n: N);
fn f<const N: Max3>(const n: N); // n needs to be const, so that may need to be explicit
fn f<const N: Max3>(const n: const N);
The latter two probably resolve naturally back into the original syntax proposal by contracting the two constraints: fn f(const n: Max3).