We explore the idea of currying and of curried functions and look to see how Python can be used to implement this important FP concept.
Our instructor Jack Card talks about using currying and curried functions in Python.
During a recent Python course, a delegate asked whether Python could support a Functional Programming (FP) style. The answer of course is ‘yes’, but the delegate was unconvinced claiming that Python could not support core FP concepts and ideas. When challenged regarding what they meant they identified Currying (or the ability to curry functions) to be one of the key requirements to qualify a language as an FP language.
Currying is a technique which allows new functions to be created from existing functions by binding one or more parameters to a specific value. It is a major source of function reuse in Python which means that functionality can be written once and then reused in multiple other situations. The name Currying may seem obscure, but the technique is named after Haskell Curry for whom the Haskell programming language is also named.
In this blog we are going to explore the idea of currying and of curried functions and look to see how Python can be used to implement this important FP concept.
To illustrate the ideas behind Currying, consider having a function written in pseudo code that takes two parameters. These two parameters x and y are used within the function body with the multiply operator in the form x * y. For example, we might have:
define function operation(x, y) begin return x * y
end This function operation() might then be used as follows
total = call operation(2, 5) Which would result in 5 being multiplied by 2 to give 10.
If we needed to double a number, we could thus reuse the operation() function many times, for example: call operation(2, 5)
call operation(2, 10) call operation(2, 6)
call operation(2, 151)
All of the above would double the second number. Although we have had to remember to provide the value 2 so that the number can be doubled. However, the number 2 has not changed between any of the executions of the operation() function. What if we fixed the first parameter to always be 2. Let us assume we could do this in some way that allowed us to create a new function that took only one parameter and automatically supplied the value 2. For example, again in pseudo code we might write something like:
double = define function using operation(2, *) where the ‘*’ indicates the parameter to be supplied. We could now call the new double function as follows:
call double(5) call double(151)
In essence double() is an alias for operation(), but an alias that provides the value 2 for the first parameter and leaves the second parameter to be filled in when the double function is invoked. This is the concept behind Currying.
Python and Curried Functions
Python does not have an explicit syntax for Currying functions; but it does provide infrastructure that allows Curried functions to be created.
For example, let us assume that we now have a Python function multiply() that takes two parameters and returns the result of multiplying them together:
def multiply(a, b): return a * b
We can thus invoke it in the normal way: print(multiply(2, 5))
The result of executing this statement is the following print out:
We could now define a new function called builder().
The builder function will that take two parameters, the first parameter (func) will represent a function to be called and the second parameter (num) will represent a number. The builder function will then return a new lambda (anonymous) function. This lambda function will have a number of features to it:
This is illustrated below:
def builder(func, num): return lambda y: func(num, y)
The builder() function can now be used to bind the first parameter of the multiply() function to anything we want. For example, we could bind it to 2 so that it will always double the second parameter and store the resulting function reference into a variable double: double = builder(multiply, 2)
We could also bind the value 3 to the first parameter of multiple to make a function that will triple any value: triple = builder(multiply, 3)
Which means we can now write: print(double(5))
which produces the output
You are not limited to just binding one parameter; you can bind any number of parameters in this way. Curried functions are therefore very useful for creating new functions from existing functions and thereby promoting code reuse.
Currying and Closures
One question that the above example might pose is ‘How do the parameters func and num become available to the lambda when it is executed, as they are only in scope (exist) within the builder() function?’. The answer to this question is linked to the implementation of a concept known as closure.
Within programming languages a closure (also known as a lexical closure or function closure) is a function together with a referencing environment. This referencing environment records the context within which the function was originally defined and, if necessary, a reference to each of the non-local variables used by that function. These non-local or free variables allow the function body to reference variables that are external to the function, but which are utilised by that function. This referencing environment is one of the distinguishing features between a functional language and a language that supports function pointers.
At the conceptual level, a closure allows a function to reference a variable available in the scope where the function was originally defined, but not available by default in the scope where it is executed.
For example, consider the following code snippet:
def builder(): addition = 1
return lambda num: num + addition increment = builder()
In the above listing the builder() function has a local variable called addition. Normally the variable addition would only be available within the function builder(). The variable addition is however, used within the function body of a new lambda function definition. This function takes a number and adds the value of addition to that number and returns this as the result of the function.
When the builder() function is invoked it returns a reference to the new created lambda function which is saved into the variable increment. Increment can now be used to execute the anonymous function using the ().
When we subsequently execute the function passing in the value 5, the lambda function will add 1 to the value 5 and return the result 6. The value of the addition local variable has thus been captured or closed into the function definition.
If this blog article has been of interest, you might like to take a look at our Python training courses ranging from introduction to advanced topics.