update from Peter Harris
This commit is contained in:
parent
336b800bf3
commit
c0529bfcca
197
pep-0309.txt
197
pep-0309.txt
|
@ -1,5 +1,5 @@
|
|||
PEP: 309
|
||||
Title: Function Currying
|
||||
Title: Partial Function Application
|
||||
Version: $Revision$
|
||||
Last-Modified: $Date$
|
||||
Author: Peter Harris <scav@blueyonder.co.uk>
|
||||
|
@ -8,29 +8,40 @@ Type: Standards Track
|
|||
Content-Type: text/x-rst
|
||||
Created: 08-Feb-2003
|
||||
Python-Version: 2.4
|
||||
Post-History: 10-Feb-2003, 27-Feb-2003
|
||||
Post-History: 10-Feb-2003, 27-Feb-2003, 22-Feb-2004
|
||||
|
||||
|
||||
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.
|
||||
This proposal is for a function or callable class that allows a new
|
||||
callable to be constructed from a callable and a partial argument list
|
||||
(including positional and keyword arguments).
|
||||
|
||||
I propose a standard library module called "functional", to hold useful
|
||||
higher-order functions, including the curry() class.
|
||||
higher-order functions, including the implementation of partial().
|
||||
|
||||
|
||||
Motivation
|
||||
==========
|
||||
|
||||
Curried functions are useful as functional 'sections' or as convenient
|
||||
anonymous functions for use as callbacks.
|
||||
In functional programming, function currying is a way of implementing
|
||||
multi-argument functions in terms of single-argument functions. A
|
||||
function with N arguments is really a function with 1 argument that
|
||||
returns another function taking (N-1) arguments. Function application
|
||||
in languages like Haskell and ML works such that a function call::
|
||||
|
||||
f x y z
|
||||
|
||||
actually means::
|
||||
|
||||
(((f x) y) z)
|
||||
|
||||
This would be only an obscure theoretical issue except that in actual
|
||||
programming it turns out to be very useful. Expressing a function in
|
||||
terms of partial application of arguments to another function can be
|
||||
both elegant and powerful, and in functional languages it is heavily
|
||||
used.
|
||||
|
||||
In some functional languages, (e.g. Miranda) you can use an expression
|
||||
such as ``(+1)`` to mean the equivalent of Python's
|
||||
|
@ -40,22 +51,24 @@ 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.
|
||||
Python does not implement multi-argument functions by currying, so if
|
||||
you want a function with partially-applied arguments you would probably
|
||||
use a lambda as above, or define a named function for each instance.
|
||||
|
||||
We need something better.
|
||||
However, lambda syntax is not to everyone's taste, so say the least.
|
||||
Furthermore, Python's flexible parameter passing using both positional
|
||||
and keyword presents an opportunity to generalise the idea of partial
|
||||
application and do things that lambda cannot.
|
||||
|
||||
|
||||
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::
|
||||
Here is one way to do a create a callable with partially-applied
|
||||
arguments in Python. The implementation below is based on improvements
|
||||
provided by Scott David Daniels::
|
||||
|
||||
class curry(object):
|
||||
class partial(object):
|
||||
|
||||
def __init__(*args, **kw):
|
||||
self = args[0]
|
||||
|
@ -69,31 +82,52 @@ Daniels::
|
|||
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.
|
||||
(A recipe similar to this has been in the Python Cookbook for some
|
||||
time [1]_.)
|
||||
|
||||
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.
|
||||
Note that when the object is called as though it were a function,
|
||||
positional arguments are appended to those provided to the
|
||||
constructor, and keyword arguments override and augment those provided
|
||||
to the constructor.
|
||||
|
||||
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.
|
||||
Examples of Use
|
||||
===============
|
||||
|
||||
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.
|
||||
So ``partial(operator.add, 1)`` is a bit like ``(lambda x: 1 + x)``.
|
||||
Not an example where you see the benefits, of course.
|
||||
|
||||
Note too, that you could wrap a class in the same way, since
|
||||
classes themselves are callable factories for objects. So in some cases,
|
||||
rather than defining a subclass, you can specialise classes by partial
|
||||
application of the arguments to the constructor.
|
||||
|
||||
For example, ``partial(Tkinter.Label, fg='blue')`` makes Tkinter
|
||||
Labels that have a blue foreground by default.
|
||||
|
||||
Here's a simple example that uses partial application to construct
|
||||
callbacks for Tkinter widgets on the fly::
|
||||
|
||||
from Tkinter import Tk, Canvas, Button
|
||||
import sys
|
||||
from functional import partial
|
||||
|
||||
win = Tk()
|
||||
c = Canvas(win,width=200,height=50)
|
||||
c.pack()
|
||||
|
||||
for colour in sys.argv[1:]:
|
||||
b = Button(win, text=colour, command=partial(c.config,bg=colour))
|
||||
b.pack(side='left')
|
||||
|
||||
win.mainloop()
|
||||
|
||||
|
||||
Abandoned Syntax Proposal
|
||||
=========================
|
||||
|
||||
I originally suggested the syntax ``fn@(*args, **kw)``, meaning the same
|
||||
as ``curry(fn, *args, **kw)``.
|
||||
I originally suggested the syntax ``fn@(*args, **kw)``, meaning the
|
||||
same as ``partial(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.
|
||||
|
@ -103,39 +137,18 @@ 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
|
||||
It has not been well-received, so I have withdrawn this part of the
|
||||
proposal.
|
||||
|
||||
|
||||
Feedback from comp.lang.python
|
||||
==============================
|
||||
Feedback from comp.lang.python and python-dev
|
||||
=============================================
|
||||
|
||||
Among the opinions voiced were the following (which I summarise):
|
||||
|
||||
* Lambda is good enough.
|
||||
|
||||
* The @ syntax is ugly (so far, unanimous).
|
||||
* The @ syntax is ugly (unanimous).
|
||||
|
||||
* It's really a curry rather than a closure. There is an almost
|
||||
identical implementation of a curry class on ActiveState's Python
|
||||
|
@ -144,13 +157,16 @@ Among the opinions voiced were the following (which I summarise):
|
|||
* A curry class would indeed be a useful addition to the standard
|
||||
library.
|
||||
|
||||
* It isn't function currying, but partial application. Hence the
|
||||
name is now proposed to be partial().
|
||||
|
||||
* 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
|
||||
* For completeness, another object that appends partial arguments
|
||||
after those supplied in the function call (maybe called
|
||||
``rightcurry``) has been suggested.
|
||||
|
||||
|
@ -160,12 +176,19 @@ 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.
|
||||
partially-applied 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 concur with calling the class partial rather than curry or closure,
|
||||
so I have amended the proposal in this PEP accordingly. But not
|
||||
throughout: some incorrect references to 'curry' have been left in
|
||||
since that's where the discussion was at the time.
|
||||
|
||||
Partially applying arguments from the right, or inserting arguments at
|
||||
arbitrary positions creates its own problems, but pending discovery of
|
||||
a good implementation and non-confusing semantics, I don't think it
|
||||
should be ruled out.
|
||||
|
||||
Carl Banks posted an implementation as a real functional closure::
|
||||
|
||||
|
@ -176,11 +199,10 @@ Carl Banks posted an implementation as a real functional closure::
|
|||
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.
|
||||
which he assures me is more efficient. You lose introspection and
|
||||
sub-classing that way, but these are maybe only marginal benefits and
|
||||
not worth a performance hit, so this would also do as a reference
|
||||
implementation.
|
||||
|
||||
I also coded the class in Pyrex::
|
||||
|
||||
|
@ -201,24 +223,31 @@ I also coded the class in Pyrex::
|
|||
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.
|
||||
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.
|
||||
I prefer that some means to partially-apply functions and other
|
||||
callables should be present in the standard library.
|
||||
|
||||
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.
|
||||
A standard library module ``functional`` should contain an
|
||||
implementation of ``partial``, and any other higher-order functions
|
||||
the community want. Other functions that might belong there fall
|
||||
outside the scope of this PEP though.
|
||||
|
||||
The @ syntax proposal is withdrawn.
|
||||
The @ syntax proposal has been withrawn.
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549
|
||||
|
||||
|
||||
Copyright
|
||||
|
|
Loading…
Reference in New Issue