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