update from Peter Harris, plus spell-check & edit

This commit is contained in:
David Goodger 2003-03-11 04:49:44 +00:00
parent 9ddde8f432
commit 49afb661db
1 changed files with 61 additions and 41 deletions

View File

@ -8,25 +8,26 @@ Type: Standards Track
Content-Type: text/x-rst Content-Type: text/x-rst
Created: 08-Feb-2003 Created: 08-Feb-2003
Python-Version: 2.4 Python-Version: 2.4
Post-History: 10-Feb-2003 Post-History: 10-Feb-2003, 27-Feb-2003
Abstract Abstract
========= ========
This proposal is for a built-in closure or curry type for Python that This proposal is for a standard curry type for Python that
allows a new callable to be constructed from another callable and a allows a new callable to be constructed from a callable and a
partial argument list (including positional and keyword arguments). A partial argument list (including positional and keyword arguments).
concise syntax shorthand for curried functions is suggested
(tentatively).
Note: after feedback on comp.lang.python, I am persuaded that the most 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 accurate term for this is a 'curry' rather than a 'closure', so the
amended since the first version of this PEP. 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 Motivation
=========== ==========
Curried functions are useful as functional 'sections' or as convenient Curried functions are useful as functional 'sections' or as convenient
anonymous functions for use as callbacks. anonymous functions for use as callbacks.
@ -49,7 +50,7 @@ We need something better.
Rationale Rationale
========== =========
Here is one way to do a curry in Python:: Here is one way to do a curry in Python::
@ -59,8 +60,11 @@ Here is one way to do a curry in Python::
self.fn, self.args, self.kw = (fn, args, kw) self.fn, self.args, self.kw = (fn, args, kw)
def __call__(self, *args, **kw): def __call__(self, *args, **kw):
d = self.kw.copy() if self.kw:
d.update(kw) d = self.kw.copy()
d.update(kw)
else:
d = kw
return self.fn(*(self.args + args), **d) return self.fn(*(self.args + args), **d)
Note that when the curry is called, positional arguments are Note that when the curry is called, positional arguments are
@ -78,17 +82,16 @@ Update: a recipe almost exactly like this has been in the Python
Cookbook for quite some time, at Cookbook for quite some time, at
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549. 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.
Tentative Syntax Proposal
==========================
I know syntax proposals have the odds stacked against them, and Abandoned Syntax Proposal
introducing new punctuation characters is frowned upon, but I think =========================
curries may be a sufficiently powerful abstraction to deserve it.
I suggest the syntax ``fn@(*args, **kw)``, meaning the same as I originally suggested the syntax ``fn@(*args, **kw)``, meaning the same
``curry(fn, *args, **kw)``. I have no idea what havoc this would as ``curry(fn, *args, **kw)``.
wreak on the parser.
At least there are no backwards-compatibility issues because the @ At least there are no backwards-compatibility issues because the @
character isn't a legal operator in any previous versions of Python. character isn't a legal operator in any previous versions of Python.
@ -119,9 +122,12 @@ Convenience functions ::
nextarg = sys.argv.pop@(0) 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 Feedback from comp.lang.python
=============================== ==============================
Among the opinions voiced were the following (which I summarise): Among the opinions voiced were the following (which I summarise):
@ -136,7 +142,15 @@ Among the opinions voiced were the following (which I summarise):
* A curry class would indeed be a useful addition to the standard * A curry class would indeed be a useful addition to the standard
library. library.
* It maybe isn't useful enough to be in the builtins. * 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 I agree that lambda is usually good enough, just not always. And I
want the possibility of useful introspection and subclassing. want the possibility of useful introspection and subclassing.
@ -151,13 +165,6 @@ dead parrot.
I concur with calling the class curry rather than closure, so I have I concur with calling the class curry rather than closure, so I have
amended this PEP accordingly. amended this PEP accordingly.
I think it's best as a builtin type rather than in a separate standard
library module, because it's simple and general enough. It may not be
an idiom that is very common in Python programming at the moment, but
I think that's because you have to code it yourself if you want it.
If added as a built-in feature, we would soon be wondering how we
managed without it.
Carl Banks posted an implementation as a real functional closure:: Carl Banks posted an implementation as a real functional closure::
def curry(fn, *cargs, **ckwargs): def curry(fn, *cargs, **ckwargs):
@ -176,32 +183,45 @@ than a built-in curry class.
I also coded the class in Pyrex:: I also coded the class in Pyrex::
cdef class curry: cdef class curry:
cdef object fn, args, kw cdef object fn, args, kw
def __init__(self, fn, *args, **kw): def __init__(self, fn, *args, **kw):
self.fn=fn self.fn=fn
self.args=args self.args=args
self.kw = kw self.kw = kw
def __call__(self, *args, **kw): def __call__(self, *args, **kw):
if self.kw: # from Python Cookbook version if self.kw: # from Python Cookbook version
d = self.kw.copy() d = self.kw.copy()
d.update(kw) d.update(kw)
else: else:
d=kw d=kw
return self.fn(*(self.args + args), **d) return self.fn(*(self.args + args), **d)
but I'm guessing that there would be minimal performance improvement The performance gain in Pyrex is less than 100% over the nested function
since it compiles to a load of Python API calls. 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 Summary
======== =======
I maintain that curry should be a built-in, with the semantics as I prefer that curry should be a built-in, with the semantics as
described, whether as a function or a class. 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. 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 Copyright
========= =========