creating partial applications of functions shouldn't be so common in real-world code that it needs to be so concise.

I don't quite agree with this; I think a lot of people use some sort of partial application every day and just don't know it.

- Of the times you use lambda, how often does it redirects all arguments to a single function, with the rest hard coded?
- How often do you instantiate a class (with some args), call a method on that class (with some args) and then throw it away, never to be seen again?

Both of these are basically crufty approximations for partial application, and I see them pretty often, especially the second one.


On Thu, Nov 14, 2013 at 9:45 AM, Andrew Barnert <abarnert@yahoo.com> wrote:
On Nov 14, 2013, at 7:05, Steven D'Aprano <steve@pearwood.info> wrote:

> On Wed, Nov 13, 2013 at 07:13:19PM -0800, Andrew Barnert wrote:
>
>> I know this is getting way off topic, but the real problem with doing
>> curried/auto-partial functions in Python isn't the parens, it's the
>> variable arguments. An auto-partial function has to accumulate
>> parameters if it doesn't get enough, execute when it does. (Currying
>> gives you that for free, because it means you only get one argument at
>> a time, but you can do auto-partials without currying.)
>
> I disagree. The real problem is the ambiguity. Given:
>
> y = divmod(x)
>
> did I intend for y to be a partial function, or did I mean to type
> divmod(x, 3) but mess up? Given how few coders come from a functional
> programming background, my money is that it's an error.

This may be a reason not to do it, but it's not something that makes it hard or impossible to do it. Variable arguments _are_ something that makes it hard or impossible to do it.

> In languages with static typing, it's easy for the compiler to resolve
> this: if y is declared as a function object, then I meant for the
> partial application. If y is declared as an int, then it's an error. But
> you can't do this in Python.

That's not how it works in any of the languages we're talking about. They have a type inference system, usually based on Hindley-Milner, not a C-style nominal type system.

Of course sometimes declared (or elsewhere-inferred) variable types will resolve an ambiguity during unification, but that's not the usual case; declared or inferred function types do so far more often. divmod is int*int->int*int (or, in a curried language, int->int->int*int), and therefore this expression is only typable if it's a partial, and y's type is inferred from the type the expression has as a partial. (And if you've declared y to have an explicit type that isn't compatible with the partial, you get a unification error.)

If you allow varargs functions, where the type can be int->int or int*int->int, HM doesn't work. And, not coincidentally, none of these languages allow varargs functions.

> [...]
>> But to handle a vararg function, you'd need a separate syntax for
>> partializing vs. calling.
>
> We have that. It's called functools.partial :-)

Yes, but the (sub-)thread started off with (summarizing) "I wish we had something more brief/simple/Scala-like for partials than functools.partial". And functools.partial is not an answer to that.

> Aside: am I the only one who wishes there was a functools.rpartial, that
> binds from the right instead of the left?

I've often wanted that. I've even built it a few times. It has the same problem with keyword arguments and keyword-only params that auto-partialing does, but it doesn't feel like as much of a limitation--you just wouldn't ever use it in those cases (especially since you can often just bind the argument by keyword in those cases).

I've also occasionally wanted a partial where you specify the indices of the arguments as keywords (which gives you rpartial just by using -1), so I could more easily bind the second of three (or even of variable) arguments. But that comes up less often.

>> Using a different operator like [] or % or << seems attractive at
>> first, but it can't handle keywords.
>> You could add a method, so spam._(n=5) returns partial(spam, n=5), but
>> ._ is hideous, and anything meaningful like bind or partial is no
>> longer a shortcut.
>
> And why is this a problem?

I assume you're not asking about why ._ being hideous is a problem, but why .partial being not a shortcut is a problem, right?

Offering foo.partial(n=5) as an alternative for partial(foo, n=5) doesn't provide any benefit--it's not easier to read, more obvious to write, shorter, or better in any other way. It just gives you two obvious ways to do it instead of one. Which is bad.

> You read code more often than you type it,
> and quite frankly, creating partial applications of functions shouldn't
> be so common in real-world code that it needs to be so concise.

It's common in many application areas. For example, I have a row of radio buttons. Instead of defining 5 separate callback functions, I write one callback function that takes a number from 0 to 4, and then I bind each button to partial(callback, n).

Many people today instead bind each button to a lambda, I suspect because the tkinter tutorials all use lambda for this purpose instead of partial.

I've also seen people write an explicit make_btn_callback function so they can bind each button to make_btn_callback(n)

Anyway, compare these three and tell me you see zero benefit in the last one:

Button(frame, str(i), lambda event: click(i, event))
Button(frame, str(i), partial(click, i))
Button(frame, str(i), click(i, ...))


_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas