Lambda Expressions

A guide to programming lambda expressions in C++, C#, Java, Javascript, and Python by JoshData.

A lambda expression is a convenient syntax available in many programming languages for writing short functions. Lambda expressions do for functions what object-oriented programing does for objects: It makes a function something you can assign to a variable.

Lambda expressions are also sometimes called anonymous functions, lambda functions, and closures. The capabilities of lambda expressions differs across programming languages, and some programming languages have multiple ways of writing lambda expressions. The concept originates in philosophy.

Background Concepts

Let’s cover some general programming concepts first.

Statements versus expressions

All programming languages with lambda expressions distinguish statements from expressions:

if x > y is a statement. x > y is an expression — it computes one value, in this case either true or false. The if statement takes an action based on the value of the expression.

Statically versus dynamically typed languages

This guide covers a few common programming languages, and if you are reading it to learn about multiple languages it may be helpful to distinguish:

Lambda Syntax

A lambda expression is a function written in a shorthand syntax, and we’ll start by looking at the syntax of lambda expressions.

Here is a regular function (not a lambda function, just a normal function) that adds one to a number as it would be written in C, C++, C#, and Java (they all happen to be identical in this case) and in Javascript. We’ll look at how the same function would be written as a lambda expression.

Add one, in C, C++, C#, and Java

    double add_one(double x) {
      return x + 1;
    }
    
Add one, in Javascript

    function add_one(x) {
      return x + 1;
    }
    

In this example, there is a single argument x, the expression x + 1 is computed, and that expression is returned to the caller.

It can be written shorter as a lambda expression in modern versions of many languages, including C++ (starting in C++11), C# (starting in C# 9.0), Java (since Java 8), Javascript (starting in ECMAScript 6), and Python (since around Python 2.2):

C++

    [](double x) { return x + 1; }
    
C#

    x => x + 1
    
Java

    x -> x + 1
    
Javascript

    x => x + 1
    
Python

    lambda x : x + 1
    

C#, Java, and Javascript use an arrow symbol (made up of an equal sign or dash plus a greater than sign) to separate the arguments from the expression body. The C# and Javascript syntaxes happen to be identical in this case. (In Javascript, an “anonymous function” refers to a different but related syntactic structure — the technical name for the Javascript syntax discussed here is the “arrow function expression.”)

In the academic discipline of philosophy, the function would be written with the Greek lowercase letter lambda λ denoting the start of a lambda expression:


    λx.x+1
    

Here’s a function that returns the maximum value of two numbers, first written as a regular function:

Maximum value, in C, C++, C#, and Java

    double max(double x, double y) {
      if (x > y)
        return x;
      return y;
    }
    

and then as a lambda expression in various languages:

C++

    [](double x, double y) {
      if (x > y)
        return x;
      return y;    
    }
    
C# and Javascript

    (x, y) => {
      if (x > y)
        return x;
      return y;    
    }
    
Java

    (x, y) -> {
      if (x > y)
        return x;
      return y;    
    }
    

Lambda expressions can also have zero arguments:

C++

    []() {
      cout << "Hello world." << endl;    
    }
    
C#

    () => {
      Console.WriteLine("Hello world.");    
    }
    
Java

    () -> {
      System.out.println("Hello world.");
    }
    
Javascript

    () => {
      console.log("Hello world.");    
    }
    
Python

    lambda : print("Hello")
    

Lambda expressions have most of the same components as a normal function:


In the body of the lambda expression, some languages allow both statement blocks and expressions (C#), some only support statements (C++), and some only support expressions (Python). The add_one examples above used expressions as lambda function bodies where possible: x + 1 in C#, Python, etc. No braces are used in those languages when the body is an expression:

C#/Javascript using an expression body

    x => x + 1
    

The max examples above used statement blocks as lambda function bodies, and when statements are used these languages require that they be surrounded in braces:

C#/Javasript using a statement block body

    (x, y) => {
      if (x > y)
        return x;
      return y;    
    }
    

This last example could be turned into an expression-bodied lambda by using the ternary operator condition ? then : else. Expression bodies are usually easier to read because they are more compact.


The one thing missing from lambda expressions is a function name. Lambda expressions don’t have a place for a name in their syntax.

(No type is given for the return value in any of the examples above either. It is optional in C++ and not permitted in other languages. In statically typed languages, the lambda expression does have a return type but the compiler figures it out from what you return.)

Lambda Expressions are Expressions

The key difference between a regularly defined function and a lambda expression is where they occur in source code. Regular functions are normally defined either at the top-level of your source code file or in a class. All of the statements in your source code are contained within a function.

A lambda expression is exactly the opposite. It occurs not outside of statements but inside statements. It actually is an expression, which means it occurs within statements wherever an expression can be used.

Assigning a lambda expression to a variable

It can be assigned to a variable. Variable assignment normally looks like this of course:

A variable assignment statement in C-family languages

    f = 10;
    

(C++ and C# are a statically typed language so the variable must be declared first with its type, but we will omit the variable declaration for now.)

Now instead of 10, the variable will be assigned a lambda expression:

C++ variable assignment with a lambda expression

    f = [](double x, double y) {
      if (x > y)
        return x;
      return y;    
    };
    
C#/Javascript variable assignment with a lambda expression

    f = (x, y) => {
      if (x > y)
        return x;
      return y;    
    };
    

The lambda expression is assigned to the variable f. Note that the f = at the start and the semicolon ; at the end belong to the variable assignment statement. The lambda expression appears in the middle as the expression. Since the lambda expression is multiple lines, it pushes the semicolon ; down a few lines to its end.

After the variable assignment, the variable actually holds the lambda function defined by the lambda expression.

Quick look at the type of lambda expressions in the statically typed languages

In statically typed languages each variable has a type. In C++ and C# (starting with C# 10), the compiler can guess the type so auto (in C++) or var (in C#) can be used:

C++ variable assignment with a lambda expression

    auto f = [](double x, double y) {
      if (x > y)
        return x;
      return y;    
    };
    
C# 10+ variable assignment with a lambda expression

    var parse = (double x, double y) => (x > y ? x : y);
    

You can also make a variable declaration with an explicit type, and in C# ≤9 and Java it’s required, even though both have var keywords. The variable types to use in C++, C# and Java are:

For example, in Java:

Java variable assignment with a lambda expression

    import java.util.function.*;
    ...
    BinaryOperator<Double> f =
      (x, y) -> {
        if (x > y)
          return x;
        return y;    
      };
    

We’ll come back to the type of lambda expressions later.

No type is needed in dynamically typed languages like Javascript and Python.

Few meaningful operators are defined for lambda expressions

Lambda expressions are expressions, but most operators don’t have any meaning with the value that a lambda expression computes. You can’t add two lambda functions together with +: It doesn’t make sense and that isn’t defined in any language.

The first operator that can be used with lambda expressions are grouping parentheses. Lambda expressions can always be wrapped in parentheses for clarity:

C# variable assignment with a lambda expression and parentheses

    f = ((x, y) => {
      if (x > y)
        return x;
      return y;    
    });
    

Be sure you see where the open and closing parens were added here.

Calling Lambda Expressions

The most important operator that works with lambda functions is the call operator. The next example starts with the same variable assignment as above, but it follows with a new statement that invokes the lambda function by calling it:

C# calling a lambda function

    Func<double, double, double> f =
      (x, y) => {
        if (x > y)
          return x;
        return y;    
      };

    double z = f(10, 20);

    // z holds 20.
    

It is just like calling a function, but instead of f being the name of a function it is the name of a variable holding a lambda function.

When f(10, 20) is called, control flow moves to the lambda expression. The statements of the lambda expression are evaluated until the return statement is executed. Then control flow moves back the assignment of the value to z.

Assigning a lambda expression to a variable and then, some time later, using the call operator on the variable is the main thing lambda expressions do.


Java does not support the call operator — this is unusual for a language that has lambda expressions. To invoke the lambda function, in this example we use .apply(...) which is the correct method to use with BinaryOperator<Double>. (The method name depends on the runnable interface type that the expression is assigned to.)

Java calling a lambda function

    import java.util.function.*;
    ...
    BinaryOperator<Double> f =
      (x, y) -> {
        if (x > y)
          return x;
        return y;    
      };

    double z = f.apply(10.0, 20.0);

    // z holds 20.
    

In the next toy example, f is first set to a lambda expression that computes the maximum value of two arguments and then later it is set to a different lambda expression that computes the minimum value of two arguments. Although the same code f(10, 20) is called identically after each assignment, f returns a different value each time because it executes the two lambda functions:

C#

    double z;
    Func<double, double, double> f;

    f = (x, y) => {
      if (x > y)
        return x;
      return y;    
    };

    z = f(10, 20);
    // z holds 20.

    f = (x, y) => {
      if (x < y)
        return x;
      return y;    
    };

    z = f(10, 20);
    // z holds 10!
    

Although f(10, 20) appears twice, it computes a different value each time. In the first call to f, control flow goes to the first lambda expression. In the second call to f, control flow goes to the second lambda expression.


In some languages, the call operator (10, 20) can occur after any expresson that evaluates to a lambda function. In C++, Javascript, and Python, it can be right after the lambda expression itself:

C++

    auto z = [](double x) { return x + 1; }(10);
    
Javascript

    let z = (x => x + 1)(10);
    
Python

    z = (lambda x : x + 1)(10)
    

Make sure you see where the lambda expression begins and ends and where the call operator begins and ends.

This pattern is commonly seen in Javascript for reasons related to scope and not really about the lambda expression. It isn’t generally useful because you can always write an expression like this without the complex lambda expression syntax.

C# and Java do not allow this.

Passing lambda expressions as arguments to other functions

The last meaningful thing that you can do with lambda expressions is passing the lambda expresson as an argument to a function. In the next set of examples, a new function foo is defined that takes one argument. The program calls foo and passes a lambda expression as the argment.

C# passing a lambda expression with an expression body to a function

    void foo(Func<double, double> f) {
      ...
    }

    ...

    foo(x => x + 1);
    
Java passing a lambda expression with an expression body to a function

    import java.util.function.*;
    
    ...
    
    void foo(UnaryOperator<Double> f) {
      ...
    }

    ...

    foo(x -> x + 1);
    
Javascript passing a lambda expression with an expression body to a function

    function foo(f) {
      ...
    }

    ...

    foo(x => x + 1);
    

Here are additional examples using the more complex lambda functions with statement bodies:

C++ passing a lambda expression to a function

    #include <functional>

    using namespace std;

    void foo(function<double(double, double)> f) {
       ...
    }
          
    ...
    
    foo( [](double x, double y) {
      if (x > y)
        return x;
      return y;
    } );
    
C# passing a lambda expression to a function

    void foo(Func<double, double, double> f) {
      ...
    }

    ...

    foo( (x, y) => {
      if (x > y)
        return x;
      return y;
    } );
    
Javascript passing a lambda expression to a function

    function foo(f) {
      ...
    }

    ...
    
    foo( (x, y) => {
      if (x > y)
        return x;
      return y;
    } );
    

When passing lambda expressions with statement bodies in function calls, the triple of symbols brace-paren-semicolon } ); is a common line ending. The close brace belongs to the lambda expression: It is the close-brace that ends the lambda expression’s body. The close parenthesis belongs to the function call: It is the close parethesis at the end of the argument list. The semicolon marks the end of the statement as in all C++, C#, Java, and Javascript statements.


The most common way to use a lambda expression is passing it to another function and then calling it within that function. Here is the same function call example again, plus calling the lambda function within foo:

C# passing a lambda expression to a function and then calling it

    foo( (x, y) => {
      if (x > y)
        return x;
      return y;
    } );

    ...

    void foo(Func<double, double, double> f) {
      double z = f(10, 20);
      // z holds 20.
    }
    

Finally, we look at a lambda expression with no return value. Lambda expressions without a return value are typically used with statement-block bodies to take an action.

C#

    foo(() => {
      Console.Out.WriteLine("first lambda");
    });
    // prints:
    //
    // foo - start
    // first lambda
    // foo - end

    foo(() => {
      Console.Out.WriteLine("second lambda");
    });
    // prints:
    //
    // foo - start
    // second lambda
    // foo - end

    ...

    void foo(Action f) {
      Console.Out.WriteLine("foo - start");
      f();
      Console.Out.WriteLine("foo - start");
    }
    

This lambda expression has a void return type in C++, C#, and Java. (Javascript and Python do not support void return types — if there is no return value, the lambda expression returns undefined (Javascript) or None (Python).)


In C++, C#, Java, Javascript, and Python, any regular function name or class method can also be assigned to a variable and passed to a function, like lambda expressions. In the statically typed languages, the variable or function argument must have the right type. But in dynamically typed languages, that’s not an issue and passing around functions can be very natural:

Python

    def min(x, y):
        if x < y:
            return x
        return y

    def max(x, y):
        if x > y:
            return x
        return y

    foo(min, 10, 20);
    # prints 10

    foo(max, 10, 20);
    # prints 20

    def foo(f, x, y) {
      f(x, y);
    }
    

Terminology

In this guide, lambda expression and lambda function mean slightly different things, although I can’t promise that anyone else makes this distinction:

A lambda expression is the code you type to define a short function. It is source code text that goes into the compiler and is recognized with a particular syntax. (In Javascript, technically they are called arrow function expressions/declarations.)

The expression evaluates at run time to a lambda function in memory. In memory during program execution, the f variable in the preceding examples holds a lambda function. It doesn’t hold the source code text that you typed in — that’s been compiled into some other more efficient representation. So it doesn’t hold an expression. Instead it probably holds a pointer to a memory location that has the compiled code.

The difference between a lambda expression and a lambda function is similar to the difference between a class and an instance of the class (an object). A class is a definition of a type of object. At run time, variables whose types are classes don’t hold classes: they hold pointers to objects. Similarly, variables that are assigned lambda expressions in code hold pointers to lambda functions at run time, not lambda expressions. (In fact, in many languages the lambda expression actually compiles to a new instance of a hidden class!)

Standard Library Routines that Use Lambda Functions

The standard library in each programming language has some methods that are convenient to use with lambda expressions.

C++

Across programming languages, lambda functions are commonly used with the language’s standard library sort function to create sort orders for user-defined data types. In C++, a lambda expression can be passed to std::sort for this purpose.

std::sort’s third argument is a function that compares two items in the list and returns whether the first item should come first. The arguments to the comparison function must be const references.

In this example, a user-defined class is sorted first by its name field and then, when there are any instances with the same name, by its value field.

C++ sorting

    #include <string>
    #include <vector>
    #include <algorithm>

    using namespace std;

    class MyClass {
    public:
      string name;
      double value;
    };

    ...

    vector<MyClass> items;

    ...

    sort(items.begin(), items.end(), [](const MyClass& a, const MyClass& b) {
      if (a.name < b.name) return true;
      if (a.name > b.name) return false;
      return a.value < b.value;
    });
    

std::sort will call the lambda function for each pair of elements in the list and will use its return value to sort the elements according to the order that the lambda function defines. The comparison function always looks something like this to achieve a sort order over multiple fields.


The standard library has a handful of functions that take comparison functions like sort does, including min_element/max_element — another common use of lambda functions across languages. This example finds the MyClass instance in a vector with the smallest value.

C++ min_element

    #include <algorithm>

    ...

    auto iter = min_element(items.begin(), items.end(), [](const MyClass& a, const MyClass& b) {
      return a.value < b.value;
    });

    cout << iter->name;
    

This is more compact than writing a for loop to iterate over the elements and track which one has the minimum value.

A comparison function can be used to create set and map containers for user-defined data types. (A hash code generator can also be used with unordered_set and unordered_map.)

You can also put lambda functions inside containers:

C++

    #include <iostream>
    #include <list>
    #include <algorithm>
    #include <functional>

    using namespace std;

    ...

    list<function<void()>> actions;
    
    actions.push_back([]() { cout << "Hello "; });
    actions.push_back([]() { cout << "world."; });
    
    for_each(actions.begin(), actions.end(), [](function<void()> f) {
      f();
    });
    

C#

Across programming languages, lambda functions are commonly used with the language’s container sort functions to create sort orders for user-defined data types. In C#, a lambda expression can be passed to List.Sort(...) for this purpose.

List.Sort()’s optional argument is a function that compares two items in the list and returns which should be first in the list. The comparison function returns -1 if the first item should come first, 1 if the second item should come first, or 0 if the items can have either order.

In this example, a user-defined class is sorted first by its name field and then, when there are any instances with the same name, by its value field.

C# sorting

    using System.Collections.Generic;

    class MyClass {
      public string Name;
      public double Value;
    };

    ...

    var items = new List<MyClass>();

    ...

    items.Sort((a, b) => {
      var cmp_name = a.Name.CompareTo(b.Name);
      if (cmp_name != 0) return cmp_name;
      return a.Value.CompareTo(b.Value);
    });
    

