User:Thepigdog/Lambda lifting

From Wikipedia, the free encyclopedia
Jump to: navigation, search

Lambda lifting or closure conversion is the process of eliminating free variables from a lambda expression within a larger lambda expression. The elimination of free variables allows expression to be moved (lifted) out of its local context to become an outer level definition of the expression.

Lambda lifting allows a lambda expression to be converted to a collection of functions defined without lambda expressions. A strict implementation of the functions may be provided using the usual stack based approach used in implementing imperative languages such as C++. However a strict implementation may not be that useful, and it is computationally expensive to do the conversion.

Lambda lifting demonstrates the equivalence of lambda expressions and functions. The eta-redex is the basis for the equivalence of lambda expressions and functions. However it may not be applied directly. Lambda lifting performs the restructuring needed to allow the eta-redex to be applied.

The term "lambda lifting" was first introduced by Thomas Johnsson around 1982.

The reverse operation is called Lambda dropping.[1]

Lambda Lifting[edit]

This section describes lambda lifting in detail. Lambda lifting may be used to convert a program written in Lambda Calculus into a functional program, without lambdas. This demonstrates the equivalence of programs written in Lambda Calculus and programs written as functions.[2]

Each Lambda Lift takes a lambda abstraction which is a sub expression of a lambda expression and replaces it by a function call (application) to a function that it creates. The free variables in the sub expression are the parameters to the function call. The process is similar to refactoring out code and putting it in a function. Lambda Lifts may be repeated until the expression has no lambda abstractions.

The purpose of lambda lifting[edit]

Lambda lifting converts a program with lambda expressions into a program with function calls only. Function calls may be implemented using a stack based implementation, which is simple and efficient.

However a stack based implementation must be strict. A functional language is normally implemented using a lazy strategy, which delays calculation until the value is needed. The lazy implementation strategy gives flexibility to the programmer.

Also Lamba lifting is expensive on processing time for the compiler.

Lambda lifting gives a direct translation of a Lambda calculus program into a functional program. At first site this would indicate the soundness of Lambda calculus as a deductive system. However this is not the case as the eta reduction used in Lambda lifting is the step that introduces cardinality problems into the Lambda calculus, because it removes the value from the variable, without first checking that there is only one value that satisfies the conditions on the variable.

Lambda lifting is useful in understanding lambda expressions.

Lamba-Lift transform of a lambda expression to functions[edit]

This section describes a meta-function called lambda-lift-tran that transforms any lambda expression into a set of functions defined without lambda abstractions. For example,

 \operatorname{lambda-lift-tran}[\lambda f.(\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x))] \equiv \operatorname{let} p\ f\ x = f\ (x\ x) \and q\ p\ f = (p\ f)\ (p\ f) \operatorname{in} q\ p

The algorithm described here regards every abstraction as an anonymous function. This transformation names the anonymous function and lifts. Alternative approaches are possible. A lambda abstractions applied to an expression may be regarded as a local function function definition.

A meta-function is a function that takes a program as a parameter. The program is data for the meta-program. The program and the meta program are at different meta-levels.

The following conventions will be used to distinguish program from the meta program,

  • Square brackets [] will be used to represent function application in the meta program.
  • Capital letters will be used for variables in the meta program. Lower case letters represent variables in the program.
  •  \equiv will be used for equals in the meta program.

Calling lambda-lift-tran on a lambda expression gives you a functional program in the form,

  • let M in N

where M is a series of function definitions, and N is the expression representing the value returned.

The processing of transforming the lambda expression is a series of lifts. Each lift has,

  1. A sub expression chosen for it by the function candidate-prefix-lambda. The sub expression should be chosen so that it may be converted into an equation with no lambdas.
  2. The lift is performed by a call to the lambda-lift meta function, described in the next section,
  •  \operatorname{lambda-lift-tran}[L] = \operatorname{lambda-process}[\operatorname{let} \operatorname{true} \operatorname{in} L]]
  •  \text{if } \operatorname{candidate-prefix-lambda}[S, L] \text{ then }
