238 lines
7.3 KiB
Plaintext
238 lines
7.3 KiB
Plaintext
PEP: 309
|
||
Title: Function Currying
|
||
Version: $Revision$
|
||
Last-Modified: $Date$
|
||
Author: Peter Harris <scav@blueyonder.co.uk>
|
||
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 curry constructor 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', 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 function currying cannot
|
||
be implicit in the same way. Instead, a Python programmer
|
||
will probably either define another named function or use a lambda.
|
||
But lambda syntax is not to everyone's taste, to say the least.
|
||
|
||
We need something better.
|
||
|
||
|
||
Rationale
|
||
=========
|
||
|
||
Here is one way to do a create a curried callable in Python. The
|
||
implementation below is based on improvements provided by Scott David
|
||
Daniels::
|
||
|
||
class curry(object):
|
||
|
||
def __init__(*args, **kw):
|
||
self = args[0]
|
||
self.fn, self.args, self.kw = (args[1], args[2:], kw)
|
||
|
||
def __call__(self, *args, **kw):
|
||
if kw and self.kw:
|
||
d = self.kw.copy()
|
||
d.update(kw)
|
||
else:
|
||
d = kw or self.kw
|
||
return self.fn(*(self.args + args), **d)
|
||
|
||
Note that when the curried function 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 class called ``curry`` that behaves the same way
|
||
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 withdraw this part of the
|
||
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. For the same reason, a C implementation will be unlikely to be much
|
||
faster, so the case for a built-in coded in C is not very strong.
|
||
|
||
|
||
Summary
|
||
=======
|
||
|
||
I prefer that some means to curry functions should be a built-in, with the
|
||
semantics as described, whether as a function or a callable 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. Other functions that might belong there fall outside this PEP though.
|
||
|
||
The @ syntax proposal is withdrawn.
|
||
|
||
|
||
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:
|