List.Sort will call the lambda function for each pair of elements in the list and will use its return value to sort the elements according to the order that the lambda function defines. The comparison function always looks something like this to achieve a sort order over multiple fields. String.CompareTo and Double.CompareTo have the same sematics as the function expected by Sort: They return -1, 0, or 1.

List.ForEach is another helpful method with a lambda expression — it simply runs the function on each element of the list. Here’s how you can print out each item in the list:

C# List.ForEach

    items.ForEach(a => {
      Console.WriteLine(a.Name);
      Console.WriteLine(a.Value);
    });
    

You could of course also write a regular foreach loop, but the lambda expression syntax might be clearer or cleaner in some cases.

You can also put lambda functions inside lists:

C#

    var actions = new List<Action>();

    actions.Add( () => Console.WriteLine("Hello") );
    actions.Add( () => Console.WriteLine("world.") );

    actions.ForEach(action => action());
    

The extension methods in System.Linq.Enumerable (reference) provide other utility methods on collections that are helpful when used with lambda expressions. For example, Count can be used to count the items in a list that pass a test:

C# Count

    using System.Linq;

    ...

    var n = items.Count(item => item.Name.Contains("Sharp"));
    

Lambda expressions are also commonly used with C# events, such as in System.Windows.Forms applications.

Rather than subscribing methods to event handlers (which are often hooked up by the Visual Studio designer automatically):

C# subscribing to an event the old way

    button1.Click += button1_Click;

    ...

    void button1_Click(object sender, EventArgs e) {
      // do something here
    }
    

A lambda expression could be used instead:

C# subscribing to an event with a lambda expression

    button1.Click += (sender, eventArgs) {
      // do something here
    };
    

Don’t subscribe to events directly with lambda expressions if the event handler (the lambda function) needs to be unsubscribed from the event later. To do that, you would need to assign the lambda expression to a variable first and then later use the variable to unsubscribe from the event.


System.Threading.Tasks.Task can be used to launch a background task that runs asynchronously on a thread pool and System.Threading.Tasks.Parallel can launch many tasks at once on the thread pool. Lambda expressions are convenient for both.

First, a single background task:

C# Task

    using System.Threading.Tasks;

    ...

    // Start a long-running task.
    var t = Task.Run(
      () => {
        // Perform an operation that takes a long time
        // to complete, simulated with Sleep.
        System.Threading.Thread.Sleep(10000); // 10s
        Console.WriteLine("Task finished.");
      }
    );

    // Do other things.
    Console.WriteLine("Doing other things.");
    
    // Wait for the task to finish.
    t.Wait();
    

Next, a background task is launched for each item in an array:

C# Task

    using System.Threading.Tasks;

    ...

    string[] items = { "These", "are", "some", "items" };

    // Run a lambda expression in parallel on items in a list.
    Parallel.ForEach(
      items,
      (item) => {
        // Perform an operation that takes a long time
        // to complete, simulated with Sleep.
        System.Threading.Thread.Sleep(1000); // 10s
        Console.WriteLine("Task finished for " + item);
      }
    );
    

The lambda expression is run multiple times, possibly simultaneously, for each item in the array, and the order in which the array elements are seen might be unpredictable. The first argument to ForEach can be any IEnumerable container. Unlike Task.Run which returns immediately, Parallel.ForEach waits until all of the loop iterations complete.

Java

See https://ddc-java-10.github.io/2020/04/28/lambdas-key-functional-interfaces/ for some examples.

Javascript

Lambda expressions and anonymous functions are used extensively in Javascript, in two ways:

Asynchronous callbacks are so pervasive in Javascript that I can’t even begin here to provide key examples. The async package and the Promise design concept are the key places to look next.

Python

Across programming languages, lambda functions are commonly used with the language’s list sorting function to create sort orders for user-defined classes for which the language doesn’t provide any built-in ordering. In Python, a lambda expression can be passed to list.sort() and sorted(...) for this purpose.

You might have seen this error when trying to sort user-defined classes:

Python sorting error

    items = [ MyClass("A", 1.0), MyClass("B", 0.5) ]
    items.sort()

    TypeError: '<' not supported between instances
    of 'MyClass' and 'MyClass'
    

MyClass, in this example, is not a sortable data type. You could implement the < operator on MyClass to make it sortable, but often the easier solution is to call list.sort with a lambda expression.

