Lecture 2: Verified Interpreters

Teacher: François Pottier

From operational semantics to (verified) interpreter

You have different semantics and want to translate between them.

A naïve interpreter

An interpreter:

executes a program.

Of course, OCaml doesn’t perform naïve step-by-step $β$-reductions, but for the sake of simplicity, we’ll study a simplified example of small-step semantic to begin with.

 type var = int (* a de Bruijn index *)

 type term =
    | Var of var
    | Lam of (* bind: *) term
    | App of term * term

(* Example: *)
 let id = Lam (Var 0)

Now: definition of $⇑^i (+k)$:

 let rec lift_ i k (t : term) : term = match t with
    | Var x -> if x < i then t else Var (x + k)
    | Lam t -> Lam (lift_ (i + 1) k t)
    | App (t1, t2) -> App (lift_ i k t1, lift_ i k t2)

let lift k t = lift_ 0 k t

And substitutions:

 let rec subst_ i (sigma : var -> term) (t : term) : term =
    match t with
     | Var x -> if x < i then t else lift i (sigma (x - i))
     | Lam t -> Lam (subst_ (i + 1) sigma t)
     | App (t1, t2) -> App (subst_ i sigma t1, subst_ i sigma t2)

let subst sigma t = subst_ 0 sigma t

and then the substitution $u · id$:

 let singleton (u : term) : var -> term = function
      0 -> u
    | x -> Var (x - 1)

Small-step call-by-value reduction:

let in_context f ox = match ox with
    | None -> None
    | Some x -> Some (f x)

let is_value = function
    | Var _ | Lam _ -> true
    | App _ -> false

let rec step (t : term) : term option =
    match t with
        | Lam _ | Var _ -> None
        (* Plotkin’s BetaV *)
        | App (Lam t, v) when is_value v ->
            Some (subst (singleton v) t)
        (* Plotkin’s AppL *)
        | App (t, u) when not (is_value t) ->
            in_context (fun t -> App (t, u)) (step t)
        (* Plotkin’s AppVR *)
        | App (v, u) when is_value v ->
            in_context (fun u -> App (v, u)) (step u)
        (* All cases covered already, but OCaml cannot see it. *)
        | App (_, _) -> assert false

⟶ Very inefficient

Natural/Big-step Semantics

Reduction sequence of $t t’$ has the form:

\[t t' ⟶^\ast_{cbv} (λx.u) t' ⟶^\ast_{cbv} (λx.u) v ⟶_{cbv} u[v/x] ⟶^\ast_{cbv} v\]

where $t ⟶^\ast_{cbv} λx.u$ and $t’ ⟶^\ast_{cbv} v$

Big-step relation $↓_{cbv}$

\[\cfrac{}{v \, ↓_{cbv} \, v} \qquad \cfrac{t_1 \, ↓_{cbv} \, λx.u_1 \qquad t_2 \, ↓_{cbv} \, v_2 \qquad u_1[v_2/x] ↓_{cbv} v}{t_1 t_2 \, ↓_{cbv} \, v}\]

A proof of

  • $t ⟶^\ast_{cbv} t’$ has linear structure
  • $t ↓_{cbv} v$ has tree structure.

Big-step interpreter

exception RuntimeError

let rec eval (t : term) : term =
    match t with
        | Lam _ | Var _ -> t
        | App (t1, t2) ->
        let v1 = eval t1 in
        let v2 = eval t2 in
            match v1 with
            | Lam u1 -> eval (subst (singleton v2) u1)
            |_ -> raise RuntimeError

NB: May not terminate (evaluate $Ω$ for example)

Big-step vs. Small-step

Lemma (From Big-step to Small-step): If $t ↓{cbv} v$, then $t ⟶^\ast{cbv} v$.

Lemma (From Small-step to Big-step): If $t_1 ⟶{cbv} t_2$ and $t_2 ↓{cbv} v$, then $t_1 ↓_{cbv} v$.

Environments and closures

Environment:

finite map of variables to (closed) values.

Idea: record $x$ being bounded to $v$ while evaluating a $t$. Instead of applying the substitution $[v/x]$ to the code, record the binding $x ⟼ v$ in an environment.

Closure:

a pair $⟨λx.t \; \mid \; e⟩$, where $e$ is an environment and $fv(λx.t) ⊆ dom(e)$ (a closure is closed)

bigcbv: $t ↓_{cbv} v$

ebigcbv: $∅ ⊢ t ↓_{cbv} c$

Leave a comment