Partial application and currying
Currying, known in Python land as partial application, is a technique in which a function taking multiple arguments composes a function that takes fewer arguments (in most languages, reducing to one, although this is not the case in Python) by partially applying it to given parameters. For example, a function, sum, might be used to compose a new function called “plus_one” by currying it with the value of one. The composed function is not evaluated; it is returned as a function object which may then be applied to other parameters.
Python’s partial() is contained in the functools module (included since Python 2.5, I believe). The first argument passed must be the function to be curried, and the rest are positional or keyword arguments that will be used to curry the passed function.
A common case where partials are useful is in defining a compare function for a list. It often happens that the list must be sorted according to rules defined at runtime. A partial application can simplify the process, especially if there is a complex sort algorithm. Assume a list of Items, items. The list will be sorted according to get_sort_attribute(), which returns the name of the attribute of Item which will be used to perform the sort, and get_sort_direction, which returns either “desc” or “asc.” Rather than using a long series of if/else statements and calling the sort() method in various ways, partial() can be used to progressively modify the sort.
To sort a list this way, we might have something like:
(def compare(a, b): attr = get_sort_attribute() dir = get_sort_direction() value_a = getattr(a, attr) value_b = getattr(b, attr) if dir == 'desc': return cmp(value_b, value_a) else: return cmp(value_a, value_b) items.sort(compare)
This can get pretty long-winded, especially if our function, compare, performs complex operations before performing the comparison. Here is the same thing using partial applications to compose our function instead:
from functools import partial # Wrap getattr in a lambda so that can accept keyword arguments attribute_getter = partial(lambda object, name: getattr(object, name), name=get_sort_attribute()) sort_fn = partial(items.sort, key=attribute_getter) if get_sort_direction() == 'desc': sort_fn = partial(sort_fn, reversed=True) sort_fn()
This is certainly more concise. I found a nice little currying function for Javascript here (edit: it was pointed out in a reader’s comment (here and at dzone) that this version of curry does not work on previously curried functions; below it is a modified version which will function as expected):
/* function curry(fn, scope) { var scope = scope || window; var args = []; for (var i=2, len = arguments.length; i < len; ++i) { args.push(arguments[i]); }; return function() { /* one big problem here is that the following statement is not returning the applied function. */ fn.apply(scope, args); }; } */ function curry(fn, scope) { var scope = scope || window; var args = []; for (var i = 2; i < arguments.length; i++) { args.push(arguments[i]); } return function() { // this takes care of the arguments problem var fn_args = []; for (var i = 0; i < args.length; i++) { fn_args.push(args[i]); } for (var i = 0; i < arguments.length; i++) { fn_args.push(arguments[i]); } // this takes care of the null return problem return fn.apply(scope, fn_args); }; }
I often use this in my Django web projects. I will include a basic error function which inserts an error message into an element with a particular id on the base template. Note that I use jQuery in the following examples.
function err(target, msg) { $('#' + target).html(msg); }
In templates that extend that template, I can then use currying to modify that for a particular location defined in this template:
var err = curry(err, window, 'err_div_in_this_template');
I can further use this to create custom error callbacks for ajax functions:
var err404 = curry(err, window, "The server could not be contacted.");
Now, err404 is the equivalent of:
function err404() { $('#' + 'err_div_in_this_template').html("The server could not be contacted."); }
Obviously, this is a pretty trivial example, but it does a good job of showing a real-world use for currying.