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. > 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. 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. # 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. > 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 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/