Concepts / Passing Functions as Parameters

Passing Functions as Parameters

A language has first-class functions when functions can be treated as data values, for example by assigning them to variables, passing them as parameters, returning them, or storing them in arrays.

  • Programming

Predicting a Program With No Named Function

Most of the functions you have written so far were given a name the moment they were defined. That habit feels natural, but it hides a question: does a function have to have a name to be useful? This article starts from code that does not need one. Read the program below and try to work out its output before you read the explanation that follows.

python

What do you think happens?

What do you think this program prints? Make your prediction before reading on.

  • [1, 2, 3]

  • [1, 4, 9]

  • [1, 2, 3, 1, 4, 9]

  • An error, because the function has no name

Reveal answer
Answer:

[1, 4, 9]

The function apply_to_all receives the unnamed function lambda x: x * x as its first argument. Inside the loop it calls that function on each value, so 1 becomes 1, 2 becomes 4, and 3 becomes 9. The list of results is then returned and printed. The function never received a name, yet the program ran without trouble.

Tracing a Function Value Into Another Function

Passing a function as a parameter works the same way as passing a number, except that the thing passed is behavior. The caller hands over a value. The receiving subroutine then uses that value by calling it. In the example, the lambda is created in the caller's code, travels into apply_to_all as the parameter named func, and is called once for every item in the list. The diagram below follows that flow from the caller into the subroutine and back out as the result.

Caller codebuilds the lambda and thelistapply_to_allreceives it as funcfunc(v) callruns once per itemresults listreturned to callerlambda x: x * xfunction valuepasses lambda as funcfunction valuecalls func(v)appends each output
Where does the function value travel, and which function is responsible for calling it? The lambda starts in the caller, is passed into apply_to_all, and is called from inside the loop there.

The responsibility is clear from the diagram. The caller decides which behavior to supply. The subroutine decides when and how often to run it. Neither side needs to know how the other is written, only that the parameter behaves like a function it can call.

Lambda Notation and Its Parts

The mathematician Alonzo Church studied computable functions and needed a way to talk about a function without fixing a name for it. The Greek letter lambda is the symbol he used, and his notation makes it possible to define a function without giving it a name. Python uses the keyword lambda for the same idea.

The source makes the problem concrete. Writing x squared on its own is an expression that produces a number from a given x. It does not say what x stands for, so it is not yet a function. To turn it into a function, x has to be declared as a parameter. A parameter declared this way is a dummy parameter: a placeholder that is replaced by a real value each time the function is called. One option is to give the function a name, such as square, but that name then becomes a permanent part of the program, which is excessive for a function needed only once. A notation that defines the function without naming it avoids that permanent name.

lambdakeyword opens a functionliteralxdummy parameter:separates parameter frombodyx * xsingle expression givingthe result
Which part of the lambda is the parameter, which is the expression body, and how does the whole literal relate to a single function value? The keyword opens the literal, x is the dummy parameter, the colon separates the parameter from the body, and x * x is the single expression that gives the result.

Read the notation left to right. The lambda keyword announces that a function is being defined. The name after it is the parameter. The colon, which plays the role that the period plays in the mathematical notation lambda (x). x^2, separates the parameter from the expression. The expression that follows is the body, and its value is what the function returns. The whole construction is a function literal: it stands for a function value directly, just as the number 42 is an integer literal that stands for a value of type int.

Definition

A function literal is a notation that represents a value of type function directly, without giving that function a name. In Python, lambda x: x * x is a function literal whose parameter is x and whose body is the expression x * x.

python
Output (expected)
16
16
Notes on the code
  • The first line stores the function literal in a variable called square, so it can be called by name.
  • The second line uses the literal directly, without storing it, which is the unnamed use the notation was designed for.

Following the Calls in the First Program

Now return to the program from the opening section. The sequence diagram below lists the calls in the order they happen. The caller starts apply_to_all with the lambda and the list. The subroutine then calls the lambda once per item, and each call sends back a value. Only when the loop finishes does the list of results go back to the caller.

