2003-02-10 09:51:45 -05:00
|
|
|
|
PEP: 309
|
2004-08-27 09:29:47 -04:00
|
|
|
|
Title: Partial Function Application
|
2003-02-10 09:51:45 -05:00
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
|
|
|
|
Author: Peter Harris <scav@blueyonder.co.uk>
|
2004-04-03 21:37:34 -05:00
|
|
|
|
Status: Accepted
|
2003-02-10 09:51:45 -05:00
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 08-Feb-2003
|
|
|
|
|
Python-Version: 2.4
|
2004-02-28 14:09:16 -05:00
|
|
|
|
Post-History: 10-Feb-2003, 27-Feb-2003, 22-Feb-2004
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
2003-03-10 23:49:44 -05:00
|
|
|
|
========
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
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).
|
2003-03-10 23:49:44 -05:00
|
|
|
|
|
2004-08-27 09:29:47 -04:00
|
|
|
|
I propose a standard library module called "functional", to hold
|
|
|
|
|
useful higher-order functions, including the implementation of
|
|
|
|
|
partial().
|
|
|
|
|
|
|
|
|
|
An implementation has been submitted to SourceForge [2]_.
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Motivation
|
2003-03-10 23:49:44 -05:00
|
|
|
|
==========
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
In functional programming, function currying is a way of implementing
|
2004-08-27 09:29:47 -04:00
|
|
|
|
multi-argument functions in terms of single-argument functions. A
|
2004-02-28 14:09:16 -05:00
|
|
|
|
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
|
2004-08-27 09:29:47 -04:00
|
|
|
|
programming it turns out to be very useful. Expressing a function in
|
2004-02-28 14:09:16 -05:00
|
|
|
|
terms of partial application of arguments to another function can be
|
|
|
|
|
both elegant and powerful, and in functional languages it is heavily
|
|
|
|
|
used.
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
In some functional languages, (e.g. Miranda) you can use an expression
|
2003-02-18 19:46:51 -05:00
|
|
|
|
such as ``(+1)`` to mean the equivalent of Python's
|
|
|
|
|
``(lambda x: x + 1)``.
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
Python does not implement multi-argument functions by currying, so if
|
2004-08-27 09:29:47 -04:00
|
|
|
|
you want a function with partially-applied arguments you would
|
|
|
|
|
probably use a lambda as above, or define a named function for each
|
|
|
|
|
instance.
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
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.
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
|
2004-08-27 09:29:47 -04:00
|
|
|
|
Example Implementation
|
|
|
|
|
======================
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
Here is one way to do a create a callable with partially-applied
|
2004-08-27 09:29:47 -04:00
|
|
|
|
arguments in Python. The implementation below is based on improvements
|
2004-02-28 14:09:16 -05:00
|
|
|
|
provided by Scott David Daniels::
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
class partial(object):
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2004-02-21 11:32:30 -05:00
|
|
|
|
def __init__(*args, **kw):
|
|
|
|
|
self = args[0]
|
|
|
|
|
self.fn, self.args, self.kw = (args[1], args[2:], kw)
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
def __call__(self, *args, **kw):
|
2004-02-21 11:32:30 -05:00
|
|
|
|
if kw and self.kw:
|
2003-03-10 23:49:44 -05:00
|
|
|
|
d = self.kw.copy()
|
|
|
|
|
d.update(kw)
|
|
|
|
|
else:
|
2004-02-21 11:32:30 -05:00
|
|
|
|
d = kw or self.kw
|
2003-02-10 09:51:45 -05:00
|
|
|
|
return self.fn(*(self.args + args), **d)
|
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
(A recipe similar to this has been in the Python Cookbook for some
|
|
|
|
|
time [1]_.)
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2004-08-27 09:29:47 -04:00
|
|
|
|
Positional arguments, keyword arguments or both can be supplied at
|
|
|
|
|
when creating the object and when calling it.
|
|
|
|
|
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
Examples of Use
|
|
|
|
|
===============
|
|
|
|
|
|
|
|
|
|
So ``partial(operator.add, 1)`` is a bit like ``(lambda x: 1 + x)``.
|
|
|
|
|
Not an example where you see the benefits, of course.
|
|
|
|
|
|
2004-08-27 09:29:47 -04:00
|
|
|
|
Note too, that you could wrap a class in the same way, since classes
|
|
|
|
|
themselves are callable factories for objects. So in some cases,
|
2004-02-28 14:09:16 -05:00
|
|
|
|
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.
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
Here's a simple example that uses partial application to construct
|
|
|
|
|
callbacks for Tkinter widgets on the fly::
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
from Tkinter import Tk, Canvas, Button
|
|
|
|
|
import sys
|
|
|
|
|
from functional import partial
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
win = Tk()
|
|
|
|
|
c = Canvas(win,width=200,height=50)
|
|
|
|
|
c.pack()
|
2004-08-27 09:29:47 -04:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
for colour in sys.argv[1:]:
|
2004-08-27 09:29:47 -04:00
|
|
|
|
b = Button(win, text=colour,
|
|
|
|
|
command=partial(c.config, bg=colour))
|
2004-02-28 14:09:16 -05:00
|
|
|
|
b.pack(side='left')
|
|
|
|
|
|
|
|
|
|
win.mainloop()
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
|
2003-03-10 23:49:44 -05:00
|
|
|
|
Abandoned Syntax Proposal
|
|
|
|
|
=========================
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
I originally suggested the syntax ``fn@(*args, **kw)``, meaning the
|
|
|
|
|
same as ``partial(fn, *args, **kw)``.
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
The @ sign is used in some assembly languages to imply register
|
|
|
|
|
indirection, and the use here is also a kind of indirection.
|
2004-08-27 09:29:47 -04:00
|
|
|
|
``f@(x)`` is not ``f(x)``, but a thing that becomes ``f(x)`` when you
|
2003-02-10 09:51:45 -05:00
|
|
|
|
call it.
|
|
|
|
|
|
2004-08-27 09:29:47 -04:00
|
|
|
|
It was not well-received, so I have withdrawn this part of the
|
|
|
|
|
proposal. In any case, @ has been taken for the new decorator syntax.
|
2003-03-10 23:49:44 -05:00
|
|
|
|
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
Feedback from comp.lang.python and python-dev
|
|
|
|
|
=============================================
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
|
|
|
|
Among the opinions voiced were the following (which I summarise):
|
|
|
|
|
|
|
|
|
|
* Lambda is good enough.
|
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
* The @ syntax is ugly (unanimous).
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
|
|
|
|
* 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.
|
|
|
|
|
|
2004-08-27 09:29:47 -04:00
|
|
|
|
* It isn't function currying, but partial application. Hence the
|
2004-02-28 14:09:16 -05:00
|
|
|
|
name is now proposed to be partial().
|
|
|
|
|
|
2003-03-10 23:49:44 -05:00
|
|
|
|
* 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).
|
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
* For completeness, another object that appends partial arguments
|
2003-03-10 23:49:44 -05:00
|
|
|
|
after those supplied in the function call (maybe called
|
|
|
|
|
``rightcurry``) has been suggested.
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
|
|
|
|
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
|
2004-02-28 14:09:16 -05:00
|
|
|
|
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 partial rather than curry or closure,
|
2004-08-27 09:29:47 -04:00
|
|
|
|
so I have amended the proposal in this PEP accordingly. But not
|
2004-02-28 14:09:16 -05:00
|
|
|
|
throughout: some incorrect references to 'curry' have been left in
|
|
|
|
|
since that's where the discussion was at the time.
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
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.
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2004-08-27 09:29:47 -04:00
|
|
|
|
which he assures me is more efficient.
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
2004-08-27 09:29:47 -04:00
|
|
|
|
I also coded the class in Pyrex, to estimate how the performance
|
|
|
|
|
might be improved by coding it in C::
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
|
|
|
|
cdef class curry:
|
2003-03-10 23:49:44 -05:00
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
cdef object fn, args, kw
|
2003-03-10 23:49:44 -05:00
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
def __init__(self, fn, *args, **kw):
|
|
|
|
|
self.fn=fn
|
2003-03-10 23:49:44 -05:00
|
|
|
|
self.args=args
|
|
|
|
|
self.kw = kw
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
|
|
|
|
def __call__(self, *args, **kw):
|
2003-03-10 23:49:44 -05:00
|
|
|
|
if self.kw: # from Python Cookbook version
|
|
|
|
|
d = self.kw.copy()
|
2003-02-18 19:46:51 -05:00
|
|
|
|
d.update(kw)
|
2003-03-10 23:49:44 -05:00
|
|
|
|
else:
|
|
|
|
|
d=kw
|
2003-02-18 19:46:51 -05:00
|
|
|
|
return self.fn(*(self.args + args), **d)
|
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
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.
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Summary
|
2003-03-10 23:49:44 -05:00
|
|
|
|
=======
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
I prefer that some means to partially-apply functions and other
|
|
|
|
|
callables should be present in the standard library.
|
|
|
|
|
|
|
|
|
|
A standard library module ``functional`` should contain an
|
|
|
|
|
implementation of ``partial``, and any other higher-order functions
|
2004-08-27 09:29:47 -04:00
|
|
|
|
the community want. Other functions that might belong there fall
|
2004-02-28 14:09:16 -05:00
|
|
|
|
outside the scope of this PEP though.
|
2003-03-10 23:49:44 -05:00
|
|
|
|
|
2004-08-27 09:29:47 -04:00
|
|
|
|
Patches for the implementation, documentation and unit tests (SF
|
|
|
|
|
patches 931005_, 931007_, and 931010_ respectively) have been
|
|
|
|
|
submitted but not yet checked in.
|
|
|
|
|
|
|
|
|
|
A C implementation by Hye-Shik Chang has also been submitted, although
|
|
|
|
|
it is not expected to be included until after the Python
|
|
|
|
|
implementation has proven itself useful enough to be worth optimising.
|
2004-02-28 14:09:16 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
References
|
|
|
|
|
==========
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
2004-02-28 14:09:16 -05:00
|
|
|
|
.. [1] http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
2004-08-27 09:29:47 -04:00
|
|
|
|
.. [2] Patches 931005_, 931007_, and 931010_.
|
|
|
|
|
|
|
|
|
|
.. _931005: http://www.python.org/sf/931005
|
|
|
|
|
.. _931007: http://www.python.org/sf/931007
|
|
|
|
|
.. _931010: http://www.python.org/sf/931010
|
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
|
2003-02-10 09:51:45 -05:00
|
|
|
|
Copyright
|
|
|
|
|
=========
|
|
|
|
|
|
|
|
|
|
This document has been placed in the public domain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
..
|
|
|
|
|
Local Variables:
|
|
|
|
|
mode: indented-text
|
|
|
|
|
indent-tabs-mode: nil
|
|
|
|
|
sentence-end-double-space: t
|
|
|
|
|
fill-column: 70
|
|
|
|
|
End:
|