\operatorname{lambda-process}[L] = \operatorname{lambda-process}[\operatorname{lambda-lift}[S, L]]
 \text{ else }
 \operatorname{lambda-process}[L] = L

Rules for Lambda lifting[edit]

The lambda-lift meta function performs a single lambda lift, which takes an expression and replaces it with a function call.

The lift is given a sub-expression (called S) to be lifted. For S;

  • Create a name for the function that will replace S (called H). Make sure that the name identified by H has not been used.
  • Add parameters to H, for all the free variables in S, to create an expression G (see make-call).

The lambda lift is the substitution of an expression for function application, along with the addition of a definition for the function.

\operatorname{lambda-lift}[S, \operatorname{let} F \operatorname{in} L] \equiv \operatorname{let} F \and \operatorname{de-lambda}[G = S] \operatorname{in} L[S:=G]

The new lambda expression has S substituted for G. Note that L[S:=G] means substitution of S for G in L. The function definitions has the function definition G = S added.

In the above rule G is the function application that is substituted for the expression S. It is defined by,

G = \operatorname{make-call}[H, \operatorname{FV}[S]]

where H is the function name. It must be a new variable, i.e. a name not already used in the lambda expression,

H \not \in \operatorname{vars}[\operatorname{let} F \operatorname{in} L]

where \operatorname{vars}[E] is a meta function that returns the set of variables used in E.

For example,

F = true
L = \lambda f.(\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x))
S = \lambda x.f\ (x\ x)
G = p\ f
 \operatorname{de-lambda}[p\ f = \lambda x.f\ (x\ x)] \equiv p\ f\ x = f \ (x\ x)

gives,

\operatorname{lambda-lift}[\lambda x.f\ (x\ x), \operatorname{let} true \operatorname{in} \lambda f.(\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x))] \equiv \operatorname{let} p\ f\ x = f\ (x\ x) \operatorname{in} \lambda f.(p\ f)\ (p\ f)

Constructing the call[edit]

The function call G is constructed by adding parameters for each variable in the free variable set (represented by V), to the function H,

  • X \in V \to \operatorname{make-call}[H, V] \equiv \operatorname{make-call}[H, V \cap \neg \{X\}]\ X
  • \operatorname{make-call}[H, \{\}] \equiv H

For example,

 S = \lambda x.f\ (x\ x)
 \operatorname{FV}(S) = \{f\}
 G \equiv \operatorname{make-call}[p, \operatorname{FV}[S]] \equiv \operatorname{make-call}[p, \{f\}] \equiv \operatorname{make-call}[p, \{\}]\ f \equiv p\ f

Eta reductions to remove lambda abstractions in lifted expressions[edit]

An eta reduction is ,

 \operatorname{eta-redex}[\lambda x.(f\ x)] = f

If you assume that the eta-redex of an expression equals the expression, this can be rearranged as,

f\ x = y \iff f = \lambda x.y

The de-lambda meta function applies eta-reductions to remove lambda abstractions. It is defined by,

 \operatorname{de-lambda}[F = \lambda X.Y] \equiv \operatorname{de-lambda}[F\ X = Y]
 \operatorname{de-lambda}[K = E] \equiv K = E if E is not a lamba abstraction

For example,

 \operatorname{de-lambda}[p\ f = \lambda x.f\ (x\ x)] \equiv \operatorname{de-lambda}[p\ f\ x = f \ (x\ x)] \equiv p\ f\ x = f \ (x\ x)

Candidate prefix lambda for lifting[edit]

The candidate-prefix-lambda is a meta function that returns a lambda abstraction within an expression whose body does not contain any lambda abstractions. The lambda abstraction may have a number of parameters, as they can be removed by the de-lambda meta function defined previously.

The rules are described below,

  •  \operatorname{candidate-prefix-lambda}[S, L] = \operatorname{lambda-prefix}[S, L, \_]
  •  \operatorname{simple-exp}[X] \and  \operatorname{simple-exp}[Y] \to \operatorname{simple-exp}[X Y]
  •  \operatorname{variable}[X] \to \operatorname{simple-exp}[X Y]
  •  \text{if } \operatorname{lambda-prefix}[S, y, r] \equiv r \text{ then } \operatorname{lambda-prefix}[\lambda x.y, \lambda x.y, true] \text{ else } \operatorname{lambda-prefix}[S, \lambda x.y, false]
  •  \text{if } \operatorname{lambda-prefix}[S, x, r] \text{ then } \operatorname{lambda-prefix}[S, x\ y, false] \text{ else } (\operatorname{lambda-prefix}[T, y, r] \iff \operatorname{lambda-prefix}[T, x\ y, false])
  •  \neg \operatorname{lambda-prefix}[\_, x, false] where x is a variable.

Example[edit]

For example the Y combinator,

\lambda f.(\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x))
Lambda Expression Function From To Variables
1 \lambda f.(\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x)) true \lambda x.f\ (x\ x) p\ f \{x, f\}
2 (\lambda f.(\lambda x.f\ (x\ x)) (\lambda x.f\ (x\ x)))[\lambda x.f\ (x\ x) := p\ f] p\ f = \lambda x.f\ (x\ x) \{x, f, p\}
3 \lambda f.(p\ f)\ (p\ f) p\ f\ x = f\ (x\ x) \lambda f.(p\ f)\ (p\ f) q\ p \{x, f, p\}
4 \lambda f.(p\ f)\ (p\ f)[\lambda f.(p\ f)\ (p\ f) := q\ p] p\ f\ x = f\ (x\ x) \and q\ p = \lambda f.(p\ f)\ (p\ f) \{x, f, p, q\}
5 q\ p p\ f\ x = f\ (x\ x) \and q\ p\ f = (p\ f)\ (p\ f) \{x, f, p, q\}

The first sub expression to be chosen for lifting is \lambda x.f\ (x\ x) . This transforms the lambda expression into \lambda f.(p\ f)\ (p\ f) and creates the equation p\ f\ x = f (x\ x) .

The second sub expression to be chosen for lifting is \lambda f.(p\ f)\ (p\ f). This transforms the lambda expression into q\ p and creates the equation q\ p\ f = (p\ f)\ (p\ f) .

And the result is,

\operatorname{let} p\ f\ x = f\ (x\ x) \and q\ p\ f = (p\ f)\ (p\ f) \operatorname{in} q\ p\

Because p is a global function, the result could also have been written,

\operatorname{let} p\ f\ x = f\ (x\ x) \and q\ f = (p\ f)\ (p\ f) \operatorname{in} q\

This would be generated if there was a change to make-call so that only free variables that are not global are added as parameters.

Execution[edit]

Apply function to,

K
\lambda f.(\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x))\ K \operatorname{let} p\ f\ x = f\ (x\ x) \and q\ p\ f = (p\ f)\ (p\ f) \operatorname{in} q\ p\ \ K
(\lambda x.K\ (x\ x))\ (\lambda x.K\ (x\ x)) \operatorname{let} p\ f\ x = f\ (x\ x) \and q\ p\ f = (p\ f)\ (p\ f) \operatorname{in} p\ K\ (p\ K)
K\ ((\lambda x.K\ (x\ x))\ (\lambda x.K\ (x\ x))))\ \operatorname{let} p\ f\ x = f\ (x\ x) \and q\ p\ f = p\ f\ (p\ f) \operatorname{in} K\ (p\ K\ (p\ K))

So,

  • (\lambda x.K\ (x\ x))\ (\lambda x.K\ (x\ x)) = K\ ((\lambda x.K\ (x\ x))\ (\lambda x.K\ (x\ x))))\

or

  •  p\ K\ (p\ K) = K\ (p\ K\ (p\ K))

The Y-Combinator calls its parameter (function) repeatedly on itself. The value is defined if the function has a fixed point. But the function will never terminate.

Lambda dropping[edit]

Lambda dropping is making the scope of functions smaller and using the context from the reduced scope to reduce the number of variables. Reducing the number of variables makes functions easier to comprehend.

Lambda dropping also may make the compilation of programs quicker for the compiler, and may also increase the efficiency of the resulting program, by reducing the number of parameters, and reducing the size of stack frames.

However Lambda dropping also makes a function harder to re-use. A dropped function is tied to its context, and can only be used in a different context if it is first lifted.

In the Lambda Lifting section, a meta function for first lifting and then converting the resulting lambda expression into recursive equation was described. The Lamda Drop meta function performs the reverse by first converting recursive equations to lambda abstractions, and then dropping the resulting lambda expression, into the smallest scope which covers all references to the lambda abstraction.

Rules used in Lambda dropping[edit]

A let expression may be converted back into a lambda expression by repeated application of the lambda-param and the lambda-equiv law,

Name Law
lambda-param f\ x = y \equiv f = \lambda x.y
lambda-equiv f \not \in FV(F) \to (\operatorname{let} f = e \and F \operatorname{in} L \equiv \operatorname{let} F \operatorname{in} \lambda f.L\ e) (where f is a variable name).

The result has the lift restructure, but the restructure is within the Lambda Calculus. The lambda-drop-tran applies these rules in the conversion of recursive functions to lambda abstractions and in reducing the scope of the resulting lambda abstraction.

Converting equations to lambda expressions[edit]

This meta-function lambda-abstract-tran transforms an expression defined using functions with a let expression to a lambda expression. The transformation works when the let condition is structured like a series of functions, but does not work for arbitrary expressions.

The lambda-lift-tran meta function described earlier creates "let" expressions where each function definition has no free variables. lambda-abstract needs functions with no free variables to work correctly.

For example,

 \operatorname{lambda-abstract-tran}[\operatorname{let} p\ f\ x = f\ (x\ x) \and q\ p\ f = (p\ f)\ (p\ f) \operatorname{in} q\ p] \equiv (\lambda q.(\lambda p. q\ p)\ (\lambda f.\lambda x.f\ (x\ x)))\ (\lambda p.\lambda f.(p\ f)\ (p\ f))

lambda-abstract-tran is defined as,

  •  \operatorname{lambda-abstract-tran}[E] = \operatorname{lambda-convert}[\operatorname{lambda-process}[\operatorname{lambda-preprocess}[E]]]

Lambda abstract performs three separate processing steps; pre-process, process, and convert. These steps are described below,

Pre-process - Split into separate lets,

  •  \operatorname{lambda-preprocess}[\operatorname{let} A \and B \operatorname{in} E] = \operatorname{let} A \operatorname{in} \operatorname{lambda-preprocess}[A, \operatorname{let} B \operatorname{in} E]
  •  \operatorname{lambda-preprocess}[\operatorname{let} A \operatorname{in} E] = \operatorname{let} A \operatorname{in} E

After lambda-preprocess the result for the example is,,

 \operatorname{let} p\ f\ x = f\ (x\ x) \operatorname{in} \operatorname{let}  q\ p\ f = (p\ f)\ (p\ f) \operatorname{in} q\ p

Process - Turn functions into lambdas,

  •  \operatorname{lambda-process}[\operatorname{let} true \operatorname{in} E] = \operatorname{lambda-process}[E]
  •  \operatorname{lambda-process}[\operatorname{let} G = S \operatorname{in} E] = \operatorname{let} \operatorname{abstract}[G = S] \operatorname{in} \operatorname{lambda-process}[E]
  •  \operatorname{lambda-process}[E] = E - E is not a let expression.

After lambda-process,

 \operatorname{let} p = \lambda f. \lambda x.f\ (x\ x) \operatorname{in} \operatorname{let}  q = \lambda p. \lambda f.(p\ f)\ (p\ f) \operatorname{in} q\ p

Convert - Turn lets into lambdas,

  •  \operatorname{lambda-convert}[\operatorname{let} F = S \operatorname{in} E] = (\lambda F.E)\ (\operatorname{lambda-convert}[S])
  •  \operatorname{lambda-convert}[E] = E - E is not a let expression.

Then after lambda-abstract-tran,

 (\lambda p.(\lambda q.q\ p)\ (\lambda p. \lambda f.(p\ f)\ (p\ f)))\ (\lambda f. \lambda x.f\ (x\ x))

The abstract meta function removes parameters from functions using lambda abstractions. It is defined by,

 \operatorname{abstract}[F X = Y] \equiv \operatorname{abstract}[F = \lambda X.Y]
 \operatorname{abstract}[K = E] \equiv K = E if K is a variable.

For example,

 \operatorname{abstract}[p\ f\ x = f \ (x\ x)] \equiv \operatorname{abstract}[p\ f = \lambda x.f\ (x\ x)] \equiv \operatorname{abstract}[p = \lambda f.\lambda x.f\ (x\ x)] \equiv \lambda f.\lambda x.f\ (x\ x)

Abstraction Sinking[edit]

Lambda-drop-tran converts recursive equations to lambda expression, and then drops the resulting abstractions into the smallest scope necessary to include all references to the abstraction.

 \operatorname{lambda-drop-tran}[L] = \operatorname{drop-params-tran}[\operatorname{sink-tran}[\operatorname{lambda-abstract-tran}[L]]]

sink-tran sinks each abstraction, starting from the innermost,

 \operatorname{sink-tran}[(\lambda X.B)\ Y] = \operatorname{sink}[(\lambda X.\operatorname{sink-tran}[B])\ \operatorname{sink-tran}[Y]]]
 \operatorname{sink-tran}[\lambda X.B] = \lambda X.\operatorname{sink-tran}[B]
 \operatorname{sink-tran}[M\ N] = \operatorname{sink-tran}[M]\ \operatorname{sink-tran}[M]
 \operatorname{sink-tran}[V] = V

Sinking is moving a lambda abstraction inwards as far as possible such that it is still outside all references to the variable.

Application - 4 cases.

X \not \in \operatorname{FV}[G] \and X \not \in \operatorname{FV}[H] \to \operatorname{sink}[(\lambda X.G\ H)\ Y] = G\ H
X \not \in \operatorname{FV}[G] \and X \in \operatorname{FV}[H] \to \operatorname{sink}[(\lambda X.G\ H)\ Y] = G\ \operatorname{sink}[(\lambda X.H)\ Y]
X \in \operatorname{FV}[G] \and X \not \in \operatorname{FV}[H] \to \operatorname{sink}[(\lambda X.G\ H)\ Y] = (\operatorname{sink}[(\lambda X.G)\ Y])\ H
X \in \operatorname{FV}[G] \and X \in \operatorname{FV}[H] \to \operatorname{sink}[(\lambda X.G\ H)\ Y] = (\lambda X.G\ H)\ Y

Abstraction. Use canonical renaming to insure that the variable names are all distinct.

X \ne V \to \operatorname{sink}[(\lambda X.\lambda V.W)\ Y] = \lambda V.\operatorname{sink}[(\lambda X.W)\ Y]

Variable - 2 cases.

X \ne V \to \operatorname{sink}[(\lambda X.V)\ Y] = V
X = V \to \operatorname{sink}[(\lambda X.V)\ Y] = Y

