update from Peter Harris, with editorial tweaks
This commit is contained in:
parent
ef58cb550d
commit
2c9fa04926
140
pep-0309.txt
140
pep-0309.txt
|
@ -1,5 +1,5 @@
|
|||
PEP: 309
|
||||
Title: Built-in Closure Type
|
||||
Title: Built-in Curry Type
|
||||
Version: $Revision$
|
||||
Last-Modified: $Date$
|
||||
Author: Peter Harris <scav@blueyonder.co.uk>
|
||||
|
@ -8,34 +8,39 @@ Type: Standards Track
|
|||
Content-Type: text/x-rst
|
||||
Created: 08-Feb-2003
|
||||
Python-Version: 2.4
|
||||
Post-History:
|
||||
Post-History: 10-Feb-2003
|
||||
|
||||
|
||||
Abstract
|
||||
=========
|
||||
|
||||
This proposal is for a built-in closure type for Python that allows a
|
||||
new callable to be constructed from another callable and a partial
|
||||
argument list (including positional and keyword arguments). A concise
|
||||
syntax shorthand for closures is suggested (tentatively).
|
||||
This proposal is for a built-in closure or curry type for Python that
|
||||
allows a new callable to be constructed from another callable and a
|
||||
partial argument list (including positional and keyword arguments). A
|
||||
concise syntax shorthand for curried functions is suggested
|
||||
(tentatively).
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Motivation
|
||||
===========
|
||||
|
||||
Closures are useful as functional 'sections' or as convenient
|
||||
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)``.
|
||||
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 closures cannot be
|
||||
implicit in the same way. Instead of using them, a Python programmer
|
||||
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.
|
||||
|
@ -46,9 +51,9 @@ We need something better.
|
|||
Rationale
|
||||
==========
|
||||
|
||||
Here is one way to do closures in Python::
|
||||
Here is one way to do a curry in Python::
|
||||
|
||||
class closure(object):
|
||||
class curry(object):
|
||||
|
||||
def __init__(self, fn, *args, **kw):
|
||||
self.fn, self.args, self.kw = (fn, args, kw)
|
||||
|
@ -58,27 +63,31 @@ Here is one way to do closures in Python::
|
|||
d.update(kw)
|
||||
return self.fn(*(self.args + args), **d)
|
||||
|
||||
Note that when the closure is called, positional arguments are
|
||||
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 ``closure(operator.add,1)`` is a bit like ``(lambda x: 1+x)``, and
|
||||
``closure(Tkinter.Label,fg='blue')`` is a callable like the Tkinter
|
||||
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 ``closure``, that behaves the same way
|
||||
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.
|
||||
|
||||
|
||||
Tentative Syntax Proposal
|
||||
==========================
|
||||
|
||||
I know syntax proposals have the odds stacked against them, and
|
||||
introducing new punctuation characters is frowned upon, but I think
|
||||
closures are a sufficiently powerful abstraction to deserve it.
|
||||
curries may be a sufficiently powerful abstraction to deserve it.
|
||||
|
||||
I propose the syntax ``fn@(*args,**kw)``, meaning the same as
|
||||
``closure(fn,*args,**kw)``. I have no idea what havoc this would
|
||||
I suggest the syntax ``fn@(*args, **kw)``, meaning the same as
|
||||
``curry(fn, *args, **kw)``. I have no idea what havoc this would
|
||||
wreak on the parser.
|
||||
|
||||
At least there are no backwards-compatibility issues because the @
|
||||
|
@ -89,6 +98,9 @@ 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
|
||||
---------------
|
||||
|
@ -99,15 +111,98 @@ Using closures as callbacks with bound arguments::
|
|||
#whatever...
|
||||
|
||||
button1 = Button(window, text="Action A",
|
||||
command=handler@('A','1'))
|
||||
command=handler@('A', '1'))
|
||||
button2 = Button(window, text="Action B",
|
||||
command=handler@('B','2',opt=1))
|
||||
command=handler@('B', '2', opt=1))
|
||||
|
||||
Convenience functions ::
|
||||
|
||||
nextarg = sys.argv.pop@(0)
|
||||
|
||||
|
||||
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 builtins.
|
||||
|
||||
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.
|
||||
|
||||
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::
|
||||
|
||||
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)
|
||||
|
||||
but I'm guessing that there would be minimal performance improvement
|
||||
since it compiles to a load of Python API calls.
|
||||
|
||||
|
||||
Summary
|
||||
========
|
||||
|
||||
I maintain that curry should be a built-in, with the semantics as
|
||||
described, whether as a function or a class.
|
||||
|
||||
The @ syntax proposal is withdrawn.
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
|
@ -122,4 +217,3 @@ This document has been placed in the public domain.
|
|||
sentence-end-double-space: t
|
||||
fill-column: 70
|
||||
End:
|
||||
|
||||
|
|
Loading…
Reference in New Issue