Last week we introduced higher-order functions. Particularly, how to deal with functions as parameters. On this post, we will see how to return a function from a function as well as an example.
Higher-order functions: motivation
As a reminder, a higher-order function is a function that either takes another function as a parameter or that returns another function as its result. To understand its usefulness, imagine it wasn’t possible to create higher-order functions. That would impose a limitation on the expressive power of the programming language.
On the other hand, being able to define higher-order functions allows us to create abstractions that otherwise wouldn’t be possible. We’ll see a simple example below. For now, we can just keep in mind that higher-order functions allow for greater expressive power.
Its importance is such that, in the past, programming languages that allowed to define higher-order functions were especially distinguished. On these programming languages, it was recognized that functions were treated as first-class citizens. Which meant that they had the following “privileges”:
1. They may be named by variables.SICP
2. They may be passed as arguments to procedures.
3. They may be returned as the results of procedures.
4. They may be included in data structures.
See that we defined higher-order functions precisely as satisfying points 2. and 3. (understand “procedure” as “function” on the quotation above).
Defining higher-order functions
In this section, we’ll see how to define a function that returns another function in Python. We’ll see two variations.
Method 1: Helper function
The first way to do it will be by including a function definition inside the body of the main function. Then we return this function.
def main_function(...): # Main function body def helper_function(...): # helper function code # Main function code return helper_function
helper_function is defined inside the body of
helper_function. Syntactically, there isn’t anything very special when defining a higher-order function. We return a function as we would do for any regular variable.
Let’s see an example.
Imagine that we have a simple function that returns the sum of two values
a + b. We could create a regular function that accepts arguments a and b and returns their sum. Instead, we’ll create a function that, given
a, will create a function that will accept one parameter and add
def create_adder(a): def add(b): return a + b return add
create_adder returns the function
add which accepts one argument (
add will make use of the argument
a passed initially to
>>> add1 = create_adder(1) >>> add1(4) 5
See that in order to use the function returned by
create_adder, we assign it to
add1. Then we can call
add1 one numerical argument.
Alternatively, we could immediately call the function returned by
>>> create_adder(1)(4) 5
The first argument,
1, is passed to
create_adder. The second argument,
4, is passed to the function returned by
Method 2: Anonymous functions (lambda)
We can see lambda expressions in Python as a syntactic sugar to define functions. The name “lambda” is derived from lambda calculus, which gave a mathematical basis to computational models and influenced several programming languages.
The basic syntax is:
lambda a1, a2, ..., an: <expression>
lambda is a special keyword, just as
def is. Both are used to define functions.
a1, a2, etc are the function parameters. Each parameter is separated by commas. The “body” of the lambda function comes after the colons
:. In practice,
<expression> corresponds to the
return statement of a “normal” function definition. This imposes a limitation on lambda functions to a single line.
Let’s see a “normal” function definition and its equivalent using lambda functions.
def normal_function(x): return x + 1 lambda_function = lambda x: x + 1
lambda_function are equivalent.
>>> normal_function(1) 2 >>> lambda_function(1) 2
Lambda expressions are mostly used in functional programming languages. You can see an example of recursive lambda functions in Scheme here.
In Python, its use is acceptable for the definition of very simple functions (as syntactic sugar). For more complex functions, lambda expressions are disfavored and the use of “normal” function definitions are encouraged. While lambda definitions offer more flexibility, using them excessively can make the code harder to understand.
Coming back to the example we used above, the definition of an adder function, we can change its definition using lambda expressions as follows:
def lambda_adder(a): return lambda b: a + b
We can compare this definition with the example above. This is when the advantages of lambda expressions become apparent. We have reduced the definition of our higher-order procedure to a single line!
Its utilization is similar to the one we had above:
>>> add1 = lambda_adder(1) >>> add1(3) 4 >>> add1(-1) 0
With this, we have finished our introduction to higher-order procedures. On future posts, we’ll see its application on more elaborate examples.