Lambdas

Dear Computer

Chapter 8: Functions Revisited

Lambdas

Sometimes we need to create a function for the sole purpose of passing it off to a higher-order function like map or filter or foldr or foldl. We could define the helper function at the top-level, as twice is here:

Haskell
-- Helper that doubles a single number.
twice :: Int -> Int
twice x = 2 * x

twiceAll :: [Int] -> [Int]
twiceAll xs = map twice xs

This is not ideal if twice is not needed for anything else. The definition has a wider scope than necessary. Defining the helper in a where clause narrows its scope:

Haskell
twiceAll :: [Int] -> [Int]
twiceAll xs = map twice xs
  where
    twice :: Int -> Int
    twice x = 2 * x

But there's an even better solution. Many languages provide lambdas as an alternative means of constructing short-lived functions with narrow scope and without a lot of fanfare. Lambdas are a lot like the blocks we used in Ruby in that they strip a function down to just its essentials: a parameter list and a body. Gone is the name. Gone is the noisy syntax. In Haskell, a lambda that doubles a number looks like this:

Haskell
\x -> 2 * x

In grammatical terms, a lambda is a backslash, a space-separated parameter list, an arrow, and an expression:

Haskell
\param1 param2 param3 ... -> expression

This lambda reports if its parameter is the empty list or a list with one element:

Haskell
\xs -> null xs || length xs == 1

This lambda reports if a number is divisible by 5:

Haskell
\x -> mod x 5 == 0

This lambda reports if two numbers are within 3 of each other:

Haskell
\x y -> abs (x - y) <= 3

Pattern matching is legal in the list of formal parameters. This lambda swaps the components of a 2-tuple:

Haskell
\(a, b) -> (b, a)

We could assign these lambdas to identifiers and then call them as we call any function:

Haskell
twice = \x -> 2 * x
swap = \(a, b) -> (b, a)

twice 5      -- yields 10
swap (1, 2)  -- yields (2, 1)

But assigning them to variables in the current scope is not usually what we do. If we wanted a named function, we would have used the named-function syntax. Instead, lambdas are usually anonymous. They are firecrackers, used for their brief moment of glory and then thrown away.

Suppose we have a list of numbers, and we want to keep only the numbers in the interval [13, 19]. We might define this teens function, which calls filter with a lambda predicate:

Haskell
teens :: [Int] -> [Int]
teens xs = filter (\x -> 13 <= x && x <= 19) xs

Parentheses are needed around the lambda to separate it from surrounding tokens.

Suppose we have a list of number pairs, and we want to turn it into a list of the larger numbers within the pairs. We might define this biggers function, which calls map with a lambda transform:

Haskell
biggers :: [(Int, Int)] -> [Int]
biggers xs = map (\(a, b) -> max a b) xs

biggers [(1, 2), (10, 5)]  -- yields [2, 10]

Suppose we have a list of words and we want to pull out their first letters to create an acronym. We might define this acronym function, which calls foldr with a lambda mixing function:

Haskell
acronym :: [String] -> String
acronym words = foldr (\x accum -> head x : accum) "" words

acronym ["higher", "order", "function"]  -- yields "hof"

Many other languages support lambdas. JavaScript has syntax similar to Haskell, but uses => instead of ->:

JavaScript
const afters = [1, 2, 3].map(x => x + 1);
console.log(afters);      // prints [2, 3, 4]
const positives = [-10, 0, 10].filter(x => x > 0);
console.log(positives);   // prints [10]

Java 8 introduced lambdas. These provide an abbreviated syntax for creating anonymous classes that have only a single method. Prior to lambdas, we might have registered a callback on a button with this clumsy code:

Java
button.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent event) {
    window.close();
  };
});

A lambda simplifies this code considerably:

Java
button.addActionListener(event -> window.close());

The Java compiler internally translates the lambda into an instance of ActionListener and inserts the lambda's expression as the body of the actionPerformed method.

Unlike Haskell, both Java and JavaScript allow the body to be a block of multiple statements instead of a single expression. If a block is used, it must be surrounded by curly braces and have an explicit return statement when returning a value.

← Higher-order FunctionsClosures →