For the example above, sinking,

 \operatorname{sink-tran}[\lambda p.(\lambda q.q\ p)\ (\lambda p. \lambda f.(p\ f)\ (p\ f))\ (\lambda f.\lambda x.f\ (x\ x))]
 = \operatorname{sink}[(\lambda p.\operatorname{sink}[(\lambda q.q\ p)\ (\lambda p. \lambda f.(p\ f)\ (p\ f))])\ (\lambda f.\lambda x.f\ (x\ x))]
 = \operatorname{sink}[(\lambda p.\operatorname{sink}[(\lambda q.q)\ (\lambda p. \lambda f.(p\ f)\ (p\ f)))\ p])\ (\lambda f.\lambda x.f\ (x\ x))]
 = \operatorname{sink}[(\lambda p.(\lambda p.\lambda f.(p\ f)\ (p\ f)))\ p)\ (\lambda f.\lambda x.f\ (x\ x))]
 = (\lambda p.\lambda f.(p\ f)\ (p\ f)))\ \operatorname{sink}[(\lambda p.p)\ (\lambda f.\lambda x.f\ (x\ x))]
 = (\lambda p.\lambda f.(p\ f)\ (p\ f))\ (\lambda f.\lambda x.f\ (x\ x))

Sinking a second time,

 \operatorname{sink-tran}[(\lambda p.\lambda f.(p\ f)\ (p\ f))\ (\lambda f.\lambda x.f\ (x\ x))]
 = \operatorname{sink}[(\lambda p.\lambda f.(p\ f)\ (p\ f))\ (\lambda f.\lambda x.f\ (x\ x))]
 = \lambda f.\operatorname{sink}[(\lambda p.(p\ f)\ (p\ f))\ (\lambda f.\lambda x.f\ (x\ x))]
 = \lambda f.(\lambda p.(p\ f)\ (p\ f))\ (\lambda f.\lambda x.f\ (x\ x))

Parameter Dropping[edit]

Parameter dropping is optimizing a function for its position in the function. Lambda lifting added parameters that were necessary so that a function can be moved out of its context. In dropping, this process is reversed, and extra parameters that contain variables that are free may be removed.

Dropping a parameter is removing an unnecessary parameter from a function, where the actual parameter being passed in is always the same expression. The free variables of the expression must also be free where the function is defined. In this case the parameter that is dropped is replaced by the expression in the body of the function definition. This makes the parameter unnecessary.

For example, consider,

 (\lambda g.k\ (\lambda n.(g\ m\ p\ n)\ (g\ q\ p\ n))\ (f\ x))\ \lambda x.\lambda o.\lambda y.o\ x\ y

In this example the actual parameter for the formal parameter o is always p. As p is a free variable in the whole expression, the parameter may be dropped. The actual parameter for the formal parameter y is always n. However n is bound in a lambda abstraction. So this parameter may not be dropped.

The result of dropping the parameter is,

 \operatorname{drop-params-tran}[(\lambda g.k\ (\lambda n.(g\ m\ p\ n)\ (g\ q\ p\ n))\ (f\ x))\ \lambda x.\lambda o.\lambda y.o\ x\ y]
 \equiv (\lambda g.k\ (\lambda n.(g\ m\ n)\ (g\ q\ n))\ (f\ x))\ \lambda x.\lambda y.p\ x\ y

For the main example,

 \operatorname{drop-params-tran}[ \lambda f.(\lambda p.(p\ f)\ (p\ f))\ (\lambda f.\lambda x.f\ (x\ x))]
 \equiv \lambda f.(\lambda p.p\ p)\ (\lambda x.f\ (x\ x))

The definition of drop-params-tran is,

\operatorname{drop-params-tran}[L] \equiv (\operatorname{drop-params}[L, D, FV[L], []])

where,

 \operatorname{build-drop-list}[L, D, \_]

Build parameter lists[edit]

For each abstraction that defines a function, build the information required to make decisions on dropping names. This information consists of information about each parameter; the parameter name, the expression for the actual value, and an indication that all the expressions have the same value.

For example in,

 (\lambda g.k\ (\lambda n.(g\ m\ p\ n)\ (g\ q\ p\ n))\ (f\ x))\ \lambda x.\lambda o.\lambda y.o\ x\ y

