Warning: My answer actually describes captured variables which is different than lambda lifting. Misread the question (need sleep). But I spent a bit of time writing this up so I'm loath to delete it. Left it up as a community WIKI.
Lambda lifting, often referred to as closures, is a way of seamlessly allowing access of in scope variables from within a nested lambda expression.
It's hard to get into the nitty gritty details of closures without picking a particular language. One of the side effects of lambda lifting, in any langauge, is that it tends to extend the lifetime of a variable from a local, short lived scope, to a much longer lived scope. Usually this occurs in the form of transferring a variable from the stack to the heap within the compiler. This is a very language specific action and therefore produces very different implementations based on the language.
I'll focus on C# since that's probably the language most common to the readers of stack overflow. Lets start with the following code.
public Func<int> GetAFunction() {
var x = 42;
Func<int> lambda1 = () => x;
Func<int> lambda2 = () => 42;
...
return lambda1;
}
In this example we've created 2 lambda expressions. In both cases it's assigned to a delegate instance of type Func. All delegates in .Net require that a real function be backing them somewhere. So under the hood, all lambda expressions/ anonymous functions in C# are translated into a method definition.
Generating a function for lambda2 is pretty straight forward. It's an isolated function that just returns a constant value.
public static int RealLambda2() {
return 42;
}
Generating lambda1 is quite a bit harder. A literal definition would look like the following
public static int RealLambda1() {
return x;
}
This obviously won't compile because x is not accessible. In order to make this work, the C# compiler must lift the variable x into a closure. It can then return a pointer to a function within the closure to satisfy the delegate expression
class Closure1 {
int x;
public int RealLambda1() {
return x;
}
}
This is a pretty simple example but should hopefully detail the art of lifting. The devil is unfortunately in the details and gets much more complex with the scenario.