Welcome, guest | Sign In | My Account | Store | Cart
1

In functional programming, currying is a way to bind arguments with a function and wait for the rest of the arguments to show up later. You "curry in" the first few parameters to a function, giving you a function that takes subsequent parameters as input and calls the original with all of those parameters. This recipe uses a class instance to hold the parameters before their first use. For example:

double = curry(operator.mul, 2)
triple = curry(operator.mul, 3)
Python, 14 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class curry:
    def __init__(self, fun, *args, **kwargs):
        self.fun = fun
        self.pending = args[:]
        self.kwargs = kwargs.copy()

    def __call__(self, *args, **kwargs):
        if kwargs and self.kwargs:
            kw = self.kwargs.copy()
            kw.update(kwargs)
        else:
            kw = kwargs or self.kwargs

        return self.fun(*(self.pending + args), **kw)

A typical use of curry is to construct callback functions for GUI operations. When the operation does not really merit a new function name, curry can be useful in creating these little functions. This can be the case with commands for buttons, for example.

self.button = Button(frame, text='A', command=curry(transcript.append, 'A'))

Curry can also be used interactively by making versions of your functions with debugging-appropriate defaults or initial parameters filled in for your current case. For example, database debugging work might well begin by setting:

Connect = curry(ODBC.Connect, dsn='MyDataSet')

If you are creating a function for regular use, and there is a good choice for a name, the 'def fun(...' form of function definition is usually more readable, and often more easily extended. As you can see from the implementation, no magic happens to "specialize" the function with the provided parameters; curry should be used when you feel the code is more clear with its use than without. Typically this will be to emphasize that you are only providing parameters to a "commonly used" (in this application) function, not providing separate processing.

10 comments

Nick Perkins 15 years, 2 months ago  # | flag

'Lightweight' subclasses. This also works very well for creating a sort of lightwieght subclass..

ie. you can curry the constructor of a class to give the illusion of a subclass as follows:

bluewindow = curry(window, bg='blue')

bw = bluewindow()

..of course type(bluewindow) is still type(window), ( not a sub-type )

Additional parameters can still be passed to the curried constructor:

bw2 = bluewindow( title='blah', fg='yellow')

curry is cool, and not just for callbacks!

Alex Martelli 15 years, 1 month ago  # | flag

curry-by-closure. Lexically nested scopes (in Python 2.2 -- "from __future__ import" if you want to use them in 2.1) allow interesting currying too, e.g.:

def curry(func, *args, **kwds):
    def callit(*moreargs, **morekwds):
        kw = kwds.copy()
        kw.update(morekwds)
        return func(*(moreargs+args), **kw)
    return callit

This curries positional arguments from the right, and gives named arguments specified at call-time precedence over those specified at currying-time, but these policies are clearly easy to alter:-).

_Without_ nested scopes, I don't think it can be done with this level of generality (injecting names into the inner scope becomes hard to do generally when one wants fully-general args and *kwds...:-).

Alex

Scott David Daniels (author) 15 years, 1 month ago  # | flag

Using curry to wrap debugging information around calls. The 'curry' function can be used in debugging as well. For example, we can "wrap" method calls in objects:

def report(originalFunction, name, *args, **kw):
  print name + '(', ', '.join(map(repr,args) +
                    [k+'='+repr(kw[k]) for k in kw.keys()]), ')'
  result = originalFunction(*args, **kw)
  if result: print name, '==>', result
  return result

class Sink:
  def write(self, text):
    pass

dest = Sink()
dest.write = curry(report, dest.write, 'write')
print >>dest, 'this', 'is', 1, 'test'
Alex Martelli 15 years, 1 month ago  # | flag

alternative curry-by-closure. Nick Perkins' latest version of curry-by-closure (from c.l.p, for the record) is more general (no named args at all, thus no accidental name capture -- except, I think, for args and create_time_kwds...) and quite readable, although marginally more verbose, due to good name choices for intermediate locals. It also has curry-from-left (for positional args) and call-time-dominates (for named args) semantics, which may be popular:

def curry(*args, **create_time_kwds):
    func = args[0]
    create_time_args = args[1:]
    def curried_function(*call_time_args, **call_time_kwds):
        args = create_time_args + call_time_args
        kwds = create_time_kwds.copy()
        kwds.update(call_time_kwds)
        return func(*args, **kwds)
    return curried_function
