Skip to content

Instantly share code, notes, and snippets.

@JulienPalard
Created August 1, 2014 10:51
Show Gist options
  • Star 39 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save JulienPalard/021f1c7332507d6a494b to your computer and use it in GitHub Desktop.
Save JulienPalard/021f1c7332507d6a494b to your computer and use it in GitHub Desktop.
KISS Python curry
#!/usr/bin/env python
def curry(func):
"""
Decorator to curry a function, typical usage:
>>> @curry
... def foo(a, b, c):
... return a + b + c
The function still work normally:
>>> foo(1, 2, 3)
6
And in various curried forms:
>>> foo(1)(2, 3)
6
>>> foo(1)(2)(3)
6
This also work with named arguments:
>>> foo(a=1)(b=2)(c=3)
6
>>> foo(b=1)(c=2)(a=3)
6
>>> foo(a=1, b=2)(c=3)
6
>>> foo(a=1)(b=2, c=3)
6
And you may also change your mind on named arguments,
But I don't know why you may want to do that:
>>> foo(a=1, b=0)(b=2, c=3)
6
Finally, if you give more parameters than expected, the exception
is the expected one, not some garbage produced by the currying
mechanism:
>>> foo(1, 2)(3, 4)
Traceback (most recent call last):
...
TypeError: foo() takes exactly 3 arguments (4 given)
"""
def curried(*args, **kwargs):
if len(args) + len(kwargs) >= func.__code__.co_argcount:
return func(*args, **kwargs)
return (lambda *args2, **kwargs2:
curried(*(args + args2), **dict(kwargs, **kwargs2)))
return curried
if __name__ == "__main__":
import doctest
doctest.testmod()
@blurrcat
Copy link

blurrcat commented Dec 5, 2016

Fantastic! Thanks!

You may use functools.wraps to make the intermediate curried functions look like the original function.

from functools import wraps

def curry(func):
    """
    >>> @curry
    ... def foo(a, b, c):
    ...     return a + b + c
    >>> foo(1)
    <function __main__.foo>
    """
    @wraps(func)
    def curried(*args, **kwargs):
        if len(args) + len(kwargs) >= func.__code__.co_argcount:
            return func(*args, **kwargs)

        @wraps(func)
        def new_curried(*args2, **kwargs2):
            return curried(*(args + args2), **dict(kwargs, **kwargs2))

        return new_curried

    return curried

Now instead of showing <function __main__.curry.<locals>.curried.<locals>.<lambda>> for foo(1), we have the expected representation of the function. This can make debugging easier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment