Unpythonic

Functional patterns for the Python maverick

Last updated: Dec 6, 2015

A Spicy Curry

We’ve mentioned currying earlier. Named after Haskell Curry, an esteemed logician whose name is also applied to the Haskell programming language, currying refers to the act of transforming a function of many arguments into a series of functions of one argument.

What?

In a fully-curried language, you can only write functions that accept one argument. If you want to write a two-function argument, you write a chain of functions that you can call with one argument each, with the last call returning a value instead of a function:

def add(x):
    def _inner(y):
        return x + y
    return _inner


add(2)(1)  # => 3

The advantage to this is that we can partially-apply functions on a whim. This example doesn’t do that property any justice, so let’s invent one that does:

user = {
    'address': {
        'city': 'Las Palmas',
        'country': 'Spain'
    }
}

def getter(prop):
    def _inner(d):
        return d.get(prop)
    return _inner

# Get the `city` property from the nested `address` of our `user` object:
get_city = reduce(compose, [getter('city'), getter('address')])
get_city(user)  # => 'Las Palmas'

The downside is that, in a language that’s not designed for it, this is a pretty tedious way to write functions. However, we can simplify any implementation of currying to relax the one-argument restriction, and simply handle multi-argument functions.

A good way to do this in Python is with a decorator. What we’d really rather write is this:

@curried
def getter(prop, d):
    return d.get(prop)

Unfortunately, python doesn’t know how many arguments a function may accept, so we’ll have to relax our dreams a bit and provide this as an argument:

@curried(2)
def getter(prop, d):
    return d.get(prop)

This is totally achievable.

Haskell sensibility, Python machinery

Here is what we’ll need our decorator to do:

  • Store the expected number of arguments
  • When the curried function is called, check the number of arguments against the expectation
  • If fewer arguments are passed, return a freshly-curried function with the arguments partially applied
  • Otherwise, call the function with those arguments

Here’s how that looks:

def curried(n):
    def curry(fn):
        def _inner(*args):
            if len(args) < n:
                return curried(n - len(args))(functools.partial(fn, *args))
            return fn(*args)
        return _inner
    return curry

And here it is in action:

@curried(5)
def returnargs(*args):
    return args

returnargs("a", "b")("c")("d")("q")

How to use curried functions

One of the guiding rules of writing functions that can be usefully curried is to arrange arguments from least to most variable. In getter, we were generally using a single key on different, varying user objects. You can see this in action in the definition of curried itself, which is in effect a curried function implemented manually. The set of possible values for n is smaller than the set of functions that we can curry, which is smaller than the set of arguments that we can apply to those functions.

Ironically, one of the more useful applications for currying in python is to unroll decorators and other function-returning-functions a bit:

import logging

logger = logging.getLogger(__name__)


@curried(3)
def logged(logger, fn, *args):
    logger.info("Calling {} with {}".format(fn, args))
    return fn(*args)


@logged(logger)
def say_hi(name, title=None):
    return "Hello, " + (title or "") + " " + name


say_hi("Harry")  # => "Hi, Harry", but also logs
say_hi("Jane", "President")  # => "Hi, President Jane", but also logs

A pity we didn’t have curried around while we were implementing curried!

Next article: Right in the Monad