|
@@ -14496,14 +14496,15 @@ variable occurrences, we review the standard notion of free variable.
|
|
|
|
|
|
\begin{definition}
|
|
|
A variable is \textbf{free in expression} $e$ if the variable occurs
|
|
|
-inside $e$ but does not have an enclosing binding that is also in
|
|
|
+inside $e$ but does not have an enclosing definition that is also in
|
|
|
$e$.\index{subject}{free variable}
|
|
|
\end{definition}
|
|
|
|
|
|
-For example, in the expression \code{(+ x (+ y z))} the variables
|
|
|
-\code{x}, \code{y}, and \code{z} are all free. On the other hand,
|
|
|
+For example, in the expression
|
|
|
+\racket{\code{(+ x (+ y z))}}\python{\code{x + y + z}}
|
|
|
+the variables \code{x}, \code{y}, and \code{z} are all free. On the other hand,
|
|
|
only \code{x} and \code{y} are free in the following expression
|
|
|
-because \code{z} is bound by the \code{lambda}.
|
|
|
+because \code{z} is defined by the \code{lambda}.
|
|
|
{\if\edition\racketEd
|
|
|
\begin{lstlisting}
|
|
|
(lambda: ([z : Integer]) : Integer
|
|
@@ -14521,14 +14522,13 @@ special treatment. We need to transport, at runtime, the values of
|
|
|
those variables from the point where the \code{lambda} was created to
|
|
|
the point where the \code{lambda} is applied. An efficient solution to
|
|
|
the problem, due to \citet{Cardelli:1983aa}, is to bundle the values
|
|
|
-of the free variables together with the function pointer for the
|
|
|
-lambda's code into a tuple, an arrangement called a \emph{flat
|
|
|
- closure} (which we shorten to just ``closure'').
|
|
|
-\index{subject}{closure}\index{subject}{flat closure} Fortunately, we
|
|
|
-have all the ingredients to make closures: Chapter~\ref{ch:Lvec} gave
|
|
|
-us tuples and Chapter~\ref{ch:Rfun} gave us function pointers. The
|
|
|
-function pointer resides at index $0$ and the values for the free
|
|
|
-variables fill in the rest of the tuple.
|
|
|
+of the free variables together with a function pointer into a tuple,
|
|
|
+an arrangement called a \emph{flat closure} (which we shorten to just
|
|
|
+``closure''). \index{subject}{closure}\index{subject}{flat closure}
|
|
|
+Fortunately, we have all the ingredients to make closures:
|
|
|
+Chapter~\ref{ch:Lvec} gave us tuples and Chapter~\ref{ch:Rfun} gave us
|
|
|
+function pointers. The function pointer resides at index $0$ and the
|
|
|
+values for the free variables fill in the rest of the tuple.
|
|
|
|
|
|
Let us revisit the example in Figure~\ref{fig:lexical-scoping} to see
|
|
|
how closures work. It's a three-step dance. The program calls function
|
|
@@ -14746,15 +14746,8 @@ class InterpLlambda(InterpLfun):
|
|
|
|
|
|
def interp_exp(self, e, env):
|
|
|
match e:
|
|
|
- case FunRefArity(id, arity):
|
|
|
- return env[id]
|
|
|
case Lambda(params, body):
|
|
|
return Function('lambda', params, [Return(body)], env)
|
|
|
- case Closure(arity, args):
|
|
|
- return tuple([self.interp_exp(arg, env) for arg in args])
|
|
|
- case AllocateClosure(length, typ, arity):
|
|
|
- array = [None] * length
|
|
|
- return array
|
|
|
case _:
|
|
|
return super().interp_exp(e, env)
|
|
|
|
|
@@ -15078,7 +15071,47 @@ to address this challenge.
|
|
|
\section{Uniquify Variables}
|
|
|
\label{sec:uniquify-lambda}
|
|
|
|
|
|
-UNDER CONSTRUCTION
|
|
|
+With the addition of \code{lambda} we have a complication to deal
|
|
|
+with: name shadowing. Consider the following program with a function
|
|
|
+\code{f} that has a parameter \code{x}. Inside \code{f} there are two
|
|
|
+\code{lambda} expressions. The first \code{lambda} has a parameter
|
|
|
+that is also named \code{x}.
|
|
|
+
|
|
|
+\begin{lstlisting}
|
|
|
+def f(x:int, y:int) -> Callable[[int], int]:
|
|
|
+ g : Callable[[int],int] = (lambda x: x + y)
|
|
|
+ h : Callable[[int],int] = (lambda y: x + y)
|
|
|
+ x = input_int()
|
|
|
+ return g
|
|
|
+
|
|
|
+print(f(0, 10)(32))
|
|
|
+\end{lstlisting}
|
|
|
+
|
|
|
+Many of our compiler passes rely on being able to connect variable
|
|
|
+uses with their definitions using just the name of the variable,
|
|
|
+including new passes in this chapter. However, in the above example
|
|
|
+the name of the variable does not uniquely determine its
|
|
|
+definition. To solve this problem we recommend implementing a pass
|
|
|
+named \code{uniquify} that renames every variable in the program to
|
|
|
+make sure they are all unique.
|
|
|
+
|
|
|
+The following shows the result of \code{uniquify} for the above
|
|
|
+example. The \code{x} parameter of \code{f} is renamed to \code{x\_0}
|
|
|
+and the \code{x} parameter of the \code{lambda} is renamed to
|
|
|
+\code{x\_4}.
|
|
|
+
|
|
|
+\begin{lstlisting}
|
|
|
+def f(x_0:int, y_1:int) -> Callable[[int], int] :
|
|
|
+ g_2 : Callable[[int], int] = (lambda x_4: x_4 + y_1)
|
|
|
+ h_3 : Callable[[int], int] = (lambda y_5: x_0 + y_5)
|
|
|
+ x_0 = input_int()
|
|
|
+ return g_2
|
|
|
+
|
|
|
+def main() -> int :
|
|
|
+ print(f(0, 10)(32))
|
|
|
+ return 0
|
|
|
+\end{lstlisting}
|
|
|
+
|
|
|
|
|
|
\fi
|
|
|
|
|
@@ -15360,13 +15393,19 @@ AST. The interesting cases are the ones for \key{lambda} and function
|
|
|
application. We transform a \key{lambda} expression into an expression
|
|
|
that creates a closure, that is, a tuple whose first element is a
|
|
|
function pointer and the rest of the elements are the values of the
|
|
|
-free variables of the \key{lambda}. However, we use the \code{Closure}
|
|
|
-AST node instead of using a tuple so that we can record the arity and
|
|
|
+free variables of the \key{lambda}.
|
|
|
+%
|
|
|
+\racket{However, we use the \code{Closure}
|
|
|
+ AST node instead of using a tuple so that we can record the arity
|
|
|
+ which is needed for \code{procedure-arity} and
|
|
|
to distinguish closures from tuples in
|
|
|
-Section~\ref{sec:optimize-closures}. In the generated code below,
|
|
|
-\itm{fvs} is the free variables of the lambda, \itm{name} is a unique
|
|
|
-symbol generated to identify the lambda and the \itm{arity} is the
|
|
|
-number of parameters (the length of \itm{ps}).
|
|
|
+Section~\ref{sec:optimize-closures}.}
|
|
|
+%
|
|
|
+In the generated code below, \itm{fvs} is the free variables of the
|
|
|
+lambda and \itm{name} is a unique symbol generated to identify the lambda.
|
|
|
+%
|
|
|
+\racket{The \itm{arity} is the number of parameters (the length of
|
|
|
+ \itm{ps}).}
|
|
|
%
|
|
|
{\if\edition\racketEd
|
|
|
\begin{lstlisting}
|
|
@@ -15380,12 +15419,12 @@ number of parameters (the length of \itm{ps}).
|
|
|
\begin{lstlisting}
|
|
|
Lambda(|\itm{ps}|, |\itm{body}|)
|
|
|
|$\Rightarrow$|
|
|
|
-Closure(|\itm{arity}|, [FunRef(|\itm{name}|), |\itm{fvs}, \ldots|])
|
|
|
+Tuple([FunRef(|\itm{name}|), |\itm{fvs}, \ldots|])
|
|
|
\end{lstlisting}
|
|
|
\fi}
|
|
|
%
|
|
|
In addition to transforming each \key{Lambda} AST node into a
|
|
|
-\key{Closure}, we create a top-level function definition for each
|
|
|
+tuple, we create a top-level function definition for each
|
|
|
\key{Lambda}, as shown below.\\
|
|
|
\begin{minipage}{0.8\textwidth}
|
|
|
{\if\edition\racketEd
|
|
@@ -15414,7 +15453,7 @@ the next paragraph, to obtain \itm{ps'} and \itm{rt'}. The type
|
|
|
\python{\code{Bottom()}}\racket{\code{\_} (the dummy type)} and the rest of
|
|
|
the element types are the types of the free variables in the
|
|
|
lambda. We use \python{\code{Bottom()}}\racket{\code{\_}} because it
|
|
|
-is difficult to give a type to the function in the closure's type.%
|
|
|
+is non-trivial to give a type to the function in the closure's type.%
|
|
|
%
|
|
|
\footnote{To give an accurate type to a closure, we would need to add
|
|
|
existential types to the type checker~\citep{Minamide:1996ys}.}
|
|
@@ -15456,7 +15495,8 @@ We transform function application into code that retrieves the
|
|
|
function from the closure and then calls the function, passing in the
|
|
|
closure as the first argument. We place $e'$ in a temporary variable
|
|
|
to avoid code duplication.
|
|
|
-%
|
|
|
+\begin{center}
|
|
|
+\begin{minipage}{\textwidth}
|
|
|
{\if\edition\racketEd
|
|
|
\begin{lstlisting}
|
|
|
(Apply |$e$| |$\itm{es}$|)
|
|
@@ -15475,6 +15515,8 @@ Let(|$\itm{tmp}$|, |$e'$|,
|
|
|
[|$\itm{tmp}$|, |$e'_1, \ldots, e'_n$|]))
|
|
|
\end{lstlisting}
|
|
|
\fi}
|
|
|
+\end{minipage}
|
|
|
+\end{center}
|
|
|
|
|
|
There is also the question of what to do with references to top-level
|
|
|
function definitions. To maintain a uniform translation of function
|
|
@@ -15504,7 +15546,7 @@ $\Rightarrow$
|
|
|
\fi}
|
|
|
{\if\edition\pythonEd
|
|
|
\begin{lstlisting}
|
|
|
-Closure(|$n$|, [FunRef(|$f$|)])
|
|
|
+Tuple([FunRef(|$f$|)])
|
|
|
\end{lstlisting}
|
|
|
\fi}
|
|
|
\end{minipage}
|
|
@@ -15578,11 +15620,11 @@ def lambda_0(fvs_1:tuple[bot,int,tuple[int]],z:int) -> int:
|
|
|
def f(fvs_2:bot, x:int) -> tuple[Callable[[tuple[],int], int]]
|
|
|
y = (777,)
|
|
|
y[0] = 4
|
|
|
- return closure(lambda_0,y,x)
|
|
|
+ return (lambda_0, y, x)
|
|
|
|
|
|
def main() -> int:
|
|
|
- g = (let clos_3 = closure(f) in clos_3[0](clos_3, 5))
|
|
|
- h = (let clos_4 = closure(f) in clos_4[0](clos_4, 3))
|
|
|
+ g = (let clos_3 = (f,) in clos_3[0](clos_3, 5))
|
|
|
+ h = (let clos_4 = (f,) in clos_4[0](clos_4, 3))
|
|
|
print((let clos_5 = g in clos_5[0](clos_5, 11))
|
|
|
+ (let clos_6 = h in clos_6[0](clos_6, 15)))
|
|
|
return 0
|
|
@@ -15600,6 +15642,7 @@ lexical scoping. Test your compiler on these new programs and all of
|
|
|
your previously created test programs.
|
|
|
\end{exercise}
|
|
|
|
|
|
+\if\edition\racketEd
|
|
|
|
|
|
\section{Expose Allocation}
|
|
|
\label{sec:expose-allocation-r5}
|
|
@@ -15643,9 +15686,6 @@ operators.
|
|
|
\end{array}
|
|
|
\]
|
|
|
\fi}
|
|
|
-{\if\edition\pythonEd
|
|
|
-UNDER CONSTRUCTION
|
|
|
-\fi}
|
|
|
\end{minipage}
|
|
|
}
|
|
|
\caption{The abstract syntax of \LangCLam{}, extending \LangCFun{} (Figure~\ref{fig:c3-syntax}).}
|
|
@@ -15669,6 +15709,8 @@ Compile the \code{procedure-arity} operator into a sequence of
|
|
|
instructions that access the tag from position $0$ of the vector and
|
|
|
extract the $5$-bits starting at position $58$ from the tag.
|
|
|
|
|
|
+\fi
|
|
|
+
|
|
|
\begin{figure}[p]
|
|
|
\begin{tikzpicture}[baseline=(current bounding box.center)]
|
|
|
\node (Rfun) at (0,2) {\large \LangLam{}};
|