the parameters to the function g are,

Formal Parameter All Same Value Actual parameter expression
x false _
o true p
y true n

Each abstraction is renamed with a unique name, and the parameter list is associated with the name of the abstraction. For example, g there is parameter list.

 D[g] = [[x, \operatorname{false}, \_], [o, \operatorname{true}, p], [y, \operatorname{true}, n]]

build-param-lists builds all the lists for an expression, by traversing the expression.

Abstraction - A lambda expression of the form  (\lambda N.S)\ L is analyzed to extract the names of parameters for the function.

 \operatorname{build-param-lists}[(\lambda N.S)\ L, D, R] \equiv \operatorname{build-param-lists}[B, D, R] \and \operatorname{build-list}[L, D[N]]

Locate the name and start building the parameter list for the name, filling in the formal parameter names. Also receive any actual parameter list from the body of the expression, and return it as the actual parameter list from this expression

 \operatorname{build-list}[\lambda P.B, [X, \_, \_]::L] \equiv \operatorname{param-list}[B, L]
 \operatorname{param-list}[B, []] \equiv \operatorname{build-param-lists}[B, R]

Variable - A call to a function.

 \operatorname{build-param-lists}[N, D, D[N]]

For a function name or parameter start populating actual parameter list by outputting the parameter list for this name.

Application - An application (function call) is processed to extract

 \operatorname{build-param-lists}[E\ P, D, R] \equiv \operatorname{build-param-lists}[E, D, T] \and \operatorname{build-param-lists}[P, D, K] \and (T = [F, S, A]::R \and S \implies \operatorname{equate}[A, P] \and V[F] = A \and K = D[F] \or T = [] \and R = [])

Retrieve the parameter lists for the expression, and the parameter. Retrieve a parameter record from the parameter list from the expression, and check that the current parameter value matches this parameter. Record the value for the parameter name for use later in checking.

 \operatorname{equate}[A, N] \equiv A = V[N] \or A = N ... if N is a variable.
 \operatorname{equate}[A, E] \equiv A = E ... otherwise.

The above logic is quite subtle in the way that it works. The same value indicator is never set to true. It is only set to false if all the values cannot be matched. The value is retrieved by using S to build a set of the Boolean values allowed for S. If true is a member then all the values for this parameter are equal, and the parameter may be dropped.

 \operatorname{ask}[S] \equiv S \in \{ X : X = S \}

The logic for equate is used in this more difficult example,

 \lambda f.((\lambda p.f\ (p\ p\ f))\ (\lambda q.\lambda x.x\ (q\ q\ x)))

has the parameter x dropped to become,

 \lambda f.((\lambda q.f\ (q\ q))\ (\lambda q.f\ (q\ q)))
function param 1 param 2
Abstract p p x
Application 1 p p f
Application 2 q q x

q in the second call has the value of p. For the second parameter, f and x are the same value.

Drop parameters[edit]

Abstraction - .

 \operatorname{drop-params}[(\lambda N.S)\ L, D, V, R] \equiv (\lambda N.\operatorname{drop-params}[S, D, F, R])\  \operatorname{drop-formal}[D[N], L, F, []]

where,

 F = FV((\lambda N.S)\ L)

Variable - .

 \operatorname{drop-params}[N, D, V, D[N]] \equiv D

For a function name or parameter start populating actual parameter list by outputting the parameter list for this name.

Application - An application (function call) is processed to extract

 (\operatorname{ask}[S] \and FV[A] \subset FV[L]) \to \operatorname{drop-params}[E\ P, D, V, R] \equiv \operatorname{drop-params}[E, [F, S, A]::R, T]
 \neg (\operatorname{ask}[S] \and FV[A] \subset FV[L]) \to \operatorname{drop-params}[E\ P, D, V, R] \equiv \operatorname{drop-params}[E, [F, S, A]::R, T]\ \operatorname{drop-params}[P, K]