Callerprint lineapply_to_allloops over the listlambda x: x * xfunction valueapply_to_all(lambda, [1, 2, 3])func(1)returns 1func(2)returns 4func(3)returns 9returns [1, 4, 9]
In what order do the calls happen, and how does the returned value flow back to the caller? The subroutine calls the lambda once per value, each call returns a squared number, and the full list returns to the caller only after the loop ends.

Four Things You Can Do With a Function Value

A language has first-class functions when functions can be treated as data values. The source lists four operations that show this. A function can be assigned to a variable, passed as a parameter to a subroutine, returned as the value of a subroutine, or placed into an array. The examples below show each operation in Python. Each one uses the same idea: the function is a value, so it can go wherever an ordinary value can go.

python
Output (expected)
10
12
14
40
Notes on the code
  • Operation 1: the lambda is stored in the variable double and called by that name.
  • Operation 2: apply_twice receives double as f and calls it twice, so 3 becomes 6 and then 12.
  • Operation 3: make_adder returns a new function value that remembers n; add_ten is that returned function.
  • Operation 4: the list holds two function values, and operations[1] selects the second one by its index before calling it.
Worked example

Tracing apply_twice step by step

What does apply_twice(double, 3) return, where double is the lambda x: x * 2?

  1. 1
    Receive the arguments

    Inside apply_twice, f refers to the lambda x: x * 2 and value is 3.

  2. 2
    Inner call

    f(value) becomes double(3), which returns 6.

  3. 3
    Outer call

    f(6) calls the same lambda again, which returns 12.

  4. 4
    Return

    apply_twice returns 12, which the print statement displays.

Result

12

Mistakes Beginners Make With Passed Functions

  • Common mistake

    Calling the function when passing it

    What it looks like
    apply_to_all(lambda x: x * x(), [1, 2, 3]) or apply_to_all(double(), [1, 2, 3])
    Why it fails

    Writing double() calls the function immediately and passes its return value instead of the function itself. The subroutine then receives a number and fails when it tries to call it.

    Do this instead

    Pass the function value without parentheses, as in apply_to_all(double, [1, 2, 3]). The subroutine adds the parentheses itself when it calls func(v).

  • Common mistake

    Expecting a lambda to contain several statements

    Why it fails

    The source describes a lambda as one expression that gives the function's result. Assignments and multi-line blocks do not fit inside the notation.

    Do this instead

    Keep the body to a single expression. If the logic needs several steps, write a normal named def function and pass its name instead.

  • Common mistake

    Believing an unnamed function cannot be stored

    What it looks like
    operations = [lambda x: x + 1, lambda x: x * 10]
    Why it fails

    Naming is optional, not forbidden. A function literal is still a value, so it can be placed in a list or assigned to a variable.

    Do this instead

    Use the literal directly when it is needed once, and assign it to a name only when the program benefits from referring to it repeatedly.

Practice Tracing an Unnamed Function

EASY

Without running the code, predict the output of this program. Then check your answer by running it. Remember that the function apply_to_all calls func once per item and returns the list of results.

def apply_to_all(func, values):
    results = []
    for v in values:
        results.append(func(v))
    return results

print(apply_to_all(lambda x: x + 3, [2, 5, 7]))

Hints
  • Identify the function value passed in: it is lambda x: x + 3, so func(v) adds 3 to v.
  • Apply the function to each item in order: 2, then 5, then 7.
  • The result is a list containing 5, 8, and 10 in that order.

Key Takeaways

  • A language has first-class functions when functions are treated as data values that can be assigned to variables, passed as parameters, returned from subroutines, and stored in arrays.
  • Lambda notation defines a function without a name: the keyword lambda, a dummy parameter, a colon, and a single expression that gives the result. The whole construction is a function literal.
  • When a function is passed as a parameter, the caller supplies the behavior and the receiving subroutine decides when and how often to call it.
  • Pass the function itself, not its result. Writing a function name without parentheses passes the value; adding parentheses calls it.