list.sort()’s optional keyword argument key takes a function that takes an item of the list and returns a sortable value to use in place of the item. It can return anything sortable (a number, string, etc.). The items in the list will be sorted according to how their correponding return values from the key function would be sorted amongst each other.

Returning a tuple of sortable values is a convenient way to create a function that sorts on multiple fields. In this example, the user-defined class is sorted first by its name field and then, when there are any instances with the same name, by its value field.

Python sorting with a lambda

    items = [ MyClass("A", 1.0), MyClass("B", 0.5) ]
    items.sort(key = lambda item : (
      item.name,
      item.value
    ))
    

Variable Capture

Besides a lambda expression’s arguments, a lambda expression can also access variables of the outer function that the lambda expression is contained within. This is called capture or closure.

For example:

C++ capture

    auto text = "Hello";
    
    auto f = [&]() {
      cout << text << endl;
    };
    
    f(); // prints "Hello"
    
C#

    var text = "Hello";
 
    Action f = () => {
      Console.WriteLine(text);
    };
 
    f(); // prints "Hello"
    
Java

    String text = "Hello";
    
    Runnable f = () -> {
      System.out.println(text);
    };
    
    f.run(); // prints "Hello"
    
Python

    text = "Hello"

    f = lambda : print(text)

    f() # prints "Hello"
    

In the above examples the captured variable is text. You can tell because it is a variable in the lambda expression that is not an argument (in these examples there are no arguments). The variable type is simply a string, but any data type including objects or other lambda functions can be captured:

C++ capture

    vector<string> text;
    
    auto f = [&](string word) {
      text.push_back(word);
    };
    
    f("Hello");
    f("world.");

    // text holds { "Hello", "world." }
    
C#

    var text = new List<string>();
 
    Action<string> f = (word) => {
      text.Add(word);
    };
 
    f("Hello");
    f("world.");

    // text holds { "Hello", "world." }
    
Java

    import java.util.*;
    import java.util.function.*;

    ...

    List<String> text = new ArrayList<String>();    
 
    Consumer<String> f = (word) -> {
      text.add(word);
    };
 
    f.accept("Hello");
    f.accept("world.");

    // text holds { "Hello", "world." }
    
Python

    text = []

    f = lambda word : text.append(word)

    f("Hello")
    f("world")

    # text holds ["Hello", "world"]
    

If the variable holds an object, that object remains valid — it won’t be garbage-collected or destroyed — until the outer function exits and the lambda function is no longer referenced anywhere.


Lambda expressions are typically small, reusable, and self-contained, but capture makes lambda expressions less reusable and not self-contained, so excessive use of capture should be avoided.

How this works varies subtly across languages, and there are three types of variable capture that you need to be aware of.

Capture by Reference

Capture by reference means that the outer variable is shared with the lambda expression.

If the outer variable changes after the lambda expression is defined but before the lambda function is executed, the lambda function will get the updated value:

C++ capture by reference

    auto text = "Hello";
    
    auto f = [&]() {
        cout << text << endl;
    };

    f(); // prints "Hello"

    text = "world.";
    
    f(); // prints "world."
    
C#

    var text = "Hello";
 
    Action f = () => {
      Console.WriteLine(text);
    };

    f(); // prints "Hello"

    text = "world.";
 
    f(); // prints "world."
    
Javascript

    let text = "Hello";
     
    f = () => { console.log(text) };

    f(); // prints "Hello"
     
    text = "world.";
     
    f(); // prints "world."
    
Python

    text = "Hello"

    f = lambda : print(text)
    
    f() // prints "Hello"

    text = "world."

    f() # prints "world"
    

Conversely, the lambda expression can change the value of the outer variable so that when the lambda function finishes, the outer function sees the updated value:

C++

    auto text = "Hello";
    
    auto f = [&]() {
        text = "world.";
    };
    
    cout << text << endl; // prints "Hello"

    f();
    
    cout << text << endl; // prints "world."
    
C#

    var text = "Hello";

    Action f = () => {
      text = "world.";
    };
 
    Console.WriteLine(text); // prints "Hello"

    f();

    Console.WriteLine(text); // prints "world."
    
Javascript

    let text = "Hello";
     
    f = () => { text = "world." };
     
    f();

    console.log(text); // prints "world."
    

(This is not possible in Python: See the language-specific notes at the end.)


Here’s a complex case. Can you figure out what will be printed?

C#

    var text = new string[] { "Hello", "world." };

    var actions = new List<Action>();
    
    foreach (var t in text) {
      actions.Add( () => {
        Console.WriteLine(t);
      } );
    }

    text = new string[] { "Goodbye", "world." };
    
    actions.ForEach(action => action());
    

In C#, Javascript, and Python, capture is always by reference.

In C++, there is no capture by default. To enable capture by reference, the & symbol can be put in the brackets that start the lambda expression. The brackets are for declaring capture.

When inside non-static methods of a class, the this variable is captured automatically in C#, Java, and Javascript and can be captured by value by adding this to the brackets in C++. That makes all of the current class instance’s fields, properties, methods, etc. available within the lambda expression as well.

In Python and Javascript, the capture rules apply to local function definitions the same as it does to lambda expressions. For example:

Python local function capture

    text = "Hello"

    def f():
        print(text)
        
    text = "world."

    f() # prints "world"
    

In Javascript, use the newer let keyword rather than var to declare variables to avoid for-loop scope mistakes.

Capture by Copy (C++)

Capture by copy means the lambda function gets a copy of the value in the capture variable at the time the lambda expression is defined. It cannot modify the outer variable and does not see changes to the outer variable.

In C++, capture can be either by reference or by copy. Using = in the capture brackets, capture is by copy.

C++ capture by copy

    auto text = "Hello";
    
    auto f = [=]() {
        text = "world."; // compiler error!
    };

    ...
    

Because a copy is made at the point where the lambda expression is declared, it will not see subsequent changes to the variable:

C++

    auto text = "Hello";
    
    auto f = [=]() {
        cout << text << endl;
    };

    text = "world.";
    
    f(); // prints "Hello"
    

Variables captured by copy are const inside the lambda expression to prevent confusion about which variable is being edited:

C++ capture

    vector<string> text;
    
    auto f = [=](string word) {
      text.push_back(word); // compiler error!
    };
    

Capture by copy is less prone to coding mistakes than capture by reference so it should be preferred, but it may come at a cost if the variable holds a complex data type that is expensive to copy.

Capture by copy should generally be used when capturing std::shared_ptr or other smart pointers because the copy will ensure the target object is not destroyed before the lambda function finishes. If that’s not a concern, capture by reference may be more efficient.

