FHERMA KERNEL LANGUAGE
Types
Scalars
i8 i16 i32 i64 signed integers of the stated width u8 u16 u32 u64 unsigned integers of the stated width f32 f64 floating-point numbers of the stated width
The set is closed. i1, i128, f16, index are not types of this
language. A value wider than 64 bits is written as a tensor of machine
words — a 1740-bit modulus is tensor<L x u64>, L words, low word first.
A position or an index is a u64.
Half- and quarter-precision floats are absent on purpose: neither has a
native form in C++ before 23 or in stable Rust, and f8 is two incompatible
formats rather than one — a scaffold would compile and then fail on a byte
count nobody could trace. They return when a kernel needs them, together
with a wire form.
Value-parameter types
A value parameter takes the same strict scalars as data: N: u32, s: f64.
Its width on every wire is the width of its type — u32 is four bytes in the
point, uint32_t in a C++ skeleton, u32 in a Rust one — never assumed.
The loose words uint, int and real are retired: they named a number
without naming its width, and every wire had to guess. They remain reserved,
and using one is refused with a hint naming the strict spelling (uint →
u32, int → i64, real → f64).
A parameter that stands as a tensor dimension must be an unsigned integer type — a length below zero or with a fraction is not a length.
Classes
A class is a set of scalars a type parameter may range over:
Numeric = Integer ∪ Real Integer = i8, i16, i32, i64, u8, u16, u32, u64 Real = f32, f64
type T: Integer admits any integer; a specification then settles it —
type T = i64 — and i64 ∈ Integer is what makes that refinement valid.
Tensors
tensor<N x T> tensor<N x L x i64> tensor<1024 x 4 x u64> tensor<? x T>
The last position is the element type — a scalar or a type parameter; the
positions before it are dimensions — a declared value parameter, a literal,
or ?. Tensors do not nest, and secret never appears inside one: secrecy
applies to the aggregate as a whole.
? is a dimension a kernel leaves unknown. A specification must resolve
every one — a buffer whose size does not follow from the point cannot be
tested.
The x separator
Inside tensor<…> the letter x separates dimensions, but x is also a
legal identifier letter. The rule: x is a separator when it is surrounded
by whitespace, or when the character before it is a digit or ?.
tensor<1024x1xi64> three tokens — unambiguous after digits tensor<? x ? x i64> three tokens tensor<N x L x i64> three tokens tensor<NxLxi64> ONE identifier — invalid
When any dimension is named, put spaces around x. The spaced form is
canonical, and tools that print the language always use it.
Secret
secret<i64> secret<tensor<N x T>>
secret<T> marks a value hidden from whoever performs the computation.
Visibility is part of the problem statement: a specification may not change
secret<T> to T or back — that is a different problem and wants a
different kernel. secret does not nest.
Mixed visibility is ordinary, and often the whole point:
kernel lookup<type T: Numeric, N: u32>(
%index: secret<u64>,
%table: tensor<N x T>,
) -> %value: secret<T>
The index is hidden, the table is public, the result is hidden — which is what makes retrieval private.