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.
Types are a particular kind of value.
∗ is a particular type (the type of types).
Γ ranges over contexts (which map names to types).
All varieties of lambda binders are values.
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