You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This machine is based on the CEK machine: Control, Environment and
Kontinuation, but you'll also see mention of a CESK machine, which
includes an additional S for "Store" register which allows mutation. I
actually used Matt Might's CESK machine as a starting point because it
better showed how to handle multiple data types and primitive functions. I
reduced that to a CEK machine as a preliminary.
CEKF is my idea, though I'd be surprised if no-one else has thought of it.
CEKF stands for Control, Environment, Kontinuation and Failure. It adds a
"Fail" register, a backtracking continuation allowing trivial support for
amb.
The rest of this document closely follows
Matt Might's blog post,
slightly amended to describe a CEKF machine.
Expressions Exp to be Evaluated
Our grammar is still in A-Normal form. It omits the set!, and adds
amb and back to cexp:
Atomic expressions aexp always terminate and never cause an error:
So we'll use $\Sigma$ to denote the set (type) of CEKF states, which
are tuples of four registers: $\mathtt{Exp}$, $Env$, $Kont$ and $Fail$.
$\varsigma$ is the symbol we'll use for individual elements of this set
(states).
Pay close attention to the typesetting here, there are effectively two
domains, the expressions being evaluated and the rest of the machine. To
distinguish, the types of $\mathtt{Exp}$ are set in fixed width.
Environments
Environments directly map variables to values:
$$
\rho \in Env = \mathtt{var} \rightharpoonup Value
$$
Again you can read this as: $Env$ is the type (set of all environments),
$\rho$ is an element of that set (a specific environment) and Environments
are functions that take a variable and return a value.
Values
Values are the same as CESK:
$$
val \in Value ::= \mathbf{void}
\;|\;
z
\;|\;
\mathbf{\#t}
\;|\;
\mathbf{\#f}
\;|\;
\mathbf{clo}(\mathtt{lam}, \rho)
\;|\;
\mathbf{cont}(\kappa)
$$
$\mathbf{void}$ is the NULL equivalent, $z$ is an integer,
$\mathbf{\#t}$ and $\mathbf{\#f}$ are booleans, $\mathbf{clo}$ is
a closure of a lambda over an environment, and $\mathbf{cont}$ is a
continuation expressed as a value. $\mathbf{cont}$ is part of $Value$
because we support call/cc.
e.g. when evaluating exp in (let ((var exp)) body) the continuation
of that evaluation is as in the letk above. It turns out that when
evaluating ANF the only construct that requires a new continuation is
when evaluating the exp part of such a let (because in ANF all other
complex expressions are in tail position), hence the name $\mathbf{letk}$.
Failure Continuations
$$
f \in Fail ::= \mathbf{backtrack}(\mathtt{exp}, \rho, \kappa, f)\;|\;\mathbf{end}
$$
$\mathbf{end}$ is the failure continuation's equivalent to
$\mathbf{halt}$. $\mathtt{exp}$ is the (unevaluated) second exp of the
amb, $\rho$, $\kappa$ and $f$ are the values current when the amb
was first evaluated.
aexp evaluation
aexp are evaluated with an auxiliary function $\mathcal{A}$:
$$
\mathcal{A} : \mathtt{aexp} \times Env \rightharpoonup Value
$$
$applyproc$ (defined later) doesn't need an Env ($\rho$)
because it uses the one in the procedure produced by
$\mathcal{A}(\mathtt{lam},\rho)=\mathbf{clo}(\mathtt{lam},\rho)$.
Return
When the expression under evaluation is an aexp, that means we need
to return it to the continuation:
In CESK, letrec is done by extending the environment to point at
fresh store locations, then evaluating the expressions in the extended
environment, then assigning the values in the store.
Even then this only works if the computed values are closures, they
can't actually use the values before they are assigned.
I'm thinking that in CEKF, for letrec only, we allow assignment into
the Env (treating it like a store) because we're not bound by functional
constraints if we're eventually implementing in C. We couldn't write
this in Haskell though.
amb arranges the next state such that its first argument will be
evaluated, and additionally installs a new Fail continuation that,
if backtracked to, will resume computation from the same state, except
evaluating the second argument.
Q. Should $applykont$ get $f$ from its arguments or should we put it in
the $\mathbf{letk}$?
A. probably best to get it from its arguments, then we will backtrack
through call/cc.
Note again that DONE terminates the machine. Also note that the final
value $val$ is lost on return to the $\mathbf{halt}$ continuation.
In my implementation I have a fifth hidden register for the sole
putpose of preserving the final value in this situation.
Running the machine
inject
We need an $inject$ function that takes an expression and creates an
initial state: