2003-02-10 09:51:45 -05:00
|
|
|
|
PEP: 309
|
2003-02-18 19:46:51 -05:00
|
|
|
|
Title: Built-in Curry Type
|
2003-02-10 09:51:45 -05:00
|
|
|
|
Version: $Revision$
|
|
|
|
|
Last-Modified: $Date$
|
|
|
|
|
Author: Peter Harris <scav@blueyonder.co.uk>
|
|
|
|
|
Status: Draft
|
|
|
|
|
Type: Standards Track
|
|
|
|
|
Content-Type: text/x-rst
|
|
|
|
|
Created: 08-Feb-2003
|
|
|
|
|
Python-Version: 2.4
|
2003-02-18 19:46:51 -05:00
|
|
|
|
Post-History: 10-Feb-2003
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Abstract
|
|
|
|
|
=========
|
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
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.
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Motivation
|
|
|
|
|
===========
|
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
Curried functions are useful as functional 'sections' or as convenient
|
2003-02-10 09:51:45 -05:00
|
|
|
|
anonymous functions for use as callbacks.
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
Python has more flexible argument-passing, and so curries cannot be
|
|
|
|
|
implicit in the same way. Instead of using them, a Python programmer
|
2003-02-10 09:51:45 -05:00
|
|
|
|
will probably either define another named function or use a lambda.
|
|
|
|
|
But lambda syntax is horrible, especially when you want to do
|
|
|
|
|
something complex.
|
|
|
|
|
|
|
|
|
|
We need something better.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
|
==========
|
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
Here is one way to do a curry in Python::
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
class curry(object):
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
def __init__(self, fn, *args, **kw):
|
|
|
|
|
self.fn, self.args, self.kw = (fn, args, kw)
|
|
|
|
|
|
|
|
|
|
def __call__(self, *args, **kw):
|
|
|
|
|
d = self.kw.copy()
|
|
|
|
|
d.update(kw)
|
|
|
|
|
return self.fn(*(self.args + args), **d)
|
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
Note that when the curry is called, positional arguments are
|
2003-02-10 09:51:45 -05:00
|
|
|
|
appended to those provided to the constructor, and keyword arguments
|
|
|
|
|
override and augment those provided to the constructor.
|
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
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
|
2003-02-10 09:51:45 -05:00
|
|
|
|
Label class, but with a blue foreground by default.
|
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
I think a built-in type called ``curry``, that behaves the same way
|
2003-02-10 09:51:45 -05:00
|
|
|
|
but maybe implemented more efficiently, would be very useful.
|
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
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.
|
|
|
|
|
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
Tentative Syntax Proposal
|
|
|
|
|
==========================
|
|
|
|
|
|
|
|
|
|
I know syntax proposals have the odds stacked against them, and
|
|
|
|
|
introducing new punctuation characters is frowned upon, but I think
|
2003-02-18 19:46:51 -05:00
|
|
|
|
curries may be a sufficiently powerful abstraction to deserve it.
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
I suggest the syntax ``fn@(*args, **kw)``, meaning the same as
|
|
|
|
|
``curry(fn, *args, **kw)``. I have no idea what havoc this would
|
2003-02-10 09:51:45 -05:00
|
|
|
|
wreak on the parser.
|
|
|
|
|
|
|
|
|
|
At least there are no backwards-compatibility issues because the @
|
|
|
|
|
character isn't a legal operator in any previous versions of Python.
|
|
|
|
|
|
|
|
|
|
The @ sign is used in some assembly languages to imply register
|
|
|
|
|
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.
|
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
(The only other connection I can see with curry is that @ looks a bit
|
|
|
|
|
like a section through a mushroom pakora.)
|
|
|
|
|
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
Examples of Use
|
|
|
|
|
---------------
|
|
|
|
|
|
|
|
|
|
Using closures as callbacks with bound arguments::
|
|
|
|
|
|
|
|
|
|
def handler(arg1, arg2, opt=0):
|
|
|
|
|
#whatever...
|
|
|
|
|
|
|
|
|
|
button1 = Button(window, text="Action A",
|
2003-02-18 19:46:51 -05:00
|
|
|
|
command=handler@('A', '1'))
|
2003-02-10 09:51:45 -05:00
|
|
|
|
button2 = Button(window, text="Action B",
|
2003-02-18 19:46:51 -05:00
|
|
|
|
command=handler@('B', '2', opt=1))
|
2003-02-10 09:51:45 -05:00
|
|
|
|
|
|
|
|
|
Convenience functions ::
|
|
|
|
|
|
|
|
|
|
nextarg = sys.argv.pop@(0)
|
|
|
|
|
|
|
|
|
|
|
2003-02-18 19:46:51 -05:00
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
|
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:
|