Re: [Python-ideas] PEP 572: Assignment Expressions (post #4)

You're looking at a very early commit there. I suggest looking at the most recent commits on one of two branches:
https://github.com/Rosuav/cpython/blob/statement-local- variables/Grammar/Grammar https://github.com/Rosuav/cpython/blob/assignment- expressions/Grammar/Grammar
Okay, and the syntax "target := value" seems to be much easier to handle with. I do support this feature, but now I'm worried about the meaning of ':=', it seems to be a lazy assignment in some degree. I'm not sure using ':=' is proper here.
Inasmuch as I might like assignment expressions, it would only be in while or if statements, personally.
Not exactly, assignment expression also works for "if expression", we can now have null checking. var.method() if var:= function() else None Null checking is importance enough to have a specific syntax in many other languages(C#, kotlin, Ruby and so on), and we can even have more than null checking by adding expression assignment.

Yes, I should have added ternary expressions to if statements. I can definitely see the use there. However, your example is not null checking. You'd have to modify it slightly to get that: None if var:= function() is None else var.method() Still not bad looking. On Thu, Apr 12, 2018, 11:01 PM Thautwarm Zhao <yaoxiansamma@gmail.com> wrote:

None if var:= function() is None else var.method()
Make sense. In some specific scenes(maybe general, I'm not sure), var.method() if var:=function() else var looks cool although it's not really null checking. It works for Python, just like use `if lst` to check if the list `lst` is empty. 2018-04-13 11:22 GMT+08:00 David Mertz <mertz@gnosis.cx>:

[David Mertz <mertz@gnosis.cx>]
I couldn't find text in the PEP spelling out precedence, but there are two plausible ways that could be grouped: 1. None if (var:= function()) is None else var.method() which is what I believe you intended, and 2. None if var:= (function() is None) else var.method() which from earlier iterations of this thread I believe is the actual meaning - but isn't at all what was intended. The most clearly related example in the PEP appears to be: x = "default" if (eggs := spam().ham) is None else eggs which forced the intended meaning as in #1 above. While "assignment" is currently a statement type rather than "an operator", viewing the current situation as if "=" were an operator it has very low precedence, so it would be just as surprising at times to boost the precedence of ":=": if x := i+1: That is, in _that_ example, if x := (i+1): is "clearly intended", not the more tightly binding if (x ;= i) + 1: On the third hand, requiring parentheses all the time would also feel strained: while m := someregexp.match(somestring): is already impossible to misread. Annoying ;-)

Yes, I have not read all the iterations of the PEP, and none of them extremely closely. I had thought it "obvious" that ':=' should have a very high operator precedence. But of course if it doesn't then expressions like the one I proposed could mean something quite different. I find the third hand argument rather compelling. All those potentially required parentheses turn elegant looking code into an ugly mess. On Thu, Apr 12, 2018, 11:56 PM Tim Peters <tim.peters@gmail.com> wrote:

Clearly the PEP should spell out the precedence of :=. I'm sure Chris intended to give := the lowest possible precedence: the analogy with = (both in Python and in other languages), and the original design of the PEP, where the syntax was (<expr> as <var>), with mandatory parentheses. (Probably his implementation helps to guess his intention too, but I can't be bothered yet to clone and build it just to answer this question definitively. :-) I find the analogy argument compelling, and I think we should just make it a style rule that parentheses should be used whenever it could be misread by a human who's not sure about the precedence. Too bad for that one example. On Thu, Apr 12, 2018 at 9:14 PM, David Mertz <mertz@gnosis.cx> wrote:
-- --Guido van Rossum (python.org/~guido)

On Fri, Apr 13, 2018 at 2:37 PM, Guido van Rossum <guido@python.org> wrote:
Yes, that's the current intention, and (modulo a bug or two) the current reference implementation. This may change tonight. (If anyone's curious, and/or wants to join the discussion in real-time, I'll be streaming live on https://twitch.tv/rosuav as I tinker with the precedence.) ChrisA

On Fri, Apr 13, 2018 at 2:14 PM, David Mertz <mertz@gnosis.cx> wrote:
Interesting. How high, exactly? https://docs.python.org/3/reference/expressions.html#operator-precedence (note: "higher precedence" doesn't mean higher on that table - the table's from lowest to highest) Currently, I've placed ':=' up with if-else expressions. That's extremely low precedence. I'm contemplating moving it to just higher than comparison operators (ie just below the bitwise operators), putting it below all the arithmetic operators. The highest precedence I would consider putting it is just below the unary operators; that would mean it binds more tightly than arithmetic operators, but you can still say "x := y[1]" without having to parenthesize the latter. foo := a > b # does this capture 'a', or 'a > b'? bar := c + d # 'c' or 'c + d'? I'm open to argument here, but my thinking is that these should capture 'a' and 'c + d'. ChrisA

On 04/12/2018 09:52 PM, Chris Angelico wrote:
I think := should act the same as = or there will be plenty of confusion. If one wants to capture less then parenthesis can be used to narrow it down: (foo := a) > b Looked at another way -- you already have the value named as 'a', so why would you also name it as 'foo'? -- ~Ethan~

On Fri, Apr 13, 2018 at 4:27 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
More likely, 'a' won't be a simple name lookup, but a function call. Consider: pos = -1 while pos := buffer.find(search_term, pos + 1) >= 0: ... Once find() returns -1, the loop terminates. Should this need to be parenthesized? ChrisA

On 13 April 2018 at 16:47, Chris Angelico <rosuav@gmail.com> wrote:
I've certainly been assuming that cases like that would need to be written as: pos = -1 while (pos := buffer.find(search_term, pos + 1)) >= 0: ... I'd write the equivalent C while loop the same way: int pos = -1 while ((pos = find(buffer, search_term, pos + 1)) >= 0): ... The parentheses around the assignment in C are technically redundant, but I consider finding the matching parenthesis to be straightforward (especially with text editor assistance), while I consider figuring out where the next lower precedence operator appears difficult (since I don't have the C operand precedence table memorized, and there isn't any simple way for my text editor to help me out). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Regarding the precedence of :=, at this point the only acceptable outcome for me is one where at the statement level, := and = can be used interchangeably and will have the same meaning (except probably we wouldn't allow combining both in a chained assignment). Because then we can say "and you can use assignment in expressions too, except there you must use := because = would be too easily confused with ==". Ideally this would allow all forms of = to be replaced with :=, even extended structure unpacking (a, b, *c := foo()). -- --Guido van Rossum (python.org/~guido)

On 13 April 2018 at 16:13, Guido van Rossum <guido@python.org> wrote:
If we're going this far, why not just replace assignment statements with assignment expressions using := completely, and leave the current assignment statement for backward compatibility only (to be removed at some point following a deprecation period)? That's not rhetorical - if you're taking this position, I genuinely don't know if you see any remaining advantages to the assignment statement other than backward compatibility. Personally, I don't like the idea - but I'm not sure if that's just because I'm so used to assignment as a statement that I'm reluctant to accept change... Paul

[David Mertz <mertz@gnosis.cx>]
But you don't really believe that ;-) That's the rub. In your specific None if var:= function() is None else var.method() example it was obvious := should have very high precedence _there_, but in an earlier _, x1, x2 = (D := b**2 - 4*a*c), (-b + sqrt(D))/2, (-b - sqrt(D))/2 it was just as obvious that you wanted := to have a very low precedence _there_ (else D would be bound to the value of plain old `b`). I don't at all mean to be picking on you there - I suffer the same illusions. It appears that "very low precedence" is actually wanted in nearly all examples, _except_ when comparison is involved. So maybe := "should have" precedence between comparison operators (<=, is, etc) and bitwise OR (|). Then both your examples above would do what you expected them to do (and what I expected them to do at first glance). OTOH, that wouldn't be at all like C, whose precedence rules Python largely follows (only the comma operator has lower precedence than assignment in C), and - worse - wouldn't be at all like Python assignment statements appear to behave.
But of course if it doesn't then expressions like the one I proposed could mean something quite different.
Whether very high or very low, one of your two examples above is screwed.
I find the third hand argument rather compelling. All those potentially required parentheses turn elegant looking code into an ugly mess.
It is a hangup for me - but I'm not yet sure how severe.

On Fri, Apr 13, 2018 at 1:55 PM, Tim Peters <tim.peters@gmail.com> wrote:
Oops, that's a mistake. I'll add that in a bit. It has extremely low precedence.
I just went to test it, and the unparenthesized version is actually bombing with SyntaxError. I'm going to call that a bug, though, and see about fixing it tonight. Currently, the precedence is so low that basically anything will get captured, and if you want to capture anything less than the entire expression, you parenthesize. I'll experiment with placing it between '|' and comparison operators, which would mean that an unparenthesized assignment expression generally won't capture a boolean, but will instead capture the value itself. That's likely to be more useful, I think. (Obviously you can always use parens to overrule the precedence table.)
Agreed as regards addition. Less certain as regards comparisons. if x := i > 1: is currently "if x := (i > 1)" and not "if (x := i) > 1". Assuming I don't run into implementation difficulties, I'll play with this tonight, then document it one way or the other, and maybe have an open question about the alternative precedence.
Agreed. There's no reason to mandate the parens here. Neither the PEP nor the reference implementation has any problem with this expression being unparenthesized. ChrisA

On Fri, Apr 13, 2018 at 1:00 PM, Thautwarm Zhao <yaoxiansamma@gmail.com> wrote:
What do you mean by "lazy"? The assignment happens at the exact point that it is reached. Is there another language that uses := to mean something where the assignment doesn't happen until later? Or where the expression is assigned unevaluated, and is evaluated later? What part of it is lazy? ChrisA

Yes, I should have added ternary expressions to if statements. I can definitely see the use there. However, your example is not null checking. You'd have to modify it slightly to get that: None if var:= function() is None else var.method() Still not bad looking. On Thu, Apr 12, 2018, 11:01 PM Thautwarm Zhao <yaoxiansamma@gmail.com> wrote:

None if var:= function() is None else var.method()
Make sense. In some specific scenes(maybe general, I'm not sure), var.method() if var:=function() else var looks cool although it's not really null checking. It works for Python, just like use `if lst` to check if the list `lst` is empty. 2018-04-13 11:22 GMT+08:00 David Mertz <mertz@gnosis.cx>:

[David Mertz <mertz@gnosis.cx>]
I couldn't find text in the PEP spelling out precedence, but there are two plausible ways that could be grouped: 1. None if (var:= function()) is None else var.method() which is what I believe you intended, and 2. None if var:= (function() is None) else var.method() which from earlier iterations of this thread I believe is the actual meaning - but isn't at all what was intended. The most clearly related example in the PEP appears to be: x = "default" if (eggs := spam().ham) is None else eggs which forced the intended meaning as in #1 above. While "assignment" is currently a statement type rather than "an operator", viewing the current situation as if "=" were an operator it has very low precedence, so it would be just as surprising at times to boost the precedence of ":=": if x := i+1: That is, in _that_ example, if x := (i+1): is "clearly intended", not the more tightly binding if (x ;= i) + 1: On the third hand, requiring parentheses all the time would also feel strained: while m := someregexp.match(somestring): is already impossible to misread. Annoying ;-)

Yes, I have not read all the iterations of the PEP, and none of them extremely closely. I had thought it "obvious" that ':=' should have a very high operator precedence. But of course if it doesn't then expressions like the one I proposed could mean something quite different. I find the third hand argument rather compelling. All those potentially required parentheses turn elegant looking code into an ugly mess. On Thu, Apr 12, 2018, 11:56 PM Tim Peters <tim.peters@gmail.com> wrote:

Clearly the PEP should spell out the precedence of :=. I'm sure Chris intended to give := the lowest possible precedence: the analogy with = (both in Python and in other languages), and the original design of the PEP, where the syntax was (<expr> as <var>), with mandatory parentheses. (Probably his implementation helps to guess his intention too, but I can't be bothered yet to clone and build it just to answer this question definitively. :-) I find the analogy argument compelling, and I think we should just make it a style rule that parentheses should be used whenever it could be misread by a human who's not sure about the precedence. Too bad for that one example. On Thu, Apr 12, 2018 at 9:14 PM, David Mertz <mertz@gnosis.cx> wrote:
-- --Guido van Rossum (python.org/~guido)

On Fri, Apr 13, 2018 at 2:37 PM, Guido van Rossum <guido@python.org> wrote:
Yes, that's the current intention, and (modulo a bug or two) the current reference implementation. This may change tonight. (If anyone's curious, and/or wants to join the discussion in real-time, I'll be streaming live on https://twitch.tv/rosuav as I tinker with the precedence.) ChrisA

On Fri, Apr 13, 2018 at 2:14 PM, David Mertz <mertz@gnosis.cx> wrote:
Interesting. How high, exactly? https://docs.python.org/3/reference/expressions.html#operator-precedence (note: "higher precedence" doesn't mean higher on that table - the table's from lowest to highest) Currently, I've placed ':=' up with if-else expressions. That's extremely low precedence. I'm contemplating moving it to just higher than comparison operators (ie just below the bitwise operators), putting it below all the arithmetic operators. The highest precedence I would consider putting it is just below the unary operators; that would mean it binds more tightly than arithmetic operators, but you can still say "x := y[1]" without having to parenthesize the latter. foo := a > b # does this capture 'a', or 'a > b'? bar := c + d # 'c' or 'c + d'? I'm open to argument here, but my thinking is that these should capture 'a' and 'c + d'. ChrisA

On 04/12/2018 09:52 PM, Chris Angelico wrote:
I think := should act the same as = or there will be plenty of confusion. If one wants to capture less then parenthesis can be used to narrow it down: (foo := a) > b Looked at another way -- you already have the value named as 'a', so why would you also name it as 'foo'? -- ~Ethan~

On Fri, Apr 13, 2018 at 4:27 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
More likely, 'a' won't be a simple name lookup, but a function call. Consider: pos = -1 while pos := buffer.find(search_term, pos + 1) >= 0: ... Once find() returns -1, the loop terminates. Should this need to be parenthesized? ChrisA

On 13 April 2018 at 16:47, Chris Angelico <rosuav@gmail.com> wrote:
I've certainly been assuming that cases like that would need to be written as: pos = -1 while (pos := buffer.find(search_term, pos + 1)) >= 0: ... I'd write the equivalent C while loop the same way: int pos = -1 while ((pos = find(buffer, search_term, pos + 1)) >= 0): ... The parentheses around the assignment in C are technically redundant, but I consider finding the matching parenthesis to be straightforward (especially with text editor assistance), while I consider figuring out where the next lower precedence operator appears difficult (since I don't have the C operand precedence table memorized, and there isn't any simple way for my text editor to help me out). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Regarding the precedence of :=, at this point the only acceptable outcome for me is one where at the statement level, := and = can be used interchangeably and will have the same meaning (except probably we wouldn't allow combining both in a chained assignment). Because then we can say "and you can use assignment in expressions too, except there you must use := because = would be too easily confused with ==". Ideally this would allow all forms of = to be replaced with :=, even extended structure unpacking (a, b, *c := foo()). -- --Guido van Rossum (python.org/~guido)

On 13 April 2018 at 16:13, Guido van Rossum <guido@python.org> wrote:
If we're going this far, why not just replace assignment statements with assignment expressions using := completely, and leave the current assignment statement for backward compatibility only (to be removed at some point following a deprecation period)? That's not rhetorical - if you're taking this position, I genuinely don't know if you see any remaining advantages to the assignment statement other than backward compatibility. Personally, I don't like the idea - but I'm not sure if that's just because I'm so used to assignment as a statement that I'm reluctant to accept change... Paul

[David Mertz <mertz@gnosis.cx>]
But you don't really believe that ;-) That's the rub. In your specific None if var:= function() is None else var.method() example it was obvious := should have very high precedence _there_, but in an earlier _, x1, x2 = (D := b**2 - 4*a*c), (-b + sqrt(D))/2, (-b - sqrt(D))/2 it was just as obvious that you wanted := to have a very low precedence _there_ (else D would be bound to the value of plain old `b`). I don't at all mean to be picking on you there - I suffer the same illusions. It appears that "very low precedence" is actually wanted in nearly all examples, _except_ when comparison is involved. So maybe := "should have" precedence between comparison operators (<=, is, etc) and bitwise OR (|). Then both your examples above would do what you expected them to do (and what I expected them to do at first glance). OTOH, that wouldn't be at all like C, whose precedence rules Python largely follows (only the comma operator has lower precedence than assignment in C), and - worse - wouldn't be at all like Python assignment statements appear to behave.
But of course if it doesn't then expressions like the one I proposed could mean something quite different.
Whether very high or very low, one of your two examples above is screwed.
I find the third hand argument rather compelling. All those potentially required parentheses turn elegant looking code into an ugly mess.
It is a hangup for me - but I'm not yet sure how severe.

On Fri, Apr 13, 2018 at 1:55 PM, Tim Peters <tim.peters@gmail.com> wrote:
Oops, that's a mistake. I'll add that in a bit. It has extremely low precedence.
I just went to test it, and the unparenthesized version is actually bombing with SyntaxError. I'm going to call that a bug, though, and see about fixing it tonight. Currently, the precedence is so low that basically anything will get captured, and if you want to capture anything less than the entire expression, you parenthesize. I'll experiment with placing it between '|' and comparison operators, which would mean that an unparenthesized assignment expression generally won't capture a boolean, but will instead capture the value itself. That's likely to be more useful, I think. (Obviously you can always use parens to overrule the precedence table.)
Agreed as regards addition. Less certain as regards comparisons. if x := i > 1: is currently "if x := (i > 1)" and not "if (x := i) > 1". Assuming I don't run into implementation difficulties, I'll play with this tonight, then document it one way or the other, and maybe have an open question about the alternative precedence.
Agreed. There's no reason to mandate the parens here. Neither the PEP nor the reference implementation has any problem with this expression being unparenthesized. ChrisA

On Fri, Apr 13, 2018 at 1:00 PM, Thautwarm Zhao <yaoxiansamma@gmail.com> wrote:
What do you mean by "lazy"? The assignment happens at the exact point that it is reached. Is there another language that uses := to mean something where the assignment doesn't happen until later? Or where the expression is assigned unevaluated, and is evaluated later? What part of it is lazy? ChrisA
participants (10)
-
Alex Walters
-
Chris Angelico
-
David Mertz
-
Ethan Furman
-
Guido van Rossum
-
MRAB
-
Nick Coghlan
-
Paul Moore
-
Thautwarm Zhao
-
Tim Peters