On Sat, May 12, 2018 at 2:28 PM Steven D'Aprano <st...@pearwood.info> wrote:
> Part 2. > > On Sat, May 12, 2018 at 08:16:07AM -0700, Neil Girdhar wrote: > > I love given compared with := mainly because > > [...] > > * Python has a reputation for being working pseudocode, and given reads > > like pseudocode. := needs to be deciphered by comparison especially in > the > > complicated cases where multiple := operators are used on one line. > > Until you learn and become familiar with a new syntax, there is > generally going to be a period you have to decipher it. I spent a long > time mentally translating list comprehensions into mathematical set > builder notation before it became second nature to me. > > Even now, I know people who find decorators and comprehensions > indecipherable. Or at least, so they claim, and they aren't motivated to > bother learning them. Oh well, that's their loss. > > Binding-expressions aren't like asynchronous programming, where the > entire coding paradigm is different, and you literally have to think > about your algorithms in another way. Whether you spell the binding > operation > > target := expr > expr as target > expr -> target > target given target = expr > let target = expr > : target expr ; > > (that last one is stolen from Forth, and should not be taken as a > serious suggestion) > > is just a matter of spelling. We'll get used to whatever spelling it is. > Some may be more convenient than others, or more error-prone, or may be > harder to parse, but they're secondary issues. (Important, but still > secondary.) Fundamentally, the operation is the same regardless of the > spelling: > > - evaluate an expression > - bind that value to a name > - return the value > > > (Except of course Nick's "given" suggestion is more complex, since the > returned value is not necessarily the same as the bound value.) > > > > * there are no difficult mental questions about evaluation order, e.g., > in > > a bracketed expression having multiple assignments. > > Aren't there just? > > x = 1 > print( x + x given x = 50 ) > > > Will that print 100, or 51? Brackets ought to make it clearer: > > (x + x given x = 50) # this ought to return 100 > x + (x given x = 50) # this ought to return 51 > > but if I leave the brackets out, I have no idea what I'll get. > It has to be 100, or else outer parentheses change how an expression is evaluated. > > > > > Similarly, instead of > > (a.b(a) given a = c.d()) do I write (a.b(a := c.d())) or ((a := > > c.d()).b(a)) ? > > > I would expect that your first example is a NameError: > > a.b(a := c.d()) > > since Python evaluates arguments to a method *after* looking up the > method. So that corresponds to: > > - look up "a" # NameError, unless you've already got an "a" > - look up "a.b" > - evaluate c.d() > - assign that value to a > - pass that to the a.b method we found earlier > > > What you probably want is the second version: > > (a := c.d()).b(a) > > which of course looks like utter crap. It might look better if we use at > least half-way sensible variable names and a more realistic looking > example, instead of obfuscated one-letter names. > > (widget := widget_builder.new(*args)).method(widget) > > > > * it avoids the question of what happens when := is used in a switch: > (a > > if (b := c) else d) Sometimes you want the assignment to happen > > unconditionally (a if (b:=c) else d) + b; sometimes you don't. How do > you > > force one case or the other? > > I think that this argument is really weak. The obvious answer is, if you > don't want the assignment to happen unconditionally, then don't do the > assignment unconditionally. Where you do it depends on when you want the > assignment to take place. There's no mystery here. > > # unconditionally pop from a list > (spam if mylist.pop(idx) else eggs) + mylist > > # conditionally pop from a list > (mylist.pop(idx) if condition else eggs) + mylist > (spam if condition else mylist.pop(idx)) + mylist > > > Take your choice of which you want: > > (spam if (mylist := expr) else eggs) + mylist > ((mylist := expr) if condition else eggs) + mylist > (spam if condition else (mylist := expr)) + mylist > > Of course, in the last two cases, you're going to get a NameError when > you try to add mylist if the ternary operator took the wrong branch. > Unless you already defined mylist earlier. > That's the problem I'm showing. This is impossible: (spam if (mylist := expr) else eggs) + mylist but just fine with given: ((spam if mylist else eggs) + mylist) given mylist = expr) > If you want something else, you have to explain what you want. > > > > given makes it obvious by separating > > assignment from the usage of its assignment target. > > This is just a purely mechanical source transformation from one to the > other. Just replace ":" with "mylist given mylist " and you are done. > First of all, you cannot convert all := expressions to given expressions. Even if you could, the point is that they are separated. I went over the separation of concerns in my other mail. > # unconditional version > (spam if (mylist given mylist = expr) else eggs) + mylist > > # conditional versions > ((mylist given mylist = expr) if condition else eggs) + mylist > (spam if condition else (mylist given mylist = expr)) + mylist > > If you can write the "given" versions, then just do the reverse > transformation and replace the redundant verbosity with a colon. > You can't always do that. I've given you two examples now where you cannot go backwards. > > > Style: > > * it avoids the big style question of when to use and when not to use > :=. > > (Even if you ask people not to, people are going to write the > > expression-statement a := b as a synonym for the statement a = b.) > > What if they do? Is it really the end of the world if some ex-Pascal > coders or people with an over-developed desire for (foolish) consistency > add a colon to their statement level assignments? > Some people add semi-colons to their statements too. Do we care? No. > > But if it aggitates people so much, then I'm perfectly happy with > Guido's suggestion that we simply ban top level binding expressions and > require them to leave the colons out. > > > > * it looks a lot like the existing Python "for" and "if" clauses, which > > also do in-expression assignments. > > "if" clauses do an assignment? Have you borrowed the keys to Guido's > time machine, and are writing from 2025 and Python 4.2? *wink* > > I don't think "given" expressions look even remotely similar to either. > > for target in iterable: > > if condition: > > another_expr given target = expr > I meant the clauses not the statements: (expression for x in it if x.y given z = x.z) All three clauses have the same format, and obvious temporal ordering. for and given both bind a name that is visible to the expression and to clauses below. > Aside from "all three use a keyword". > > > -- > Steve > _______________________________________________ > Python-ideas mailing list > Python-ideas@python.org > https://mail.python.org/mailman/listinfo/python-ideas > Code of Conduct: http://python.org/psf/codeofconduct/ > > -- > > --- > You received this message because you are subscribed to a topic in the > Google Groups "python-ideas" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/python-ideas/CFuqwmE8s-E/unsubscribe. > To unsubscribe from this group and all its topics, send an email to > python-ideas+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. >
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/