This case study shows how to implement a type-preserving normalizer using normalization by evaluation [Berger and Schwichtenberg 1991]. Here we compute \(\eta\)-long normal forms for the simply-typed lambda calculus. See also [Cave and Pientka 2012] for more information on this example.
The setup is a standard intrinsically-typed lambda calculus:
tp : type.
--name tp T.
b : tp.
arr : tp → tp → tp.
tm : tp → type.
--name tm E.
app : tm (arr T S) → tm T → tm S.
lam : (tm T → tm S) → tm (arr T S).
schema tctx = block tm T;
Below, we describe \(\eta\)-long normal forms. Notice that we allow embedding of neutral terms
into normal terms only at base type b: this enforces \(\eta\)-longness.
neut : tp → type.
--name neut R.
norm : tp → type.
--name norm M.
nlam : (neut T → norm S) → norm (arr T S).
rapp : neut (arr T S) → norm T → neut S.
embed : neut b → norm b.
schema ctx = block neut T;
The key idea is to evaluate terms into a special domain of semantic values, which can then be
reified back into normal forms. Below we describe the domain of semantic values, which we can
think of as being defined by recursion on the type. At base type, semantic values are precisely
normal forms. At arrow type arr A B, a semantic value
Sem [g] [ |- arr A B] is a function from semantic values of type A in
an extended context h to semantic values of type B in h.
The extension of a context is tracked by a substitution $W which transports normal
terms in context g to normal terms in context h.
In the course of this development, $W will only ever be instantiated with a
weakening substitution, transporting from g to
g, x1:neut T1,..., xn:neut Tn, but the extra generality does no harm here.
stratified Sem : { : ctx} ( ⊢ tp) → ctype =
| Base : (g ⊢ norm b) → Sem [g] [ ⊢ b]
| Arr :
{ : ctx}
({ : ctx} { : $(h ⊢ g)} Sem [h] [ ⊢ ] → Sem [h] [ ⊢ ]) →
Sem [g] [ ⊢ arr ];
We can weaken semantic values:
rec sem_wkn : { : ctx} { : $(h ⊢ g)} Sem [g] [ ⊢ ] → Sem [h] [ ⊢ ] =
mlam ⇒ mlam ⇒ fn e ⇒
case e of
| Base [g ⊢ ] ⇒ Base [h ⊢ [$W]]
| Arr [g] f ⇒ Arr [h] (mlam ⇒ mlam ⇒ f [h'] $[h' ⊢ $W[$W2]]);
Environments:
typedef Env : { : tctx} { : ctx} ctype =
{ : ( ⊢ tp)} { : #(g ⊢ tm [])} Sem [h] [ ⊢ ];
rec env_ext : Env [g] [h] → Sem [h] [ ⊢ ] → Env [g, x : tm []] [h] =
fn s ⇒ let s : Env [g] [h] = s in fn e ⇒ mlam ⇒ mlam ⇒
case [g, x : tm _ ⊢ #p] of
| [g, x : tm ⊢ x] ⇒ e
| [g, x : tm ⊢ #q[…]] ⇒ s [ ⊢ ] [g ⊢ #q];
rec env_wkn :
{ : ctx} { : ctx} { : $(h' ⊢ h)} Env [g] [h] → Env [g] [h'] =
mlam ⇒ mlam ⇒ mlam ⇒ fn s ⇒ let s : Env [g] [h] = s in mlam ⇒
mlam ⇒ sem_wkn [h'] $[h' ⊢ $W] (s [ ⊢ ] [g ⊢ #p]);
We evaluate a term in an environment providing semantic values for each free variable:
rec eval : (g ⊢ tm []) → Env [g] [h] → Sem [h] [ ⊢ ] =
fn t ⇒ fn s ⇒ let s : Env [g] [h] = s in
case t of
| [g ⊢ #p] ⇒ s _ [g ⊢ #p]
| [g ⊢ lam (\x. )] ⇒
Arr [h] (mlam ⇒ mlam ⇒ fn e ⇒
eval [g, x : tm _ ⊢ ] (env_ext (env_wkn [h'] [h] $[h' ⊢ $W] s) e))
| [g ⊢ app ] ⇒ let Arr [h] f = eval [g ⊢ ] s in
f [h] $[h ⊢ …] (eval [g ⊢ ] s);
Reflect and reify
rec app' :
( : ctx) { : (g ⊢ neut (arr [] []))} (g ⊢ norm []) → (g ⊢ neut []) =
mlam ⇒ fn n ⇒ let [g ⊢ ] = n in [g ⊢ rapp ];
rec reflect : (g ⊢ neut []) → Sem [g] [ ⊢ ] =
fn r ⇒ let [g ⊢ ] : (g ⊢ neut []) = r in
case [ ⊢ ] of
| [ ⊢ b] ⇒ Base [g ⊢ embed ]
| [ ⊢ arr ] ⇒
Arr [g] (mlam ⇒ mlam ⇒ fn e ⇒ reflect (app' [h ⊢ [$W]] (reify e)))
and rec reify : Sem [g] [ ⊢ ] → (g ⊢ norm []) =
fn e ⇒
case e of
| Base [g ⊢ ] ⇒ [g ⊢ ]
| Arr [g] f ⇒
let [g, x : neut _ ⊢ ] =
reify (f [g, x : neut _] $[g, x ⊢ …] (reflect [g, x : neut _ ⊢ x])) in
[g ⊢ nlam (\x. )];
Finally, we can normalize terms, by first evaluating them into semantic values, then reifying them back into semantic values.
rec emp_env : Env [] [] =
mlam ⇒ mlam ⇒ impossible [ ⊢ #p];
rec nbe : ( ⊢ tm ) → ( ⊢ norm ) =
fn t ⇒ reify (eval t emp_env);
One could also implement nbe to directly normalize terms in an environment:
[g |- tm A] -> [h |- norm A] where h is appropriately related to
g. We leave this as an exercise for the reader.
© Computation and Logic Group
Department of Computer Science, McGill University
3480 University Street, Montreal, Quebec, Canada, H3A 0E9