Self-currying functions

Dave Benjamin ramen at lackingtalent.com
Wed May 21 03:06:17 EDT 2003


I am impressed by the way that functional languages like O'Caml, Haskell,
and Lisp create self-currying functions. By self-currying, I mean that the
normal behavior of a function when given insufficient parameters is to
return a function with the same behavior, bound to the parameters given,
that takes the remaining parameters. This can be accomplished in Python
(with nested_scopes) like this:

>>> add = lambda x: lambda y: x + y
>>> add(2)(3)
5
>>> add(2)
<function <lambda> ...
>>> map(add(2), range(3))
[2, 3, 4]

This is a neat trick, I thought, but people are going to think you're very
odd if you insist on writing all of your functions as nested lambda
expressions. I wanted a function I could apply to existing, "normal" Python
functions to simply alter its behavior to take each parameter one at a time.
I tried writing one, but I couldn't figure out a way to avoid using "eval".
I was wondering if anyone here could do it. Here's what I got:

def curried(func, nargs=None):
    if nargs is None: nargs = func.func_code.co_argcount

    params  = ['x%d' % n for n in range(nargs)]
    lambdas = 'lambda %s, *args, **kwds' % ': lambda '.join(params)
    args    = '%s, *args, **kwds' % ', '.join(params)
    code    = '%s: func(%s)' % (lambdas, args)

    return eval(code, {'func': func})

Now, I can make existing functions self-currying like this:

>>> from curried import curried
>>> divide = lambda x, y: x / y
>>> divide(64, 4) # just a normal function
16
>>> divide = curried(divide)
>>> map(divide(64), range(1, 10))
[64, 32, 21, 16, 12, 10, 9, 8, 7]

If additional parameters or keyword arguments are specified in the final
invocation, they will be passed on to the curried function.

For built-in functions, the number of arguments the function normally takes
must be specified as the second parameter to curried(), since they don't
have func_* attributes:

>>> import operator
>>> mul = curried(operator.mul)
Traceback (most recent backtrace innerlast):
  ...
AttributeError: 'builtin_function_or_method' object has no attribute
'func_code'
>>> mul = curried(operator.mul, 2)
>>> mul(2)(3)
6
>>> for func in map(mul, range(3)):
...     print func(4)
...
0
4
8

If we add a "print code" before the return statement in curried(), we can
see the underlying function that's being created:

>>> curried(lambda x, y, z: x + y * z)
lambda x0: lambda x1: lambda x2: *args, **kwds:
func(x0, x1, x2, *args, **kwds)

The above nested lambda expression is what curried() creates and then
eval's. I'd like to do this without the need for an eval, but I am having
trouble building a for-loop to accomplish this. The nested scopes just
aren't nested enough, or my brain just doesn't want to wrap around the
problem. Can anyone help me figure this out?

Thanks. =)
Dave





More information about the Python-list mailing list