This article's lead section may be too long.(September 2024) |
Lambda lifting is a meta-process that restructures a computer program so that functions are defined independently of each other in a global scope. An individual "lift" transforms a local function into a global function. It is a two step process, consisting of;
The term "lambda lifting" was first introduced by Thomas Johnsson around 1982 and was historically considered as a mechanism for implementing functional programming languages. It is used in conjunction with other techniques in some modern compilers.
Lambda lifting is not the same as closure conversion. It 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 technique may be used on individual functions, in code refactoring, to make a function usable outside the scope in which it was written. Lambda lifts may also be repeated, in order to transform the program. Repeated lifts may be used to convert a program written in lambda calculus into a set of recursive functions, without lambdas. This demonstrates the equivalence of programs written in lambda calculus and programs written as functions. [1] However it does not demonstrate the soundness of lambda calculus for deduction, 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 (see Curry's paradox).
Lambda lifting is expensive on processing time for the compiler. An efficient implementation of lambda lifting is on processing time for the compiler. [2]
In the untyped lambda calculus, where the basic types are functions, lifting may change the result of beta reduction of a lambda expression. The resulting functions will have the same meaning, in a mathematical sense, but are not regarded as the same function in the untyped lambda calculus. See also intensional versus extensional equality.
The reverse operation to lambda lifting is lambda dropping. [3]
Lambda dropping 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 it 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.
The following algorithm is one way to lambda-lift an arbitrary program in a language which doesn't support closures as first-class objects:
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.
The following OCaml program computes the sum of the integers from 1 to 100:
letrecsumn=ifn=1then1elseletfx=n+xinf(sum(n-1))insum100
(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 a parameter:
letrecsumn=ifn=1then1elseletfwx=w+xinfn(sum(n-1))insum100
Next, lift f into a global function:
letrecfwx=w+xandsumn=ifn=1then1elsefn(sum(n-1))insum100
The following is the same example, this time written in JavaScript:
// Initial versionfunctionsum(n){functionf(x){returnn+x;}if(n==1)return1;elsereturnf(sum(n-1));}// After converting the free variable n to a formal parameter wfunctionsum(n){functionf(w,x){returnw+x;}if(n==1)return1;elsereturnf(n,sum(n-1));}// After lifting function f into the global scopefunctionf(w,x){returnw+x;}functionsum(n){if(n==1)return1;elsereturnf(n,sum(n-1));}
Lambda lifting and closure are both methods for implementing block structured programs. It implements block structure by eliminating it. All functions are lifted to the global level. Closure conversion provides a "closure" which links the current frame to other frames. Closure conversion takes less compile time.
Recursive functions, and block structured programs, with or without lifting, may be implemented using a stack based implementation, which is simple and efficient. However a stack frame based implementation must be strict (eager). The stack frame based implementation requires that the life of functions be last-in, first-out (LIFO). That is, the most recent function to start its calculation must be the first to end.
Some functional languages (such as Haskell) are implemented using lazy evaluation, which delays calculation until the value is needed. The lazy implementation strategy gives flexibility to the programmer. Lazy evaluation requires delaying the call to a function until a request is made to the value calculated by the function. One implementation is to record a reference to a "frame" of data describing the calculation, in place of the value. Later when the value is required, the frame is used to calculate the value, just in time for when it is needed. The calculated value then replaces the reference.
The "frame" is similar to a stack frame, the difference being that it is not stored on the stack. Lazy evaluation requires that all the data required for the calculation be saved in the frame. If the function is "lifted", then the frame needs only record the function pointer, and the parameters to the function. Some modern languages use garbage collection in place of stack based allocation to manage the life of variables. In a managed, garbage collected environment, a closure records references to the frames from which values may be obtained. In contrast a lifted function has parameters for each value needed in the calculation.
The Let expression is useful in describing lifting and dropping, and in describing the relationship between recursive equations and lambda expressions. Most functional languages have let expressions. Also, block structured programming languages like ALGOL and Pascal are similar in that they too allow the local definition of a function for use in a restricted scope.
The let expression used here is a fully mutually recursive version of let rec, as implemented in many functional languages.
Let expressions are related to Lambda calculus. Lambda calculus has a simple syntax and semantics, and is good for describing Lambda lifting. It is convenient to describe lambda lifting as a translations from lambda to a let expression, and lambda dropping as the reverse. This is because let expressions allow mutual recursion, which is, in a sense, more lifted than is supported in lambda calculus. Lambda calculus does not support mutual recursion and only one function may be defined at the outermost global scope.
Conversion rules which describe translation without lifting are given in the Let expression article.
The following rules describe the equivalence of lambda and let expressions,
Name | Law |
---|---|
Eta-reduction equivalence | |
Let-Lambda equivalence | |
Let combination |
Meta-functions will be given that describe lambda lifting and dropping. 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,
For simplicity the first rule given that matches will be applied. The rules also assume that the lambda expressions have been pre-processed so that each lambda abstraction has a unique name.
The substitution operator is used extensively. The expression means substitute every occurrence of G in L by S and return the expression. The definition used is extended to cover the substitution of expressions, from the definition given on the Lambda calculus page. The matching of expressions should compare expressions for alpha equivalence (renaming of variables).
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.
Lambda lifts may be used on individual functions, in code refactoring, to make a function usable outside the scope in which it was written. Such lifts may also be repeated, until the expression has no lambda abstractions, in order to transform the program.
A lift is given a sub-expression within an expression to lift to the top of that expression. The expression may be part of a larger program. This allows control of where the sub-expression is lifted to. The lambda lift operation used to perform a lift within a program is,
The sub expression may be either a lambda abstraction, or a lambda abstraction applied to a parameter.
Two types of lift are possible.
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.
An anonymous lift takes a lambda abstraction (called S). For S;
The lambda lift is the substitution of the lambda abstraction S for a function application, along with the addition of a definition for the function.
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,
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.
Example for anonymous lift. |
---|
For example, See de-lambda in Conversion from lambda to let expressions. The result is, |
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. |
---|
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 for named lift. |
---|
For example, See de-lambda in Conversion from lambda to let expressions. The result is, gives, |
A lambda lift transformation takes a lambda expression and lifts all lambda abstractions to the top of the expression. The abstractions are then translated into recursive functions, which eliminates the lambda abstractions. The result is a functional program in the form,
where M is a series of function definitions, and N is the expression representing the value returned.
For example,
The de-let meta function may then be used to convert the result back into lambda calculus.
The processing of transforming the lambda expression is a series of lifts. Each lift has,
After the lifts are applied the lets are combined together into a single let.
Then Parameter dropping is applied to remove parameters that are not necessary in the "let" expression. The let expression allows the function definitions to refer to each other directly, whereas lambda abstractions are strictly hierarchical, and a function may not directly refer to itself.
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. Lambda abstractions applied to a parameter have a dual interpretation as either a let expression defining a function, or as defining an anonymous function. Both interpretations are valid.
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.
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,
Rule | function type | choice |
---|---|---|
2 | ||
3 | ||
1 | anon | |
Rule | function type | choice |
---|---|---|
2 | anon | |
2 |
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.
For example,
Rule | function type | choice |
---|---|---|
2 | ||
1 | named | |
Rule | function type | choice |
---|---|---|
1 | anon | |
For example, the Y combinator,
is lifted as,
and after Parameter dropping,
As a lambda expression (see Conversion from let to lambda expressions),
Lambda Expression | Function | From | To | Variables | |
---|---|---|---|---|---|
1 | true | ||||
2 | |||||
3 | |||||
4 | |||||
5 |
If lifting anonymous functions only, the Y combinator is,
and after Parameter dropping,
As a lambda expression,
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.
Apply function to K,
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 [4] is making the scope of functions smaller and using the context from the reduced scope to reduce the number of parameters to functions. Reducing the number of parameters makes functions easier to comprehend.
In the Lambda lifting section, a meta function for first lifting and then converting the resulting lambda expression into recursive equation was described. The Lambda 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,
A Lambda drop is applied to an expression which is part of a program. Dropping is controlled by a set of expressions from which the drop will be excluded.
where,
The lambda drop transformation sinks all abstractions in an expression. Sinking is excluded from expressions in a set of expressions,
where,
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.
Sink test excludes expressions from dropping,
Example of sinking | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
For example,
|
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,
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. It has four parameters;
Abstraction - A lambda expression of the form is analyzed to extract the names of parameters for the function.
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 actual parameter details.
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.
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.
Similarly, def uses set theory to query if a variable has been given a value;
Let - Let expression.
And - For use in "let".
For example, building the parameter lists for,
gives,
and the parameter o is dropped to give,
Build parameter list for | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Build parameter list example | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Gives,
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,
By similar arguments as used above get, and from previously, |
Another example is,
Here x is equal to f. The parameter list mapping is,
and the parameter x is dropped to give,
Build parameter list for | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
The logic in equate is used in this more difficult example.
After collecting the results together, From the two definitions for ; so Using and by comparing with the above, so, in, reduces to, also, reduces to, So the parameter list for p is effectively; |
Use the information obtained by Build parameter lists to drop actual parameters that are no longer required. drop-params has the parameters,
Abstraction
where,
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
Let - Let expression.
And - For use in "let".
Drop parameters from applications | |||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
From the results of building parameter lists; so, so,
|
drop-formal removes formal parameters, based on the contents of the drop lists. Its parameters are,
drop-formal is defined as,
Which can be explained as,
Condition | Expression |
---|---|
) | |
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,
Lambda calculus is a formal system in mathematical logic for expressing computation based on function abstraction and application using variable binding and substitution. Untyped lambda calculus, the topic of this article, is a universal model of computation that can be used to simulate any Turing machine. It was introduced by the mathematician Alonzo Church in the 1930s as part of his research into the foundations of mathematics. In 1936, Church found a formulation which was logically consistent, and documented it in 1940.
In probability theory and statistics, the negative binomial distribution is a discrete probability distribution that models the number of failures in a sequence of independent and identically distributed Bernoulli trials before a specified (non-random) number of successes occurs. For example, we can define rolling a 6 on some dice as a success, and rolling any other number as a failure, and ask how many failure rolls will occur before we see the third success. In such a case, the probability distribution of the number of failures that appear will be a negative binomial distribution.
In probability theory and statistics, the exponential distribution or negative exponential distribution is the probability distribution of the distance between events in a Poisson point process, i.e., a process in which events occur continuously and independently at a constant average rate; the distance parameter could be any meaningful mono-dimensional measure of the process, such as time between production errors, or length along a roll of fabric in the weaving manufacturing process. It is a particular case of the gamma distribution. It is the continuous analogue of the geometric distribution, and it has the key property of being memoryless. In addition to being used for the analysis of Poisson point processes it is found in various other contexts.
In combinatory logic for computer science, a fixed-point combinator, is a higher-order function that returns some fixed point of its argument function, if one exists.
In mathematical optimization, the method of Lagrange multipliers is a strategy for finding the local maxima and minima of a function subject to equation constraints. It is named after the mathematician Joseph-Louis Lagrange.
In linear algebra, the Cayley–Hamilton theorem states that every square matrix over a commutative ring satisfies its own characteristic equation.
In mathematics, the unitary group of degree n, denoted U(n), is the group of n × n unitary matrices, with the group operation of matrix multiplication. The unitary group is a subgroup of the general linear group GL(n, C), and it has as a subgroup the special unitary group, consisting of those unitary matrices with determinant 1.
In mathematics, a self-adjoint operator on a complex vector space V with inner product is a linear map A that is its own adjoint. If V is finite-dimensional with a given orthonormal basis, this is equivalent to the condition that the matrix of A is a Hermitian matrix, i.e., equal to its conjugate transpose A∗. By the finite-dimensional spectral theorem, V has an orthonormal basis such that the matrix of A relative to this basis is a diagonal matrix with entries in the real numbers. This article deals with applying generalizations of this concept to operators on Hilbert spaces of arbitrary dimension.
In probability theory and statistics, the Weibull distribution is a continuous probability distribution. It models a broad range of random variables, largely in the nature of a time to failure or time between events. Examples are maximum one-day rainfalls and the time a user spends on a web page.
In Riemannian geometry, the sectional curvature is one of the ways to describe the curvature of Riemannian manifolds. The sectional curvature K(σp) depends on a two-dimensional linear subspace σp of the tangent space at a point p of the manifold. It can be defined geometrically as the Gaussian curvature of the surface which has the plane σp as a tangent plane at p, obtained from geodesics which start at p in the directions of σp. The sectional curvature is a real-valued function on the 2-Grassmannian bundle over the manifold.
In mathematics, Jensen's inequality, named after the Danish mathematician Johan Jensen, relates the value of a convex function of an integral to the integral of the convex function. It was proved by Jensen in 1906, building on an earlier proof of the same inequality for doubly-differentiable functions by Otto Hölder in 1889. Given its generality, the inequality appears in many forms depending on the context, some of which are presented below. In its simplest form the inequality states that the convex transformation of a mean is less than or equal to the mean applied after convex transformation; it is a simple corollary that the opposite is true of concave transformations.
In mathematics, Church encoding is a means of representing data and operators in the lambda calculus. The Church numerals are a representation of the natural numbers using lambda notation. The method is named for Alonzo Church, who first encoded data in the lambda calculus this way.
A ratio distribution is a probability distribution constructed as the distribution of the ratio of random variables having two other known distributions. Given two random variables X and Y, the distribution of the random variable Z that is formed as the ratio Z = X/Y is a ratio distribution.
In statistics, a power transform is a family of functions applied to create a monotonic transformation of data using power functions. It is a data transformation technique used to stabilize variance, make the data more normal distribution-like, improve the validity of measures of association, and for other data stabilization procedures.
In probability theory and statistics, the Conway–Maxwell–Poisson distribution is a discrete probability distribution named after Richard W. Conway, William L. Maxwell, and Siméon Denis Poisson that generalizes the Poisson distribution by adding a parameter to model overdispersion and underdispersion. It is a member of the exponential family, has the Poisson distribution and geometric distribution as special cases and the Bernoulli distribution as a limiting case.
In mathematics, the spectral theory of ordinary differential equations is the part of spectral theory concerned with the determination of the spectrum and eigenfunction expansion associated with a linear ordinary differential equation. In his dissertation, Hermann Weyl generalized the classical Sturm–Liouville theory on a finite closed interval to second order differential operators with singularities at the endpoints of the interval, possibly semi-infinite or infinite. Unlike the classical case, the spectrum may no longer consist of just a countable set of eigenvalues, but may also contain a continuous part. In this case the eigenfunction expansion involves an integral over the continuous part with respect to a spectral measure, given by the Titchmarsh–Kodaira formula. The theory was put in its final simplified form for singular differential equations of even degree by Kodaira and others, using von Neumann's spectral theorem. It has had important applications in quantum mechanics, operator theory and harmonic analysis on semisimple Lie groups.
In probability theory and statistics, the Poisson distribution is a discrete probability distribution that expresses the probability of a given number of events occurring in a fixed interval of time if these events occur with a known constant mean rate and independently of the time since the last event. It can also be used for the number of events in other types of intervals than time, and in dimension greater than 1.
In computer science, a "let" expression associates a function definition with a restricted scope.
Lambda calculus is a formal mathematical system based on lambda abstraction and function application. Two definitions of the language are given here: a standard definition, and a definition using mathematical formulas.
In statistics and probability, the Neyman Type A distribution is a discrete probability distribution from the family of Compound Poisson distribution. First of all, to easily understand this distribution we will demonstrate it with the following example explained in Univariate Discret Distributions; we have a statistical model of the distribution of larvae in a unit area of field by assuming that the variation in the number of clusters of eggs per unit area could be represented by a Poisson distribution with parameter , while the number of larvae developing per cluster of eggs are assumed to have independent Poisson distribution all with the same parameter . If we want to know how many larvae there are, we define a random variable Y as the sum of the number of larvae hatched in each group. Therefore, Y = X1 + X2 + ... Xj, where X1,...,Xj are independent Poisson variables with parameter and .