PEP 309 (Partial Function Application) Idea
Chris Perkins
chrisperkins99 at gmail.com
Sat Mar 12 14:24:07 EST 2005
>Scott David Daniels wrote:
>>>Chris Perkins wrote:
>>>>Random idea of the day: How about having syntax support for
>>>>currying/partial function application, like this:
>>>>func(..., a, b)
>>>>func(a, ..., b)
>>>>func(a, b, ...)
>>>>
>>>>That is:
>>>>1) Make an Ellipsis literal legal syntax in an argument list.
>>>>2) Have the compiler recognize the Ellipsis literal and transform
>>>> the function call into a curried/parially applied function.
>>>>So the compiler would essentially do something like this:
>>>>
>>>>func(a, ...) ==> curry(func, a)
>>>>func(..., a) ==> rightcurry(func, a)
>>>>func(a, ..., b) ==> rightcurry(curry(func,a), b)
>
>The interaction of this with keyword args and omitted args is
>problematic (as is the case for rightcurry, in fact). I can't
>think of a good way to explain what _should_ happen for a
>function defined as def function(*args, **kwargs): ... when you:
>
> def fun(bug, frog, verb): ...
> f1 = function(1, ..., frog=3)
> f2 = f1(..., 4)
> f2()
>
>Keywords were why I did no rightcurry definition in the first place;
>I couldn't convince myself there was a good, obvious, resolution.
>
I agree that it's not obvious what _should_ happen in complex cases.
I wrote up a psuedo-implementation to play with, just to get a feel
for it. It works only on @curryable functions, and I use "__" in place
of "...". I think that's about as close as I can get in pure Python.
def curryable(func):
"This hurts my brain a little bit, but I _think_ it works."
def proxyfunc(*args, **kwds):
if list(args).count(Ellipsis) > 1:
raise TypeError('Your mother was a hampster...')
if Ellipsis in args:
@curryable
def curried(*a, **k):
kwdict = kwds.copy()
kwdict.update(k)
epos = list(args).index(Ellipsis)
return func(*(args[:epos] + a + args[epos+1:]), **kwdict)
return curried
return func(*args, **kwds)
return proxyfunc
def test():
__ = Ellipsis
@curryable
def fun(bug, frog, verb):
print bug, frog, verb
f1 = fun(1, __, frog=3)
f2 = f1(__, 4)
try:
f2()
except TypeError, e:
print e # multiple values for keyword 'frog'
f1 = fun(1, __, verb=3)
f2 = f1(__, 4)
f2()
f2(verb=99)
try:
f1(__, 2, __)
except TypeError, e:
print e
if __name__ == '__main__':
test()
After playing with this a bit, I found the semantics to be reasonably
obvious once I got used to it; at least in the cases I tried. I'd be
quite happy to have anything fishy throw an exception. Cases where
I would actually use this would be quite simple, I think.
On the other hand, I'm not convinced that this construct would be
especially useful. I spent some time looking at uses of lambda in the
standard library, hoping to find lots of examples where a (...)
function would be more readable, but I eventually gave up. (Found
dozens of cases where a list-comp or genexp would be better, though).
While I think that func(x, ...) is more readable than partial(func, x),
I'm not sure that I would use either of them often enough to warrant
special syntax. If anyone disagrees, feel free to petition python-dev;
I don't expect to pursue this any further.
Thanks to all for your comments.
Chris Perkins
More information about the Python-list
mailing list