This is an old revision of this page, as edited by Thepigdog (talk | contribs) at 13:51, 21 November 2013 (→Lambda dropping in Lambda Calculus). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Revision as of 13:51, 21 November 2013 by Thepigdog (talk | contribs) (→Lambda dropping in Lambda Calculus)(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)
Lambda lifting is the process of eliminating free variables from local function definitions from a computer program. The elimination of free variables allows the compiler to hoist local definitions out of their surrounding contexts into a fixed set of top-level functions with an extra parameter replacing each local variable. By eliminating the need for run-time access-links, this may reduce the run-time cost of handling implicit scope. Many functional programming language implementations use lambda lifting during compilation.
The term "lambda lifting" was first introduced by Thomas Johnsson around 1982.
Lambda lifting is not the same as closure conversion. Lambda lifting requires all call sites to be adjusted (adding extra arguments to calls) and does not introduce a closure for the lifted lambda expression. In contrast, closure conversion does not require call sites to be adjusted but does introduce a closure for the lambda expression mapping free variables to values.
The reverse operation is called lambda dropping.
Algorithm
The following algorithm is one way to lambda-lift an arbitrary program in a language which doesn't support closures as first-class objects:
- Rename the functions so that each function has a unique name.
- Replace each free variable with an additional argument to the enclosing function, and pass that argument to every use of the function.
- Replace every local function definition that has no free variables with an identical global function.
- Repeat steps 2 and 3 until all free variables and local functions are eliminated.
If the language has closures as first-class objects that can be passed as arguments or returned from other functions, the closure will need to be represented by a data structure that captures the bindings of the free variables.
Example
The following OCaml program computes the sum of the integers from 1 to 100:
let rec sum n =
if n = 1 then
1
else
let f x =
n + x in
f (sum (n - 1)) in
sum 100
(The let rec
declares sum
as a function that may call itself.) The function f, which adds sum's argument to the sum of the numbers less than the argument, is a local function. Within the definition of f, n is a free variable. Start by converting the free variable to an argument:
let rec sum n =
if n = 1 then
1
else
let f w x =
w + x in
f n (sum (n - 1)) in
sum 100
Next, lift f into a global function:
let rec f w x =
w + x
and sum n =
if n = 1 then
1
else
f n (sum (n - 1)) in
sum 100
The following is the same example, this time written in JavaScript:
// Initial version
function sum (n) {
function f (x) {
return n + x;
}
if (n == 1) {
return 1;
}
else {
return f( sum(n - 1) );
}
}
// After converting the free variable n to a formal parameter w
function sum (n) {
function f (w, x) {
return w + x;
}
if (n == 1) {
return 1;
}
else {
return f( n, sum(n - 1) );
}
}
// After lifting function f into the global scope
function f (w, x) {
return w + x;
}
function sum (n) {
if (n == 1) {
return 1;
}
else {
return f( n, sum(n - 1) );
}
}
Conversion without lifting
The let expression, is useful in describing lifting and dropping, and in describing the relationship between recursive equations and lambda expressions. The "let" expression is present in many functional languages to allow the local definition of expression, for use in defining another expression. The "let" expression may be generalized slightly to allow equations to be used in defining the local expression. A let expression then looks like,
where,
- f is a variable name, where f has a local scope.
- E is a Boolean expression.
- L is a Lambda expression.
The following rules describe the equivalence of lambda and let expressions,
Name |
Law
|
lambda-param |
|
lambda-let |
(where f is a variable name).
|
let-let |
(where f is a variable name).
|
Meta-functions will be given that describe the conversion between lambda and let expressions. 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.
- will be used for equals in the meta program.
For simplicity the first rule given that matches will be applied.
Conversion from Lambda to Let expressions
The following rules describe how to convert from a Lambda expression to a let expresion, without altering the structure.
For example the Y combinator,
Rule |
Lambda Expression
|
2 |
|
6, 3 |
|
2 |
|
1 |
|
5 |
|
6, 3 |
|
Conversion from Let to Lambda expressions
These rule reverse the conversion described above. They convert from a let expression to a Lambda expresion, without altering the structure. Not all let expressions may be converted using these rules.
For example the let expression obtained from the Y combinator,
Rule |
Lambda Expression
|
1 |
|
6 |
|
7 |
|
3 |
|
2, 4, 5 |
|
6 |
|
7 |
|
3, 4, 5 |
|
|
|
For a second example take the lifted version of the Y combinator, derived in Lambda Lifting
Rule |
Lambda Expression
|
8 |
|
2 |
|
6, 7 |
|
2, 4, 5 |
|
6, 7 |
|
|
|
Lambda Lifting
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.
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.
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. An efficient implementation of Lamba lifting is O(n 2) 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.
In the Untyped Lambda Calculus, where the basic types are functions, lifting may change the result of beta reduction of a lambda expression. This because in the Untyped Lambda Calculus beta reduction of a lifted Lambda expression may give a different result. The resulting functions will have the same meaning, in a mathematical sense, but not be regarded as the same in the Untyped Lambda Calculus.
Lambda lifting is useful in understanding lambda expressions, and the compiler optimization of code.
Lamba-Lift transformation
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,
The de-let meta function may then be used to convert the result back into Lambda Calculus.
Calling lambda-lift-tran on a lambda expression gives you a functional program in the form,
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,
- 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.
- The lift is performed by a call to the lambda-lift meta function, described in the next section,
After the lifts are applied the lets are combined together into a single let.
Choosing the expression for lifting
There are two different ways that an expression may be selected for lifting. The first treats all lambda abstractions as defining anonymous functions. The second, treats lambda abstractions which are applied to a parameter as defining a function.
These two predicates are needed for both definitions.
lambda-free - An expression containing no lambda abstractions.
lambda-anon - An anonymous function. An expression like where X is lambda free.
Choosing anonymous functions only for lifting
Search for the deepest anonymous abstraction, so that when the lift is applied the function lifted will become a simple equation. This definition does not recognize a lambda abstractions with a parameter as defining a function. All lambda abstractions are regarded as defining anonymous functions.
lift-choice - The first anonymous found in traversing the expression or none if there is no function.
For example,
Lambda choice on is
Rule |
function type |
choice
|
2 |
|
|
3 |
|
|
1 |
anon |
|
|
|
|
Lambda choice on is
Rule |
function type |
choice
|
2 |
anon |
|
2 |
|
|
Choosing named and anonymous functions for lifting
Search for the deepest named or anonymous function definition, so that when the lift is applied the function lifted will become a simple equation. This definition recognizes a lambda abstraction with an actual parameter as defining a function. Only lambda abstractions without an application are treated as anonymous functions.
lambda-named - A named function. An expression like where M is lambda free and N is lambda free or an anonymous function.
lift-choice - The first anonymous or named function found in traversing the expression or none if there is no function.
For example,
Lambda choice on is
Rule |
function type |
choice
|
2 |
|
|
1 |
named |
|
|
|
|
Lambda choice on is
Rule |
function type |
choice
|
1 |
anon |
|
|
|
|
Lifting
Two types of lifting are possible.
- Anonymous lift
- Named lift
An anonymous lift has a lift expression which is a lambda abstraction only. It is regarded as defining an anonymous function. A name must be created for the function.
A named lift expression has a lambda abstraction applied to an expression. This lift is regarded as a named definition of a function.
Anonymous Lift
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 V). Make sure that the name identified by V has not been used.
- Add parameters to V, 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.
The new lambda expression has S substituted for G. Note that L 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,
where V is the function name. It must be a new variable, i.e. a name not already used in the lambda expression,
where is a meta function that returns the set of variables used in E.
Constructing the call
The function call G is constructed by adding parameters for each variable in the free variable set (represented by V), to the function H,
Example of call construction.
|
|
Named Lift
The named lift is similar to the anonymous lift except that the function name V is provided.
As for the anonymous lift, the expression G is constructed from V by applying the free variables of S. It is defined by,
Example - Lifting named and anonymous functions
For example the Y combinator,
Lifting named and anonymous functions
|
Lambda Expression |
Function |
From |
To |
Variables
|
1 |
|
true |
|
|
|
2
|
|
|
|
|
|
3 |
|
|
|
|
|
4 |
|
|
|
|
|
5 |
|
|
|
|
|
Example - Lifting anonymous functions only
Lifting anonymous functions only
|
Lambda Expression |
Function |
From |
To |
Variables
|
1 |
|
true |
|
|
|
2 |
|
|
|
|
|
3 |
|
|
|
|
|
4 |
|
|
|
|
|
5 |
|
|
|
|
|
The first sub expression to be chosen for lifting is . This transforms the lambda expression into and creates the equation .
The second sub expression to be chosen for lifting is . This transforms the lambda expression into and creates the equation .
And the result is,
Surprisingly this result is simpler than the one obtained from lifting named functions.
As a lambda expression (see Conversion from Let to Lambda expressions),
Execution
Apply function to,
|
|
|
|
|
|
So,
or
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
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 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.
Lambda dropping is performed in two steps,
- Sinking
- Parameter dropping
The Lambda-drop-tran implements Lambda dropping. It is defined as,
Abstraction Sinking
sink-tran sinks each abstraction, starting from the innermost,
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.
Abstraction. Use renaming to insure that the variable names are all distinct.
Variable - 2 cases.
Parameter Dropping
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,
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,
-
For the main example,
-
The definition of drop-params-tran is,
where,
Build parameter lists
For each abstraction that defines a function, build the information required to make decisions on dropping names. This information describes 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,
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.
build-param-lists builds all the lists for an expression, by traversing the expression.
Abstraction - A lambda expression of the form is analyzed to extract the names of parameters for the function.
- Failed to parse (Conversion error. Server ("https://wikimedia.org/api/rest_") reported: "Cannot get mml. Server problem."): {\displaystyle \operatorname {build-param-lists} \equiv \operatorname {build-param-lists} \land \operatorname {build-list} ]}
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
Variable - A call to a function.
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
-
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.
- ... if N is a variable.
- ... 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.
Simmilarly, def uses set theory to query if a value has been given a value;
The logic for equate is used in this more difficult example,
has the parameter x dropped to become,
|
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.
Build parameter list example
|
Rule |
Expression
|
Abstraction
|
|
Abstraction
|
|
|
|
Rule |
Expression
|
Add param
|
|
Add param
|
|
Add param
|
|
End list
|
|
|
|
Rule |
Expression
|
Application
|
|
Application
|
|
Variable
|
|
Variable
|
|
Gives,
Rule |
Expression
|
application
|
|
|
|
application, Variable
|
|
application, Variable
|
|
Variable
|
|
Rule |
Expression
|
application
|
|
|
|
application, Variable
|
|
application, Variable
|
|
Variable
|
|
As there are no definitions for, , then equate can be simplified to,
By removing expressions not needed,
By comparing the two expressions for , get,
If is true;
If is false there is no implication. So which means it may be true or false.
If is true;
If is true;
So is false.
The result is,
Rule |
Expression
|
application
|
|
application
|
|
variable
|
|
|
|
By similar arguments as used above get,
and from previously,
|
Drop parameters
Abstraction
where,
Variable
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
For example, where,
Example
Drop parameters from applications
|
Condition |
Expression
|
|
|
|
|
|
|
|
|
|
|
|
From the results of building parameter lists;
so,
so,
Condition |
Expanded |
Expression
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Condition |
Expanded |
Expression
|
V = \{p, q, m\}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Drop formal parameters
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,
Which can be explained as,
- 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.
- else do not drop the parameter.
- else return the body of the function.
For example,
Condition |
Expression
|
|
|
|
|
) |
|
|
|
|
|
Example
Starting with the function definition of the Y-combinator,
Transformation |
Expression
|
|
|
abstract * 4
|
|
lambda-abstract-tran
|
|
sink-tran
|
|
sink-tran
|
|
drop-param
|
|
beta-redex
|
|
Which gives back the Y combinator,
See also
References
- Attention: This template ({{cite doi}}) is deprecated. To cite the publication identified by doi:10.1145/258994.259007, please use {{cite journal}} (if it was published in a bona fide academic journal, otherwise {{cite report}} with
|doi=10.1145/258994.259007
instead.
- Johnsson, Thomas (1985), "Lambda Lifting: Transforming Programs to Recursive Equations", Conf. on Func. Prog. Languages and Computer Architecture., ACM Press
-
Optimal Lambda Lifting in Quadratic Time, pp. pp 37-56, doi:10.1007/978-3-540-85373-2_3, ISBN 978-3-540-85372-5
{{citation}}
: |pages=
has extra text (help); Cite has empty unknown parameter: |1=
(help); Unknown parameter |booktitle=
ignored (help)
-
Olivier Danvy and Ulrik P. Schultz (2001). Lambda-Dropping: Transforming Recursive Equations into Programs with Block Structure (PDF).
External links
Categories: