\documentclass[11pt]{article}
\usepackage{ifthen}
\usepackage{alltt}
% \usepackage{indentfirst}
\usepackage{latexsym}
\usepackage[pdftex]{graphicx}
\usepackage{gdefs}
% \usepackage{logdef}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{setspace}
\usepackage{rotating}
\usepackage{longtable}
\usepackage{hhline}
\usepackage{refs}
\usepackage{alg}
%\usepackage{graph}
\usepackage{changebar}
\usepackage{array}
\usepackage{wrapfig}
\usepackage{textcomp}
\usepackage[small,compact]{titlesec}

\usepackage{tweaklist}
% this sets the spacings in the itemize environment
\renewcommand{\itemhook}{\setlength{\topsep}{0pt}%
  \setlength{\itemsep}{0pt}%
  \setlength{\parsep}{0ex}%
}

% this sets the spacings in the enumerate environment
\renewcommand{\enumhook}{\setlength{\topsep}{0pt}%
  \setlength{\itemsep}{0pt}%
  \setlength{\parsep}{0ex}%
}

% this sets the spacings in the description environment
\renewcommand{\descripthook}{\setlength{\topsep}{0pt}%
  \setlength{\itemsep}{0pt}%
  \setlength{\parsep}{0ex}%
}

\setlength{\topmargin}{0.0in}
\setlength{\headheight}{0.0in}
\setlength{\headsep}{0.0in}
\setlength{\textwidth}{6.5in}
\setlength{\oddsidemargin}{0.0in}
\setlength{\evensidemargin}{0.0in}
\setlength{\textheight}{9.0in}

\setcounter{topnumber}{5}
\def\topfraction{0.99}
\def\textfraction{0.05}
\def\floatpagefraction{0.9}
\setcounter{bottomnumber}{5}
\def\bottomfraction{0.99}
\setcounter{totalnumber}{10}
\def\dbltopfraction{0.99}
\def\dblfloatpagefraction{0.8}
\setcounter{dbltopnumber}{5}

