Currying decorator in python
The below implementation is naive, google for "currying python" for more accurate examples.
def curry(x, argc=None):
if argc is None:
argc = x.func_code.co_argcount
def p(*a):
if len(a) == argc:
return x(*a)
def q(*b):
return x(*(a + b))
return curry(q, argc - len(a))
return p
@curry
def myfun(a,b,c):
print '%d-%d-%d' % (a,b,c)
myfun(11,22,33)
myfun(44,55)(66)
myfun(77)(88)(99)
This one is fairly simple and doesn't use inspect or examine the given function's args
import functools
def curried(func):
"""A decorator that curries the given function.
@curried
def a(b, c):
return (b, c)
a(c=1)(2) # returns (2, 1)
"""
@functools.wraps(func)
def _curried(*args, **kwargs):
return functools.partial(func, *args, **kwargs)
return _curried
Many of the answers here fail to address the fact that a curried function should only take one argument.
A quote from Wikipedia:
In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument (partial application).
Choosing to decorate it with recursion and without co_argcount
makes for a decently elegant solution.
from functools import partial, wraps, reduce
def curry(f):
@wraps(f)
def _(arg):
try:
return f(arg)
except TypeError:
return curry(wraps(f)(partial(f, arg)))
return _
def uncurry(f):
@wraps(f)
def _(*args):
return reduce(lambda x, y: x(y), args, f)
return _
As shown above, it is also fairly trivial to write an uncurry
decorator. :) Unfortunately, the resulting uncurried function will allow any number of arguments instead of requiring a specific number of arguments, as may not be true of the original function, so it is not a true inverse of curry
. The true inverse in this case would actually be something like unwrap
, but it would require curry
to use functools.wraps
or something similar that sets a __wrapped__
attribute for each newly created function:
def unwrap(f):
try:
return unwrap(f.__wrapped__)
except AttributeError:
return f
The source code for curry
in the toolz
library is available at the following link.
https://github.com/pytoolz/toolz/blob/master/toolz/functoolz.py
It handles args, kwargs, builtin functions, and error handling. It even wraps the docstrings back onto the curried object.