Raymond Hettinger wrote:
* The PFA implementation proposed for Py2.4 ran slower than an
equivalent closure.  If the latest implementation offers better
performance, then that may be a reason for having it around.

Not having done the timing, I'll defer to Paul and yourself here. However, one of the proposed enhancements is to automatically flatten out nested partial calls - this won't speed up the basic cases, but will allow incremental construction in two or more stages without a speed loss at the final call.


flags rather than the first argument of a binary operator.  Still, I
found closures to be more flexible in that they could handle any
argument pattern and could freeze more than one variable or keyword at a
time.

I'm not sure what this one is about - the PEP 309 implementation allows a single partial call to freeze an arbitrary number of positional arguments (starting from the left), and an arbitrary number of keyword arguments at any position. (This is why the name was changed from curry to partial - it was general purpose partial function application, rather than left currying)


* The instance method limitation never came up for me.  However, it
bites to have a tool working in a way that doesn't match your mental
model.  We have to document the limitations, keep them in mind while
programming, and hope to remember them as possible causes if bugs ever
arise.  It would be great if these limitations could get ironed out.

The 'best' idea I've come up with so far is to make partial a class factory instead of a straight class, taking an argument that states how many positional arguments to prepend at call time. A negative value would result in the addition of (len(callargs)+1) to the position at call time.


Then, partial() would return a partial application class which appended all positional arguments at call time, partial(-1) a class which prepended all positional arguments. partial(1) would be the equivalent of partialmethod, with the first argument prepended, and the rest appended.

In the general case, partial(n)(fn, *args1)(*args2) would give a call that looked like fn(args2[:n] + args1 + args2[n:]) for positive n, and fn(args2[:len(args2)+n+1] + args1 + args2[len(args2)+n+1:]) for negative n. n==0 and n==-1 being obvious candidates for tailored implementations that avoided the unneeded slicing. The presence of keyword arguments at any point wouldn't affect the positional arguments.

With this approach, it may be necessary to ditch the flattening of nested partials in the general case. For instance, partial(partial(-1)(fn, c), a)(b) should give an ultimate call that looks like fn(a, b, c). Simple cases where the nested partial application has the same number of prepended arguments as the containing partial application should still permit flattening, though.

* Using the word "partial" instead of "lambda" traded one bit of
unreadability for another.

The class does do partial function application though - I thought the name fit pretty well.


* It is not clear that the proposed implementation achieves one of the
principal benefits laid out in the PEP:  "I agree that lambda is usually
good enough, just not always. And I want the possibility of useful
introspection and subclassing."

I think it succeeds on the introspection part, since the flattening of nested partials relies on the introspection abilities. Not so much on the subclassing - partialmethod wasn't able to reuse too much functionality from partial.


If we get a better implementation, it would be nice if the PEP were
updated with better examples.  The TkInter example is weak because we
often want to set multiple defaults at the same time (foreground,
background, textsize, etc) and often those values are config options
rather than hardwired constants.

Hmm - the PEP may give a misleading impression of what the current implementation can and can't do. It's already significantly more flexible than what you mention here. For instance, most of the examples you give below could be done using keyword arguments.


That's likely to be rather slow though, since you end up manipulating dictionaries rather than tuples, so I won't pursue that aspect. Instead, I'm curious how many of them could be implemented using positional arguments and the class factory approach described above:

  cmp(x,y)        # partial()(cmp, refobject)
  divmod(x,y)     # partial(-1)(y)
  filter(p,s)     # partial()(filter, p)
  getattr(o,n,d)  # partial(-1)(getattr, n, d)
  hasattr(o,n)    # partial(-1)(hasttr, n)
  int(x,b)        # partial(-1)(int, b)
  isinstance(o,c) # partial(-1)(isinstance, c)
  issubclass(a,b) # partial(-1)(issubclass, b)
  iter(o,s)       # partial(-1)(iter, s)
  long(x,b)       # partial(-1)(long, b)
  map(f,s)        # partial()(map, f)
  pow(x,y,z)      # partial(1)(pow, y) OR partial(-1)(pow, z)
                  # OR partial(-1)(pow, y, z)
  range([a],b,[c])# partial(-1)(range, c) (Default step other than 1)
                  # Always need to specify start, though
  reduce(f,s,[i]) # partial()(f
  round(x, n)     # we would want a right curry.
  setattr(o,n,v)  # partial(1)(setattr, n)

Essentially, partial() gives left curry type behaviour, partial(-1) gives right curry behaviour, and partial(n) let's you default an argument in the middle. For positive n, the number is the index of the first argument locked, for negative n it is the index of the last argument that is locked.

An argument could be made for providing the names 'leftpartial' (fixing left-hand arguments) and 'rightpartial' (fixing right-hand arguments) as aliases for partial() and partial(-1) respectively.

Regards,
Nick.

--
Nick Coghlan   |   [EMAIL PROTECTED]   |   Brisbane, Australia
---------------------------------------------------------------
            http://boredomandlaziness.skystorm.net
_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to