This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Add a curry function to the functools module
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.3
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: alex, collinwinter, eric.araujo, ezio.melotti, gregory_p, markonervo, petri.lehtinen, pitrou, rhettinger, serprex
Priority: normal Keywords:

Created on 2011-11-18 16:01 by markonervo, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Messages (15)
msg147882 - (view) Author: Marko Nervo (markonervo) Date: 2011-11-18 16:01
I think it would be very usefull to add a curry function to the functools module. It should be like this.


def curry(func, *args, **kwargs):

    if (len(args) + len(kwargs)) >= func.__code__.co_argcount:

        return func(*args, **kwargs)

    return (lambda *x, **y: curry(func, *(args + x), **dict(kwargs, **y)))


This function allows you to create curried functions or methods in two main ways:


(1)


>>> def adder(x, y, z):
...     return (x + y + z)
>>> adder = curry(adder)


(2)


>>> @curry
... def adder(x, y, z):
...     return (x + y + z)


Curried functions could be used as follow.


>>> adder(2, 3, 4)
>>> adder(2, 3)(4)
>>> adder(2)(3)(4)
>>> adder(z = 4)(2, 3)  # etc, etc, etc...


Best regards,
Marco.
msg147885 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2011-11-18 16:14
This sounds similar to http://wiki.python.org/moin/PythonDecoratorLibrary#Pseudo-currying

Do you have a concrete use case for this?
msg147886 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2011-11-18 16:15
You can use functools.partial for similar effect. Not sure what a dedicated curry() primitive would improve.
msg147887 - (view) Author: Alex Gaynor (alex) * (Python committer) Date: 2011-11-18 16:16
This already exists, as functools.partial: http://docs.python.org/library/functools.html#functools.partial
msg147897 - (view) Author: Marko Nervo (markonervo) Date: 2011-11-18 16:59
In [1]: import functools

In [2]: def adder(x, y, z):
   ...:     return (x + y + z)
   ...: 

In [3]: adder = functools.partial(adder)

In [4]: adder(2)(3)(4)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/marko/<ipython console> in <module>()

TypeError: adder() takes exactly 3 arguments (1 given)


No, it can't be replaced using functools.partial.
msg147901 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-11-18 17:03
To go back to your original message:

> I think it would be very usefull to add a curry function to the functools module.

For what use cases?

> Curried functions could be used as follow.
>>> adder(2, 3, 4)
>>> adder(2, 3)(4)
>>> adder(2)(3)(4)
>>> adder(z = 4)(2, 3)  # etc, etc, etc...

I don’t know the curry concept from other languages, but I’m experienced with Python and don’t like this idea at all.  A function returns something; the returned value being callable or not is a property of that value, not of the function.  The examples above look confusing to me.

I think you should discuss your use cases on the python-ideas mailing list.
msg147904 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2011-11-18 17:08
FWIW there is a somewhat related thread that proposed a new syntax for curried functions: http://mail.python.org/pipermail/python-ideas/2009-March/003220.html
msg147911 - (view) Author: Marko Nervo (markonervo) Date: 2011-11-18 18:02
I totally disagree with the syntax for curried function, but I think a curry function is perfect for the functools module and I also think it could be useful at least as functools.partial. In addition, a lot of languages support currying, such as Haskell, Scala, Javascript...

However, here an example less confusional

>>> adder = curry(lambda (x, y): (x + y))
>>> adder3 = adder(3)
>>> adder3(4)
7
>>> adder3(5)
8

Currying let you defining new functions on other functions. This is a common practice in functional programming (point-free style). So, why not?
msg147912 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2011-11-18 18:07
> However, here an example less confusional
> 
> >>> adder = curry(lambda (x, y): (x + y))
> >>> adder3 = adder(3)
> >>> adder3(4)
> 7
> >>> adder3(5)
> 8
> 
> Currying let you defining new functions on other functions.

But so does functools.partial. So the question is, what use case does it
help that functools.partial doesn't?
msg147913 - (view) Author: Ezio Melotti (ezio.melotti) * (Python committer) Date: 2011-11-18 18:09
In that thread Guido said:
"""
Haskell has this too, perhaps even more extreme: there's not really
such a thing in Haskell as a function of N arguments (N > 1). "f a b =
..." defines a function f of one argument a which returns another
function ("f a") of one argument b. And so on.

That doesn't mean we need to copy this idea in Python.
"""