Drop formal parameters[edit]

drop-formal removes formal parameters, based on the contents of the drop lists. Its parameters are,

  • The drop list,
  • The function definition (lambda abstraction).
  • The free variables from the function definition.

drop-formal is defined as,

  1.  (\operatorname{ask}[S] \and FV[A] \subset V) \to \operatorname{drop-formal}[[F, S, A]::Z, \lambda F.Y, V] \equiv \operatorname{drop-formal}[[F, S, A]::Z, Y[F:=A], L]
  2.  \neg (\operatorname{ask}[S] \and FV[A] \subset V) \to \operatorname{drop-formal}[[F, S, A]::Z, \lambda F.Y, V] \equiv \lambda F.\operatorname{drop-formal}[[F, S, A]::Z, Y, V]
  3.  \operatorname{drop-formal}[Z, Y, V] \equiv Y

Which can be explained as,

  1. If all the actual parameters have the same value, and all the free variables of that value are available for definition of the function then drop the parameter, and replace the old parameter with it's value.
  2. else do not drop the parameter.
  3. else return the body of the function.

For example,

 D = [[x, \operatorname{false}, \_], [o, \operatorname{true}, p], [y, \operatorname{true}, n]]
 \operatorname{FV}[K] = \{ k, m, p, q, f, x \}
Condition Expression
 \operatorname{false}  \operatorname{drop-formal}[D, \lambda x.\lambda o.\lambda y.o\ x\ y, K]
 \operatorname{true} \and \{p\} \subset \operatorname{FV}[K]  \lambda x.\operatorname{drop-formal}[D, \lambda o.\lambda y.o\ x\ y, K]
 \neg (\operatorname{true} \and \{n\} \subset \operatorname{FV}[K] )  \lambda x.\operatorname{drop-formal}[D, (\lambda y.o\ x\ y)[o:=p], K]
 \lambda x.\lambda y.\operatorname{drop-formal}[D, p\ x\ y, K]
 \lambda x.\lambda y.p\ x\ y

Example[edit]

Starting with the function definition of the Y-combinator,

\operatorname{let} p\ f\ x = f\ (x\ x) \and q\ p\ f = (p\ f)\ (p\ f) \operatorname{in} q\ p\
Transformation Expression
 \operatorname{let} p\ f\ x = f\ (x\ x) \and q\ p\ f = (p\ f)\ (p\ f) \operatorname{in} q\ p
abstract * 4 \operatorname{let} p = \lambda f.\lambda x.f\ (x\ x) \and q = \lambda p.\lambda f.(p\ f)\ (p\ f) \operatorname{in} q\ p
lambda-abstract-tran  (\lambda q.(\lambda p. q\ p)\ (\lambda f.\lambda x.f\ (x\ x)))\ (\lambda p.\lambda f.(p\ f)\ (p\ f))
sink-tran  (\lambda p.\lambda f.(p\ f)\ (p\ f))\ (\lambda f.\lambda x.f\ (x\ x))
sink-tran  \lambda f.(\lambda p.(p\ f)\ (p\ f))\ (\lambda f.\lambda x.f\ (x\ x))
drop-param  \lambda f.(\lambda p.p\ p)\ (\lambda x.f\ (x\ x))
beta-redex  \lambda f.(\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x))\

Which gives back the Y combinator,

\lambda f.(\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x))

Links[edit]

References[edit]

  1. ^ Danvy, O.; Schultz, U. P. (1997). "Lambda-dropping". ACM SIGPLAN Notices 32 (12): 90. doi:10.1145/258994.259007.  edit
  2. ^ Johnsson, Thomas (1985), "Lambda Lifting: Transforming Programs to Recursive Equations", Conf. on Func. Prog. Languages and Computer Architecture. (ACM Press) 

See also[edit]

External links[edit]

Category:Implementation of functional programming languages Category:Lambda calculus