PEP: 309 Title: Built-in Curry Type Version: $Revision$ Last-Modified: $Date$ Author: Peter Harris Status: Draft Type: Standards Track Content-Type: text/x-rst Created: 08-Feb-2003 Python-Version: 2.4 Post-History: 10-Feb-2003, 27-Feb-2003 Abstract ======== This proposal is for a standard curry type for Python that allows a new callable to be constructed from a callable and a partial argument list (including positional and keyword arguments). Note: after feedback on comp.lang.python, I am persuaded that the most accurate term for this is a 'curry' rather than a 'closure', so the terminology has been amended since the first version of this PEP. I propose a standard library module called "functional", to hold useful higher-order functions, including the curry() class. Motivation ========== Curried functions are useful as functional 'sections' or as convenient anonymous functions for use as callbacks. In some functional languages, (e.g. Miranda) you can use an expression such as ``(+1)`` to mean the equivalent of Python's ``(lambda x: x + 1)``. In general, languages like that are strongly typed, so the compiler always knows the number of arguments expected and can do the right thing when presented with a functor and less arguments than expected. Python has more flexible argument-passing, and so curries cannot be implicit in the same way. Instead of using them, a Python programmer will probably either define another named function or use a lambda. But lambda syntax is horrible, especially when you want to do something complex. We need something better. Rationale ========= Here is one way to do a curry in Python:: class curry(object): def __init__(self, fn, *args, **kw): self.fn, self.args, self.kw = (fn, args, kw) def __call__(self, *args, **kw): if self.kw: d = self.kw.copy() d.update(kw) else: d = kw return self.fn(*(self.args + args), **d) Note that when the curry is called, positional arguments are appended to those provided to the constructor, and keyword arguments override and augment those provided to the constructor. So ``curry(operator.add, 1)`` is a bit like ``(lambda x: 1 + x)``, and ``curry(Tkinter.Label, fg='blue')`` is a callable like the Tkinter Label class, but with a blue foreground by default. I think a built-in type called ``curry``, that behaves the same way but maybe implemented more efficiently, would be very useful. Update: a recipe almost exactly like this has been in the Python Cookbook for quite some time, at http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549. Update: It seems likely that a standard library implementation would be in Python, and would have to prove its worth there before making it into the built-ins. Abandoned Syntax Proposal ========================= I originally suggested the syntax ``fn@(*args, **kw)``, meaning the same as ``curry(fn, *args, **kw)``. At least there are no backwards-compatibility issues because the @ character isn't a legal operator in any previous versions of Python. The @ sign is used in some assembly languages to imply register indirection, and the use here is also a kind of indirection. ``f@(x)`` is not ``f(x)`` , but a thing that becomes ``f(x)`` when you call it. (The only other connection I can see with curry is that @ looks a bit like a section through a mushroom pakora.) Examples of Use --------------- Using closures as callbacks with bound arguments:: def handler(arg1, arg2, opt=0): #whatever... button1 = Button(window, text="Action A", command=handler@('A', '1')) button2 = Button(window, text="Action B", command=handler@('B', '2', opt=1)) Convenience functions :: nextarg = sys.argv.pop@(0) It has not been well-received, so I am not pursuing this as a serious proposal. Feedback from comp.lang.python ============================== Among the opinions voiced were the following (which I summarise): * Lambda is good enough. * The @ syntax is ugly (so far, unanimous). * It's really a curry rather than a closure. There is an almost identical implementation of a curry class on ActiveState's Python Cookbook. * A curry class would indeed be a useful addition to the standard library. * It maybe isn't useful enough to be in the built-ins. * The idea of a module called ``functional`` was well received, and there are other things that belong there (for example function composition). * For completeness, another curry class that appends curried arguments after those supplied in the function call (maybe called ``rightcurry``) has been suggested. I agree that lambda is usually good enough, just not always. And I want the possibility of useful introspection and subclassing. I disagree that @ is particularly ugly, but it may be that I'm just weird. We have dictionary, list and tuple literals neatly differentiated by special punctuation -- a way of directly expressing curried function literals is not such a stretch. However, not one single person has said they like it, so as far as I'm concerned it's a dead parrot. I concur with calling the class curry rather than closure, so I have amended this PEP accordingly. Carl Banks posted an implementation as a real functional closure:: def curry(fn, *cargs, **ckwargs): def call_fn(*fargs, **fkwargs): d = ckwargs.copy() d.update(fkwargs) return fn(*(cargs + fargs), **d) return call_fn which he assures me is more efficient. All you lose with this implementation is introspection and sub-classing. These are only marginal benefits and not worth a performance hit, so this would also do as a reference implementation of a built-in curry function rather than a built-in curry class. I also coded the class in Pyrex:: cdef class curry: cdef object fn, args, kw def __init__(self, fn, *args, **kw): self.fn=fn self.args=args self.kw = kw def __call__(self, *args, **kw): if self.kw: # from Python Cookbook version d = self.kw.copy() d.update(kw) else: d=kw return self.fn(*(self.args + args), **d) The performance gain in Pyrex is less than 100% over the nested function implementation, since to be fully general it has to operate by Python API calls. Any C implementation will be unlikely to be much faster, so the case for a builtin coded in C is not very strong. Summary ======= I prefer that curry should be a built-in, with the semantics as described, whether as a function or a class. However, it should do its apprenticeship in the standard library first. The standard library module ``functional`` should contain ``curry`` and ``rightcurry`` classes, and any other higher-order functions the community want. These other functions fall outside this PEP though. The @ syntax proposal is withdrawn. Since this proposal is now much less ambitious, I'd like to aim for inclusion in Python 2.3. Copyright ========= This document has been placed in the public domain. .. Local Variables: mode: indented-text indent-tabs-mode: nil sentence-end-double-space: t fill-column: 70 End: