Self-currying functions

Carl Banks imbosol at aerojockey.com
Wed May 21 03:56:30 EDT 2003


Dave Benjamin wrote:
> 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


Here you go (first try):

    # selfcurry.py

    from __future__ import nested_scopes

    def curry(f,leftargs):
        def curriedfunction(*args):
            return f(*(leftargs+args))
        return curriedfunction

    def selfcurry(f, nargs=None):
        if nargs is None:
            nargs = f.func_code.co_argcount
        def callifenoughargs(*args):
            if len(args) < nargs:
                return selfcurry(curry(f,args),nargs-len(args))
            else:
                return f(*args)
        return callifenoughargs


Not extensively tested, but it worked for me in simple cases.

>>> import selfcurry   
>>> def pr(a,b,c,d): print a,b,c,d
>>> prs = selfcurry.selfcurry(pr)
>>> prs(1,2,3,4)
1 2 3 4
>>> onemore = prs(1,2,3)
>>> onemore(4)
1 2 3 4
>>> x1 = prs(1)
>>> x2 = x1(2) 
>>> x3 = x2(3)
>>> x4 = x3(4)
1 2 3 4



-- 
CARL BANKS




More information about the Python-list mailing list