PEP 309 (Partial Function Application) Idea

2005-03-11 Thread Chris Perkins
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)

I haven't though this through much, and I'm sure there are issues, but
I do like the way it looks.  The "..." really stands out as saying
"something is omitted here".


Chris Perkins
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: PEP 309 (Partial Function Application) Idea

2005-03-12 Thread Chris Perkins
>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
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: PEP 309 (Partial Function Application) Idea

2005-03-14 Thread Chris Perkins
Steven Bethard <[EMAIL PROTECTED]> wrote:
> 
> getting attributes with defaults[1]:
>  objs.sort(key=lambda a: getattr(a, 'lineno', 0))
>  objs.sort(key=getattr(__, 'lineno', 0)
> 

Yes, this exact example is one of the (very) few that I found in the
standard library where the syntax actually helps.

> Places where you might be able to use it (with some changes):
> 
> using bound methods[1][2]:
>  map(lambda x: x.strip(), lst)
>  map(str.strip(), lst) #!! doesn't work for unicode
>  map(methodcaller('strip'), lst) # proposed by Alex Martelli
>  __.strip() # note that calling strip on __ would have to
> # return a curryable looking for one arg...
> 

Yes, the bound method problem is what convinced me to give up on this.
One can hardly write "strip()" (yes, that's four dots).
But note that there is a library out there that does precisely this
(and much more, if I remember) - I believe it's in David Mertz's
Xoltar toolkit. It does allow you to write "_.strip()" and get a
curried callable (He uses "_", a single underscore, as the magic
thingy, which is odd considering its established meaning in
interactive mode).

If we could add an object to the builtins (or elsewhere) called "__"
that works as I proposed for "...", and also behaves like the things
in David Mertz's library (sorry, don't remember exactly where to find
it, or what it's called, and don't have time to look it up right now),
then this might really get interesting. Then we could do any of:
getattr(__, 'lineno', 0)
__.strip()
__.foo().bar(__)

> adding method to an instance[1]:
>  self.plural = lambda n: int(n != 1)

and _maybe_ even:
self.plural = int(__ != 1)


But wait - on second thought, "__" is a legal identifier now, unlike
"...", so we can't have the compiler messing around with expressions
like foo(x, __), can we.  OK, now I really give up on the whole idea!


Chris Perkins
-- 
http://mail.python.org/mailman/listinfo/python-list