[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 ----