(In C#, Java, Javascript, and Python capture is always by reference (or by value in Java), so this section on capture by copy does not apply to those languages.)

Caveats and Capture by Value

It can become very difficult to track what captured variables will hold inside lambda expressions when the variable’s value changes, like in some of the examples in the previous section! It is especially hard in some specific cases:

Avoid using capture by reference in these circumstances whenever possible. especially capturing for-loop variables and variables within loops.

Modifying the captured variable either inside or outside the lambda expression is not possible in Java, which requires that captured variables be final or assigned once, so that it only ever has one value. That’s nice! It prevents complex situations that are prone to error. Since the captured variable cannot be changed, capture by reference is probably not the right term: capture by value might be a more appropriate term.

To get around these issues in all of the languages, you can sometimes make the captured variable a container (e.g. a list) and modify what is inside the container. Although the variable should not (or in Java cannot) be assigned a new value, its methods can be called and its fields and properties can be modified freely.


In addition to the captured variable’s value, it is can also be very hard to track its lifetime. Objects stored in captured variables may remain in memory so long as the lambda function remains stored in a variable. The lifetime of captured variables is dependent on the lifetime of the lambda function. If the lambda function is stored in a global variable, for example, any objects in variables captured by the lambda expression may hold onto system resources indefinitely.

Or, worse, those captured objects may become invalid in some way (due to their own semantics), leading to a crash or error the next time the lambda function is called. Lambda functions must be careful that nothing intervenes between definition and execution that would violate expectations the function has about the captured variables.

The Type of Lambda Expressions

If you are writing your own functions that take lambda functions as arguments in statically typed languages, or if you are using C# ≤9 or Java and want to assign a lambda expression to a variable, you will need to know the correct types to use.

C++

The type for lambda functions in C++ is std::function<return_type(arg_type_1, arg_type_2, ...)> which is defined in the functional standard header.

The template argument return_type(arg_type_1, arg_type_2, ...) is a little unusual, but it makes sense and pretty clearly indicates what’s what.

C#

Three types in C# are generally used with lambda expressions.

System.Action is the usual type for lambda functions that do not return anything (like a void return type function). Without any generic arguments, it is the type for lambda functions that take no arguments and have no return value.

When System.Action has generic arguments, they are the lambda function’s argument types. For example, Action<string> is the type of a lambda function that takes a string and performs an action without returning a value. Action<string,Object> is the type of a lambda function that takes a string and an object as arguments (in that order) and performs an action without returning a value.

System.Func is the usual type for lambda functions that have a return value. The first (zero or more) generic arguments are the argument types. The last generic argument is always the type of the return value of the lambda function. So, for example, Func<string> is the type of a lambda function that takes no arguments and returns a string. Func<Object,string,int> is the type of a lambda function that takes an object and a string as arguments (in that order) and returns integer.

System.Predicate is a special case of Func where the return type is bool. So, for example, Predicate<string> is the type of a lambda function that takes a string argument and returns a boolean. Predicate<Object,string,int> is the type of a lambda function that takes those three argument types and returns a bool.

These types are used throughout the built-in .NET assemblies.

Actually any delegate type can be the type of a lambda expression, but there is generally no practical reason to use any type other than the three above unless the length of the names of these types becomes too cumbersome to type or read in the source code.

The var keyword can only be used with lambda expressions starting in C# 10, and only for some lambda expressions. Otherwise, you must give a full type.

Java

In Java, there is no fixed type that must be used with a lambda expression. The pre-defined interface classes in java.util.function (reference) define some commonly used types, such as Function<T,R> which can be assigned a lambda function that takes a T as an argument and returns an R-type value. The Runnable (reference) interface can be used for lambda expressions that take no arguments and return nothing.

Any interface with a single method (a functional interface type) can be the type of a lambda expression, so long as the method has the same return type and arguments as the lambda expression. See the language reference.

Unfortunately, to invoke the lambda function, you must know the name of the method in the particular functional interface that is being used. Consult the interface documentation for the name of the method used to invoke the lambda function.

Dynamically Typed Languages

Although dynamically typed languages like Javascript and Python still have types at runtime, the types are generally not specified in source code.

Metaprogramming

In C#, lambda expressions can be used for metaprogramming. See System.Linq.Expressions.Expression.

Language-Specific Notes

C++

In C++, capture can also be explicit: The brackets can contain a comma-separated list of variables to capture. Variables not mentioned in the list are not captured and not available to the lambda expression. Capture is by copy by default, unless the variable name is preceded with &. The this variable can also be listed when in a non-static class member function, and it is always captured by reference. (refreence)

C#

Use _ as the name of lambda expression arguments to indicate they are not used, rather than dummy1, dummy2, etc. _ is a lambda discard parameter when it is the name of more than one argument.

Capture can be turned off to avoid accidental capture or if you want to have a variable the same name as a variable in the outer scope by placing the static keyword immediately before the lambda expression.

Lambda expressions can use async.

Java

Nothing yet.

Javascript

Javascript has a longer, older quasi-alternative syntax to lambda expressions (called function expressions or anonymous functions) that looks like this:

Javascript function expression

    function(x, y) {
      if (x > y)
        return x;
      return y;    
    }
    

They are almost equivalent. If you don’t use this, arguments, yield, and a few other rarely used keywords, the two syntaxes are pretty much the same.

As noted previously, the capture rules apply to function expressions the same as they do with lambda expressions.

Python

As noted earlier, the capture rules apply to local function definitions the same as they do with lambda expressions.


Because Python variable scope is determined by the nearest assignment, variables in the outer scope cannot be assigned to:

Python

    text = "Hello"

    def f():
        text = "world."
        
    f()

    print(text) # prints "Hello"
    

The text variable in the inner function is a different variable than the one in the outer funtion because there is an assignment to text in the inner function. A trick is to make the outer variable a contaier:

Python

    text = [ "Hello" ]

    def f():
        text[0] = "world."
        
    f()

    print(text[0]) # prints "world."
    

Although Python capture is always by reference, there is a trick for achieving capture by value: assigning default values to lambda expression arguments.

Python

    text = "Hello"
    
    f = lambda text=text: print(text)

    text = "world.";
    
    f() # prints "Hello"
    

It’s a weird and neat trick.

References