Jeremy Siek 3 tahun lalu
induk
melakukan
74f943929f
1 mengubah file dengan 79 tambahan dan 37 penghapusan
  1. 79 37
      book.tex

+ 79 - 37
book.tex

@@ -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{}};