This is an attempt at formalizing some aspects of Zig’s comptime.
We have three types of binders:
Ordinary lambda binders (λ) can capture variables and be applied arbitrarily, but are not dependent.
They correspond to a function like fn f(x: A) B {}.
Static lambda binders (λs) cannot capture variables.
They correspond to a function like fn f(comptime x: A) B {},
where x is not free in B.
Their argument must be provided at compile time, and type checking
the body is deferred until they are called.
Generic lambda binders (Λs) cannot capture variables.
They correspond to a function like fn f(comptime x: A) B(x) {}.
Their argument must be provided at compile time, and the type checking
of both the body and the return type is deferred until they are called.
Conventions:
Uppercase Latin letters range over terms.
Lowercase Latin letters range over values (a subset of terms).
Except x, which ranges over variables.
Γ ranges over contexts (which map names to types).
∗ is a particular value.
All types of lambda binders are expressions.
Binders are annotated with their argument and return types,
which we sometimes omit for brevity.
We will be simultaneously defining two relations:
Γ⊢M:aM⇝z:aM has type a in Γ M evaluates to z at type a