\setlength{\marginparwidth}{.95in}
\let\oldmarginpar\marginpar
\renewcommand\marginpar[1]{\-\oldmarginpar[\raggedleft\footnotesize #1]%
{\raggedright\footnotesize #1}}

\let\oldthebibliography=\thebibliography
  \let\endoldthebibliography=\endthebibliography
  \renewenvironment{thebibliography}[1]{%
    \begin{oldthebibliography}{#1}%
      \setlength{\parskip}{0ex}%
      \setlength{\itemsep}{0ex}%
  }%
  {%
    \end{oldthebibliography}%
  }

\usepackage{tocloft}
\renewcommand\cftsecfont{\normalfont}
\renewcommand\cftsecpagefont{\normalfont}
\renewcommand{\cftsecleader}{\cftdotfill{\cftsecdotsep}}
\renewcommand\cftsecdotsep{\cftdot}
\renewcommand\cftsubsecdotsep{\cftdot}
\renewcommand\cftsubsubsecdotsep{\cftdot}
% \renewcommand{\contentsname}{}

\newcommand{\pe}{\textit{pe}}
\newcommand{\interp}{\textit{int}}
\newcommand{\cogen}{\textit{cogen}}

\newcommand{\TAB}{\hspace*{2.0ex}}

\title{Partial Evaluation, Part 1 \\
       CS701
}
\author{Thomas Reps}
\date{}

\begin{document}           % End of preamble and beginning of text.

\maketitle
\begin{abstract}
This lecture concerns partial evaluation. Comparisons are made between
partial evaluation and compiling. Two principles are given that help
determine when to use partial evaluation.
We show how a partial evaluator can be used to translate a program
from one language $L_2$ into another language $L_1$ even if the
partial evaluator's capabilities are limited so that its input is an
$L_1$ program and its output is an $L_1$ program.
\end{abstract}

\tableofcontents

\newpage

Note: quite a lot (but not all) of the material presented here is
taken from the following book \cite{Book:JGS93}.

\section{Introduction}
When using a normal evaluator, one must supply all of the
parameters.
Otherwise, the output is undefined.

\begin{center}
 \begin{tabular}{rr}
  program:& $P(x,y,z) \longrightarrow$\\
  data:& $\langle x,y\rangle \longrightarrow$
 \end{tabular}
 \fbox{
  \begin{tabular}{c}
   Evaluator\\
   (Interpreter)
  \end{tabular}
 } $\longrightarrow \bot$
\end{center}

This outcome may be what we expected, but it can be improved. Using a
partial evaluator, we can obtain a modified version of the input
program that is optimized for the supplied parameters. Of course we
expect the modified version of the input program to behave that same
when given the rest of its input as the original version of the
program when it is given the same input.

\begin{center}
 \begin{tabular}{rr}
  program:& $P(x,y,z) \longrightarrow$\\
  data:& $\langle x,y\rangle \longrightarrow$
 \end{tabular}
 \fbox{
  \begin{tabular}{c}
   Partial\\
   Evaluation
  \end{tabular}
 } $\longrightarrow P_{\langle x,y\rangle}(z) = P(x,y,z)$
\end{center}

The input program is called the \emph{subject program}\footnote{In
  logic and philosophy, this program would be called the \emph{object program}.
  We use ``subject program'' because in the field of compilers
  the term ``object program'' is already used for something else.
}
and the output program in called the \emph{residual program}.

\subsection{Example: Ray Tracing}
In ray tracing, there is a tree $T$ that describes the object(s) in
the scene. Given this tree, ray tracing code will determine how each
ray contributes to the final image.

\begin{algorithm}[ht]
 \begin{algtab}
  \algforeach{ray $\overrightarrow{r}$}
   $pixel \leftarrow \text{Trace}(T, \overrightarrow{r})$\\
   display $pixel$\\
  \algend
 \end{algtab}
 \caption{Ray Tracing Code}
\end{algorithm}

In other words, for many different rays, Trace is called with the same
value of tree $T$.
Using partial evaluation, we can create a version of Trace that is
optimized for $T$.

\begin{center}
 \begin{tabular}{r}
  $\text{Trace}(T,\overrightarrow{r}) \longrightarrow$\\
  $\langle T\rangle \longrightarrow$
 \end{tabular}
 \fbox{
  \begin{tabular}{c}
   Partial\\
   Evaluation
  \end{tabular}
 } $\longrightarrow \text{Trace}_{\langle T\rangle}(\overrightarrow{r})$
\end{center}
The residual program $\text{Trace}_{\langle
T\rangle}(\overrightarrow{r})$ would be used as follows:
\begin{algorithm}[ht]
 \begin{algtab}
  \algforeach{ray $\overrightarrow{r}$}
   $pixel \leftarrow \text{Trace}_{\langle T\rangle}(\overrightarrow{r})$\\
   display $pixel$\\
  \algend
 \end{algtab}
 \caption{Partially Evaluated Ray Tracing Code}
\end{algorithm}

Other examples include
\begin{itemize}
  \item
    Encryption: For many different blocks, \texttt{Encrypt(key, block)}
    is called with the same value of \texttt{key}.
  \item
    Output: For many strings \texttt{s}, \texttt{Write(fd, s)} is
    called with the same value of \texttt{fd}.
  \item
    String matching: For many substrings \texttt{s}, \texttt{Match(pat,s)}
    is called with the same value of \texttt{pat}.
\end{itemize}

\section{Relationship between Partial Evaluation and Compilation}
The notation $\semp{p}$ denotes the meaning of $p$,
as distinct from uses of $p$, which indicate that $p$ is being treated as a
data object (i.e., the text, abstract syntax tree, or control-flow
graph for $p$).

For a program $p$ and input $i$, let $\semp{p}[i]$ denote
the result of running $p$ on $i$.

\subsection{Properties of a Partial Evaluator}
Let $p$ be a program that takes a pair of inputs $[s,d]$.
A partial evaluator $\pe$ has the following property: for
all programs $p$ and inputs $s$, it produces
\begin{align}
 \semp{\pe} [p,s] &= p_s, \text{ such that } \label{Eq:pe1} \\
 \text{ for all } d, \semp{p_s}[d] &= \semp{p}[s,d]. \label{Eq:pe2}
\end{align}
The input variables $s$ and $d$ typically stand for \emph{static} and
\emph{dynamic} (or \emph{supplied} and \emph{delayed}).

\subsection{The Futamura Projections}

\paragraph{Compilation/Translation}
An interpreter $\interp$ is an example of a program that takes a pair of
inputs (in this case, a program $p$ and input-datum $i$) and has the same
behavior as that program on its input:
\begin{equation}
  \label{Eq:interpMeaning}
  \semp{\interp}[p,i] = \semp{p}[i].
\end{equation}

Suppose that we want to run the same interpreted program $p$ on many
(dynamic) inputs.
This desire leads to the idea of applying a partial
evaluator to an interpreter.
\begin{align}
 \semp{pe}[\interp, p] &= int_p, \text{ such that } \label{Eq:fp1}\\
 \text{ for all } i, \semp{\interp_p}[i] &= \semp{\interp}[p,i] \tag{from \eqref{pe2}}  \\
 &= \semp{p}[i] \tag{from \eqref{interpMeaning}}
\end{align}
Notice that $\interp_p$ and $p$ take the same input and produce the same
output.
The explanation is that $\interp_p$ is a ``compiled'' version of $p$.

\paragraph{Compiler/Translator}
Suppose that we want to partially evaluate the same interpreter on
many (dynamic) programs.
This desire leads to the idea of applying a partial
evaluator to a second partial evaluator whose input is an interpreter.
\begin{align}
 \semp{\pe}[\pe, \interp] &= \pe_\interp, \text{ such that } \label{Eq:fp2}\\
 \text{for all } p, \semp{\pe_\interp}[p] &= \semp{\pe}[\interp,p] \tag{from \eqref{pe2}} \\
 &= \interp_p \tag{from \eqref{fp1}}
\end{align}
Note that $\pe_\interp$ takes in $p$ and returns $\interp_p$, the compiled
version of $p$. Therefore, $\pe_\interp$ is a compiler.

\paragraph{Compiler-Compilation/Translator-Generation}
Suppose that we want to partially evaluate the same partial evaluator
on many (dynamic) interpreters.
This desire leads to the idea of applying a
partial evaluator to second partial evaluator that expects a partial
evaluator as input.
\begin{align}
 \semp{\pe}[\pe, \pe] &= \pe_\pe, \text{ such that } \label{Eq:fp3} \\
 \text{for all } \interp, \semp{\pe_\pe}[\interp] &= \semp{\pe}[\pe,\interp] \tag{from \eqref{pe2}} \\
                     &= \pe_\interp  \tag{from \eqref{fp2}}
\end{align}
Note that $\pe_\pe$ takes in an interpreter and returns a (compiled)
compiler. Therefore, $\pe_\pe$ is a compiler generator---also known
as a ``compiler-compiler''---whose input specification of the
compiler to be generated is an interpreter.
What we mean by this last remark should become clearer in
\sectref{CompilingViaPartialEvaluation}.

\paragraph{Futamura Projections}
\eqrefsp{fp1}{fp2}{fp3} are known as the first, second, and third
\emph{Futamura projections}, respectively.
Note the pattern of right-shifting in \eqref{interpMeaning} and
the Futamura projections:
\[
\begin{array}{l@{\hspace{0ex}}l@{\hspace{0ex}}l}
  \semp{\interp} & [p,       &i] \\
  \semp{pe}      & [\interp, &p] \\
  \semp{\pe}     & [\pe,     &\interp] \\
  \semp{\pe}     & [\pe,     &\pe]
\end{array}
\]

\section{Compiling (Translating) via Partial Evaluation}
\label{Se:CompilingViaPartialEvaluation}

We now look at various possibilities for the programming languages
that could be involved.
We can write down more detailed versions of the Futamura projections
by using subscripts and superscripts on $\pe$, $\interp$, and $p$
to indicate various languages with which these programs are associated.

Notation:
\begin{enumerate}
  \item
    $\semp{p_L}_L$ means that the program $p$ is an $L$-program that
    is interpreted as an $L$-program.
    (The outer $L$ is typically redundant because it would not
    make sense to perform something like $\semp{p_{L_1}}_{L_2}$,
    and thus the outer $L$ can be omitted.)

  \item
    $\interp_{L_1}^{L_2}$ means that the interpreter $\interp$ is an
    $L_1$-program that interprets $L_2$-programs.
    Thus, the fully annotated version of \eqref{interpMeaning} is
    \begin{equation}
      \label{Eq:interpMeaningAnnotated}
      \semp{\interp_{L_1}^{L_2}}_{L_1}[p_{L_2},i] = \semp{p_{L_2}}_{L_2}[i].
    \end{equation}

  \item
    $\pe_{L_3}^{L_1}$ means that the partial evaluator $\pe$ is an
    $L_3$-program that partially evaluates an $L_1$-program (to produce another
    $L_1$-program).
\end{enumerate}
Notice that interpreters and partial evaluators are special because
they must indicate which language they interpret and partially
evaluate respectively.

To be more general, we could have introduced notation like $\pe_{L_3}^{L_1 \rightarrow L_2}$
to mean that $\pe$ is an $L_3$-program that partially evaluates
$L_1$-programs to produce $L_2$-programs.
However, that would mean that $\pe$ has language translation
already ``built into it,'' and as we now show, translation between
languages can be achieved \emph{without} the partial evaluator
having the capability to perform language translation \emph{per se}.
The only feature that we rely on is that $\pe$ and $\interp$ can
be written in one language $L$, but operate on programs written in
a different language $L'$.

Using the above-defined notation, we can rewrite the first Futamura
projection (\eqref{fp1}) as follows:
\begin{align*}
 \semp{\pe_{L_3}^{L_1}} [\interp_{L_1}^{L_2}, p_{L_2}] &= (\interp_p)_{L_1}, \text{ such that } \\
 \text{for all } d, \semp{ (\interp_p)_{L_1}} [d] &= \semp{ p_{L_2}}[d].
\end{align*}
For all inputs, the programs $\interp_p$ and $p$ produce the same output.
However, $(\interp_p)_{L_1}$ is written in $L_1$, whereas
$p_{L_2}$ is written in $L_2$.
Therefore, $(\interp_p)_{L_1}$ is a \emph{compiled}---or at
least \emph{translated}---version of $p_{L_2}$.

Note that what captured our knowledge of how languages $L_1$ and $L_2$
are related is the interpreter $\interp_{L_1}^{L_2}$.
In other words,
\begin{quote}
  \textit{If we are given a partial evaluator that works on $L_1$
  programs, to be able to translate an arbitrary $L_2$ program into
  $L_1$, all we need do is specify in $L_1$ how to interpret $L_2$
  programs.}
\end{quote}

\section{Applications of Partial Evaluation}
There are two guiding principles to follow when considering where to
use partial evaluation. Partial evaluation is best used where (1)
there are multiple parameters and (2) the parameters have different
rates of variation.

\subsection{Examples}
\subsubsection{String Matching}
Given a two string $s$ and $p$, we want to know if $p$ is a substring
of $s$. The string $p$ is called the \emph{pattern} and we say there
is a \emph{match} is $p$ is a substring of $s$. We express this
problem as $\text{Match}(p,s)$. To use partial evaluation, we need to
determine which argument changes more frequently. Initially, it seems
that $s$ changes more slowly than $p$.

\paragraph{Naive String Matching}
The naive algorithm just tries to match $p$ to every possible location
of $s$.
\begin{align*}
 s = &s_1s_2s_3 \cdots s_ms_{m+1}s_{m+2} \cdots s_n\\
 p = &p_1p_2p_3 \cdots p_m \tag{first try}\\
     &\ \ \ p_1p_2p_3 \cdots p_m \tag{second try}\\
     &\ \ \ \ \ \ p_1p_2p_3 \cdots p_m \tag{third try}\\
     &\ \ \ \ \ \ \ \ \ \vdots
\end{align*}
Notice that $p$ is just compared to the fist $|p|$ spots in $s$, and
then, if there is no match, $p$ is shifted and compared to the second
through $|p|+1$ spots. This continues until a match is found or all
the (valid) locations of $s$ have been tested. The code for the naive
string-matching algorithm is:
\begin{algorithm}[h]
 \begin{algtab}
  \algforto{$i=1$}{$|s| - |p| + 1$}
   \algif{$\text{Match}(p, s_i \cdots s_{i+|p|-1})$}
    \algreturn true\\
   \algend
  \algend
  \algreturn false
 \end{algtab}
 \caption{Naive String Matching}
\end{algorithm}

The cost of this approach is $O(n \cdot m)$. Notice that $p$ is fixed
throughout the loop, and hence looks like a good candidate for partial
evaluation. The second argument of Match is clearly changing.

\paragraph{Knuth-Morris-Pratt (KMP) String-Matching Algorithm}
KMP takes advantage of redundancy by using memory to make larger
shifts (rather than just shifts of size one as in the naive
algorithm).
\begin{align*}
     &1234567\\
 s = &abbabab\\
 p = &abbb \tag{first try}
\end{align*}
Observe that after this comparison, we know that a single shift will
also fail (remembering that the second character was a $b$ while the
first character in $p$ is an $a$. Thus, the second test of KMP will
begin at $s_4$.

The cost of the KMP algorithm is $O(n + m)$. This same effect can be
obtained via partial evaluation.\footnote{Consel and Danvy}

\subsubsection{Printf}
The printf function takes a format string $s$ and a variable number of
arguments $va$ and prints the format string in the context of the
variable arguments. The format string can be thought of as a
program. If $s = ab\%d$, then the corresponding program for printf$(s,
va)$ would be
\begin{algorithm}[ht]
 \begin{algtab}
  print $a$\\
  print $b$\\
  printSignedDecimalInteger $va[0]$.
 \end{algtab}
 \caption{Program of a Format String}
\end{algorithm}

Now in C/C++, the format string must be constant or static. Even in
other languages though, programmers are most likely to have a static
format string and a dynamic list of variable arguments. Using partial
evaluation, we can convert $\text{printf}(s, va)$ into
$\text{printf}_s(va)$. It is also interesting to point out that
$\text{printf}_s$ will also be partially evaluated to take an exact
number of arguments instead of a variable number.

\subsubsection{Cryptography}
While the creation of a cryptographic scheme maybe quite complex,
using a cryptographic scheme is quite easy. There are two functions,
Encrypt$(key, plainText)$ and Decrypt$(key, crypterText)$ which work
as expected. Normally though, the entire text is not encrypted or
decrypted at once. Instead, the text in encrypted and decrypted a
block at a time.
\begin{algorithm}[ht]
 \begin{algtab}
  \algforeach{$block \in plainText$}
   $\text{add}(\text{Encrypt}(key, block), cypterText)$\\
  \algend
 \end{algtab}
 \caption{Encryption Code}
\end{algorithm}
\begin{algorithm}[ht]
 \begin{algtab}
  \algforeach{$block \in cypterText$}
   $\text{add}(\text{Encrypt}(key, block), plainText)$\\
  \algend
 \end{algtab}
 \caption{Decryption Code}
\end{algorithm}

If the text is divided into many blocks, then this cryptographic
scheme would benefit from partially evaluated functions of the form
$\text{Encrypt}_{key}(block)$ and $\text{Decrypt}_{key}(block)$. It
does not matter if the encryption and decryption keys are the same.

\subsubsection{Cutting Through Abstraction Layers to Reduce Software Bloat}
Abstraction is typically a good practice to follow.
The benefit is that it reduces complexity because you can think of
each layer of a system as an abstract machine whose ``instruction
set'' is the layer's API.
However, a system constructed with multiple layers of abstraction
can be very slow because each operation first has to work its way
down the chain of abstract machines---with each higher-level abstract
machine calling down into one or more lower-level abstract machines.

For example, consider the Open Systems Interconnection (OSI) protocols
used in networking. There are seven layers of abstraction in this
model. For one application to communicate a message to another
application requires the message to pass through 14 layers: seven on
the way out for the sending application to send the message,
and seven on the way in for the receiving application to receive
the message. While the OSI model can handle
many different communication scenarios, a single application that
communicates with other instances of itself across the network will
probably only use only one or two of these scenarios.
Consequently, it might be possible to make a system more efficient if
the implementation of the OSI protocols were partially evaluated with
the application as static input.
The dynamic input would be the messages sent or received.

\subsection{A Drawback}
Partial evaluation can decrease the time need to execute a function,
but it does not always produce universally better code. Often the
savings in time is paid for by an increase in program size. Consider
the following piece of code:
\begin{algorithm}[ht]
 \begin{algtab}
  \algforto{$i=1$}{9}
   \algforto{$j=1$}{9}
    print f$(i,j)$\\
   \algend
  \algend
 \end{algtab}
 \caption{Draw Back Example}
\end{algorithm}

The function f has multiple parameters (two to be exact) and its first
parameter $i$ varies much slower than its second parameter $j$. Because
of this property, it would seem like this piece of code is a good candidate
for partially evaluation. The code that is produced by partial evaluation
can be seen in algorithm \algref{pe_dbe}.
\begin{algorithm}[ht]
 \begin{algtab}
  \algforto{$j=1$}{9}
   print $\text{f}_1(j)$\\
  \algend
  \algforto{$j=1$}{9}
   print $\text{f}_2(j)$\\
  \algend
  \algforto{$j=1$}{9}
   print $\text{f}_3(j)$\\
  \algend
  \algforto{$j=1$}{9}
   print $\text{f}_4(j)$\\
  \algend
  \algforto{$j=1$}{9}
   print $\text{f}_5(j)$\\
  \algend
  \algforto{$j=1$}{9}
   print $\text{f}_6(j)$\\
  \algend
  \algforto{$j=1$}{9}
   print $\text{f}_7(j)$\\
  \algend
  \algforto{$j=1$}{9}
   print $\text{f}_8(j)$\\
  \algend
  \algforto{$j=1$}{9}
   print $\text{f}_9(j)$\\
  \algend
 \end{algtab}
 \caption{Partially Evaluated Drawback Example}
 \label{Alg:pe_dbe}
\end{algorithm}

Not only did the body of the original piece of code get much longer,
but there are now nine specialized version of the function f. As one
might guess, the increased performance is not always justified by the
increase in code size.

Moreover, because of hardware caches, there can be a terrible effect
on performance from having a larger piece of code, and thus the
partially evaluated code can run slower than the original code.

\section{A Simple Flow-Chart Language}
We will start by learning about \emph{intra}procedural partial
evaluation---i.e., partial evaluation of single-procedure program.
After that, we will look at \emph{inter}procedural partial evaluation---i.e.,
partial evaluation across procedure boundaries.
For intraprocedural partial evaluation, we will work with an
imperative programming language---in particular, a simple while-loop language that
works on integers and lists.
For interprocedural partial evaluation, we will work with a simple
first-order functional programming language.

\begin{center}
\begin{tabular}{|l@{\hspace{1.5ex}}|@{\hspace{1.5ex}}l|}
  \hline
  Meta-language & Subject language \\
  \hline\hline
  \begin{tabular}{@{\hspace{0ex}}l@{\hspace{0ex}}}
    Pidgin-Algol \\
    tables of cases \\
    informal graph diagrams
  \end{tabular}
  &
  \begin{tabular}{@{\hspace{0ex}}l@{\hspace{0ex}}}
    flow-chart language $L$, with $L$ programs represented \\
    \qquad as $S$-expressions and expressions represented \\
    \qquad as abstract syntax trees \\
  \end{tabular} \\
  \hline
\end{tabular}
\end{center}

In language $L$ we have the following constructs:
\begin{center}
\begin{tabular}{|l|}
  \hline
  assignment \\
  if cond then goto label else label' \\
  goto label \\
  read of initial data \\
  print \\
  Syntactic sugar (as needed): \\
  \qquad\begin{tabular}{l}
    begin ... end \\
    while (...) do ... od \\
    repeat ... until (...)
  \end{tabular} \\
  \hline
\end{tabular}
\end{center}

\begin{center}
\begin{tabular}{|l@{\hspace{1.5ex}}|@{\hspace{1.5ex}}l|}
  \hline
  Data type & Operators \\
  \hline\hline
  Booleans & $\&$, $\mid$, $\sim$ \\
  integers & plus, $<$, $>$, $=$, $\ldots$ \\
  $S$-expressions & hd, tl, cons, nil, isNil \\
  \hline
\end{tabular}
\end{center}

For convenience, we will use the following algebraic datatype
(a.k.a.\ variant record) for representing expressions of $L$:

\begin{center}
\begin{tabular}{|rcl|}
  \hline
  exp & ::=        & ConstExpr(constant) \\
      & $\mid$     & IdentExpr(identifier) \\
      & $\mid$     & Compound(operator exp exp) \\
  constant   & ::= & true, false, $\ldots, -2, -1, 0, 1, 2, \ldots$ \\
  identifier & ::= & [a-zA-Z]+ \\
  operator   & ::= & + $\mid$ $-$ $\mid$ * $\mid$ cons $\mid$ $\ldots$ \\
  \hline
\end{tabular}
\end{center}

The partial evaluator uses the following subroutine for simplifying
$L$ expressions:

\begin{center}
\begin{tabular}{|l|}
\hline
simplify(e, store) =     \qquad// store is a map from names to values \\
\TAB cases e of \\
\TAB\TAB ConstExpr(*): e,   \qquad// nothing to simplify \\
\TAB\TAB IdentExpr(i): definedIn(i, store) ? ConstExpr(lookup(i, store)) : e, \\
\TAB\TAB Compound(op, e1, e2): \\
\TAB\TAB\TAB let v1 = simplify(e1, store) and v2 = simplify(e2, store) in \\
\TAB\TAB\TAB\TAB cases v1 of \\
\TAB\TAB\TAB\TAB\TAB ConstExpr(c1): \\
\TAB\TAB\TAB\TAB\TAB\TAB cases v2 of \\
\TAB\TAB\TAB\TAB\TAB\TAB\TAB ConstExpr(c2): ConstExpr(funcOf(op)(c1,c2)),  \qquad// evaluate \\
\TAB\TAB\TAB\TAB\TAB\TAB\TAB default: Compound(op, v1, v2)  \qquad// residuate simplified expression \\
\TAB\TAB\TAB\TAB\TAB default: Compound(op, v1, v2)  \qquad// residuate simplified expression \\
\hline
\end{tabular}
\end{center}


\bibliography{cs701}
\bibliographystyle{plain}

\end{document}
