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.
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.
What do you think happens?
What do you think this program prints? Make your prediction before reading on.
Reveal 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.
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.
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.
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.
16
16- 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.
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.
10
12
14
40- Operation 1: the lambda is stored in the variable double and called by that name.
- Operation 2:
apply_twicereceives double as f and calls it twice, so 3 becomes 6 and then 12. - Operation 3:
make_adderreturns a new function value that remembers n;add_tenis that returned function. - Operation 4: the list holds two function values, and operations[1] selects the second one by its index before calling it.
Tracing apply_twice step by step
What does apply_twice(double, 3) return, where double is the lambda x: x * 2?
- 1Receive the arguments
Inside
apply_twice, f refers to the lambda x: x * 2 and value is 3. - 2Inner call
f(value) becomes
double(3), which returns 6. - 3Outer call
f(6) calls the same lambda again, which returns 12.
- 4Return
apply_twicereturns 12, which the print statement displays.
12
Mistakes Beginners Make With Passed Functions
Common mistake Calling the function when passing it
What it looks likeapply_to_all(lambda x: x * x(), [1, 2, 3]) or apply_to_all(double(), [1, 2, 3])Why it failsWriting
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 insteadPass 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 failsThe 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 insteadKeep 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 likeoperations = [lambda x: x + 1, lambda x: x * 10]Why it failsNaming 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 insteadUse 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
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 resultsprint(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.