Tracy Ruggles 13 years, 8 months ago  # | flag

Enhancement: the original function's __doc__ string... Why not also add this before returning the curried function:

curried_function.__doc__ = "Curried function '%s':\n%s" % (
    func.func_name, func.func_doc
    )
David Abrahams 12 years, 5 months ago  # | flag

This isn't currying. The operation performed by the operator described here is actually called "Partial Application". A good definition of currying can be inferred from http://tinyurl.com/3d6w6 and http://tinyurl.com/ly29.

Several languages have misused the term "curry" this way

 http://tinyurl.com/2p6gb, http://tinyurl.com/2onya (Python)

 http://tinyurl.com/36pus (JavaScript)

 http://tinyurl.com/3dj6j (Dylan)

Currying transforms a function taking a single tuple argument into a function taking multiple arguments, and uncurrying reverses the process.

>>> def curry(f):
...     return lambda *args: f(args)
...
>>> def add2(args): # add the first 2 elements of an argument tuple
...     return args[0] + args[1]
...
>>> curry(add2)(1,2) # make add2 accept multiple args
3
>>> def uncurry(f):
...     return lambda x: f(*x)
...
>>> uncurry(curry(add2))((1,2)) # reverse currying; pass a tuple
3
Scott David Daniels (author) 11 years, 6 months ago  # | flag

Current curry code. I'd eliminate some micro-optimizations today and avoid spurious copies. Also, to allow named parameters such as 'self' to be specified in keywords, _no_ directly named args are provided.

class curry:
    def __init__(*args, **kwargs):
        self = args[0]
        self.fun = args[1]
        self.pending = args[2:]
        self.kwargs = kwargs

    def __call__(*args, **kwargs):
        self = args[0]
        kw = self.kwargs.copy()
        kw.update(kwargs)
        return self.fun(*self.pending + args, **kw)

The above works back through _many_ versions of python (though you might have to use "apply" to call self.fun if your Python is old). "Nested scopes" became a feature after this code was written. If I were to write it now, I might write:

def curry(*args, **kwargs):
    function, args = args[0], args[1:]
    def result(*rest, **kwrest):
        combined = kwargs.copy()
        combined.update(kwrest)
        return function(*args + rest, **combined)
    return result

Finally, putting the micro-optimizations (and one new one) back in:

import new

def curry(*args, **kwargs):
    function, args = args[0], args[1:]
    if args and kwargs:
        def result(*rest, **kwrest):
            combined = kwargs.copy()
            combined.update(kwrest)
            return function(*args + rest, **combined)
    elif args:
        if len(args) > 1:
            def result(*rest, **kwrest):
                return function(*args + rest, **kwrest)
        else:
            # Special magic: make a bound object method on the arg
            return new.instancemethod(function, args[0], object)
    elif kwargs:
        def result(*rest, **kwrest):
            if kwrest:
                combined = kwargs.copy()
                combined.update(kwrest)
            else:
                combined = kwargs
            return function(*rest, **combined)
    else:
        return function
    return result
Scott David Daniels (author) 10 years, 6 months ago  # | flag

Warning: special case. Change needed above. In the code above, bonono recently pointed out that instancemethod will not do what I want with a value of None, so the code above should be changed from:

...
elif args:
    if len(args) > 1:
...

to:

...
elif args:
    if len(args) > 1 or args[0] is None:
...
Marko Mikulicic 10 years, 4 months ago  # | flag

Curryng methods. I've found that using the closure you cannot apply curry to instance methods and classmethods using:

class X(object):
   def test(self, arg):
      ..
   curried = curry(test, 10)
...

However with the addition of the decorator protocol to the class based implementation

class curry(object):
    ....
    ....
    def __get__(self, obj, typ):
        """
        This method is an addition to the python cook book receipe.

        It allows partial function to be applied to method and classmethods
        """
        return curry(self.fn.__get__(obj, typ), *self.args, **self.kw)

perhaps it could be done better but it works (even with classmethod)

blokeley 6 years, 3 months ago  # | flag

Python 2.5+ has a function in the standard library to do this. See http://docs.python.org/library/functools.html#functools.partial

Add a comment

Sign in to comment