Felix has a very much more general capability.
var x : 3 * 2 * 5 = (case 2 of 3, case 1 of 2, case 4 of 5);
println$ x;
var y = x :>> int;
println$ y;
var z : 2 ^ 5 = true, true, true, false, false;
println$ z, z.1;
var a = z :>> int;
println$ a;
Basically there is a concept called a compact linear type. Unit (1) is compact linear, any finite sum, product, or exponential of a compact linear type is compact linear. For example
typedef bool = 2;
is compact linear.
Values of compact linear types have a natural bijection to integers, represented above by using the type coercion operator :>>
. Felix represents small compact linear types which are defined as 64 bit unsigned integers, or non-compact arrays with compact linear indices.
Felix supports projections and assignments to any component of a compact linear type individually. The primary intent is for array indices because this then gives you polyadic (rank independent) array access.
For example you can coerce between double^10^20
which is a linear array dimension 20 of linear arrays dimenion 10 to a matrix double^(10*20)
which has takes a tuple index to double^200
which is a single linear array of 200 values. The arrays here are not compact linear but the exponents are.
The compiler optimises these operations to ones using addition, multiplication, division and modulo of constants, and expects the underlying C compiler to convert these to bitshifts for powers of two.
The operations are generalisations of C bitfields, which only work for powers of 2. Compact linear types are fully general, up to the 64 bit restriction. However Felix does not provide pointers to components of compact linear types although you can assign to them.