New features require a valid use case before being included, and I'm afraid "why not?" is not valid :)
(Also nothing prevents you to define your own curry function/decorator and use it where you need it.)
msg147918 - (view) Author: Marko Nervo (markonervo) Date: 2011-11-18 19:38
> But so does functools.partial. So the question is, what use case does it
> help that functools.partial doesn't?

Sure, it's common `defining new functions on other functions`... more times. Here a stupid example with fold (our reduce).


@curry
def fold(function, start, sequence):
    if len(sequence) == 0:
        return start
    else:
        return fold(function, function(start, sequence[0]), sequence[1:])


Now, someone could be define a generic summer function by fold.


import operator as op

summer = fold(op.add)


Now, an other programmer could be use summer for defining listsummer (a function which sum only list), as follow.


listsummer = summer([])


In addition, curry is cleaver than functools.partial. 


summer = functools.partial(fold, op.add)
listsummer = functools.partial(summer, [])


However, it is an additional feature. Nobody forces you to use it, but if you need it... Yeah, you could rewrite it each time, but why? It is perfect in functools (:
msg147919 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2011-11-18 19:43
> Sure, it's common `defining new functions on other functions`... more
> times. Here a stupid example with fold (our reduce).
> 
> @curry
> def fold(function, start, sequence):
>     if len(sequence) == 0:
>         return start
>     else:
>         return fold(function, function(start, sequence[0]), sequence[1:])
> 
> Now, someone could be define a generic summer function by fold.
> 
> import operator as op
> 
> summer = fold(op.add)

Right... so you defined these helper functions (curry, fold) just to...
define a generic summer function? Really?

I understand that fold() and curry() may look pretty to functional
languages people, but they don't solve any real-world problems that
Python doesn't already solve in a neater way. You will have to try a bit
harder and showcase examples of *useful* code that are made
significantly easier through the use of curry().

(no, generic summer functions are *not* real-world use cases. They are
homework exercises for CS students, at best)
msg147921 - (view) Author: Marko Nervo (markonervo) Date: 2011-11-18 20:20
> You will have to try a bit
> harder and showcase examples of *useful* code that are made
> significantly easier through the use of curry().

Curry is a more advanced functools.partial. So, it could be used *at least* as partial, but it is more powerfull, usable and readable. I think it's a valid reason; if it isn't, I haven't anything else to say.
msg147939 - (view) Author: Petri Lehtinen (petri.lehtinen) * (Python committer) Date: 2011-11-19 12:25
@markonervo: Have you tried the pointfree library: http://pypi.python.org/pypi/pointfree/ ? I think it's exactly what you're asking for.

The only case I've ever needed currying was managing callback soup in async code. And even then, functools.partial() was perfectly enough.

I don't think this would be useful in Python stdlib, so -1 from me.
msg147975 - (view) Author: Collin Winter (collinwinter) * (Python committer) Date: 2011-11-20 03:56
I assume I was added to this thread since I wrote the functional module, so I'll give my take in that capacity. IMO Python doesn't need a more general version of partial(); indeed, I question the need for partial() as it is today. Querying Google Code Search for code using partial, I haven't found any usages in the wild where partial() makes code more readable than simply defining a new function. partial() is almost always a loss for readability.
History
Date User Action Args
2022-04-11 14:57:23adminsetgithub: 57639
2011-11-20 03:56:20collinwintersetmessages: + msg147975
2011-11-19 12:25:15petri.lehtinensetnosy: + petri.lehtinen
messages: + msg147939
2011-11-18 20:20:21markonervosetmessages: + msg147921
2011-11-18 19:43:33pitrousetmessages: + msg147919
2011-11-18 19:38:14markonervosetmessages: + msg147918
2011-11-18 18:09:21ezio.melottisetmessages: + msg147913
2011-11-18 18:07:39pitrousetmessages: + msg147912
2011-11-18 18:02:54markonervosetmessages: + msg147911
2011-11-18 17:08:04ezio.melottisetmessages: + msg147904
stage: resolved
2011-11-18 17:03:04eric.araujosetmessages: + msg147901
2011-11-18 16:59:41markonervosetmessages: + msg147897
2011-11-18 16:16:17alexsetstatus: open -> closed

nosy: + alex
messages: + msg147887

resolution: not a bug
2011-11-18 16:15:36pitrousetnosy: + pitrou
messages: + msg147886
2011-11-18 16:14:01ezio.melottisetnosy: + ezio.melotti

messages: + msg147885
versions: + Python 3.3, - Python 2.7, Python 3.4
2011-11-18 16:01:33markonervocreate