[Python-checkins] python/nondist/peps pep-0309.txt,1.1,1.2
goodger@users.sourceforge.net
goodger@users.sourceforge.net
Tue, 18 Feb 2003 16:46:53 -0800
Update of /cvsroot/python/python/nondist/peps
In directory sc8-pr-cvs1:/tmp/cvs-serv9196
Modified Files:
pep-0309.txt
Log Message:
update from Peter Harris, with editorial tweaks
Index: pep-0309.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/peps/pep-0309.txt,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** pep-0309.txt 10 Feb 2003 14:51:45 -0000 1.1
--- pep-0309.txt 19 Feb 2003 00:46:51 -0000 1.2
***************
*** 1,4 ****
PEP: 309
! Title: Built-in Closure Type
Version: $Revision$
Last-Modified: $Date$
--- 1,4 ----
PEP: 309
! Title: Built-in Curry Type
Version: $Revision$
Last-Modified: $Date$
***************
*** 9,13 ****
Created: 08-Feb-2003
Python-Version: 2.4
! Post-History:
--- 9,13 ----
Created: 08-Feb-2003
Python-Version: 2.4
! Post-History: 10-Feb-2003
***************
*** 15,22 ****
=========
! 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).
--- 15,27 ----
=========
! 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.
***************
*** 24,33 ****
===========
! Closures 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
--- 29,38 ----
===========
! 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
***************
*** 35,40 ****
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
will probably either define another named function or use a lambda.
But lambda syntax is horrible, especially when you want to do
--- 40,45 ----
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
***************
*** 47,53 ****
==========
! Here is one way to do closures in Python::
! class closure(object):
def __init__(self, fn, *args, **kw):
--- 52,58 ----
==========
! Here is one way to do a curry in Python::
! class curry(object):
def __init__(self, fn, *args, **kw):
***************
*** 59,73 ****
return self.fn(*(self.args + args), **d)
! Note that when the closure 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
Label class, but with a blue foreground by default.
! I think a built-in type called ``closure``, that behaves the same way
but maybe implemented more efficiently, would be very useful.
Tentative Syntax Proposal
--- 64,82 ----
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.
+
Tentative Syntax Proposal
***************
*** 76,83 ****
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.
! I propose the syntax ``fn@(*args,**kw)``, meaning the same as
! ``closure(fn,*args,**kw)``. I have no idea what havoc this would
wreak on the parser.
--- 85,92 ----
I know syntax proposals have the odds stacked against them, and
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
! ``curry(fn, *args, **kw)``. I have no idea what havoc this would
wreak on the parser.
***************
*** 90,93 ****
--- 99,105 ----
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
***************
*** 100,106 ****
button1 = Button(window, text="Action A",
! command=handler@('A','1'))
button2 = Button(window, text="Action B",
! command=handler@('B','2',opt=1))
Convenience functions ::
--- 112,118 ----
button1 = Button(window, text="Action A",
! command=handler@('A', '1'))
button2 = Button(window, text="Action B",
! command=handler@('B', '2', opt=1))
Convenience functions ::
***************
*** 109,112 ****
--- 121,207 ----
+ 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
=========
***************
*** 123,125 ****
fill-column: 70
End:
-
--- 218,219 ----