Re: [Python-ideas] Inline assignments using "given" clauses

I'd like to address Steven D'Aprano's reply earlier in the list concerning "given" vs ":=" clauses. My stance on the PEP is that the general ability to assign locally within an expression is undesirable. I gave several reasons, but in general, it means processing lines becomes more complex, and it makes refactoring more difficult. I don't think that there are enough cases where it would be useful to justify its negatives. I can recall but several occasions (writing regexes, mainly) that I've wanted something like this. However, if we were to assert that PEP 572 was mandatory, and then dispute its implementation, then I would argue as follows: *Cost:* A lot of people seem quite fixed upon the character cost of "given" vs ":=". I think this is a straw man. Most people spend longer reading than writing code, so if you agree with that observation, then it's the line length that you are concerned with. While it is true that overly long lines are not desirable, it's also true that incredibly short lines are equally difficult to understand. I don't think that keyword length can be independently taken too seriously as support for any proposal. Once you have to include grouping parentheses, this length difference quickly diminishes anyway. *Repetition of variable:* In addition, several people seem unhappy that the "given" keyword approach sees a repetition of the state variable in question. I think this is actually a benefit for two reasons. Firstly, you can save the expression in question to the intermediate variable, and then access a sub expression in a condition e.g while value.in_use given value = get_next_pool_item(): print(value.refcount()) Instead of while (value:=get_next_pool_item()) and value.in_use: print(value.refcount()) Secondly, it reads in the order that matters. When reading the first line, one encounters what the condition is evaluating *first*, and then the implementation details (m=p.match) second. It reads as one would describe a mathematical equation in a paper, and clearly separates *what you're interested in* from *what it depends upon*. This is what I particularly dislike about the ":=" operator approach, the value, and name it is bound to, are unrelated at the point of evaluation, yet are right next to each other. It's visual noise. In the example above, the reader has to read the entire line when trying to find the loop condition. Someone earlier suggested this was a bad thing, that the expression was far to the right of the line. I disagree. In cases where you might want to assign an expression to a variable, it is going to be used at least twice (otherwise just use the expression directly). At this point, the target name should be explicit in what it represents. This depends upon context slightly, such as what is happening in the local region of code. An example; when matching regexes, the match conditions (if x.group(2) == ...) are more important than what you matched on, usually. In addition, the two cases are not identical - if the API is such that get_next_pool_item should not return None values, this wouldn't be caught in the ":=" approach, unless you add more conditions. Yes, you could argue that I've cherry picked an example here. Actually, I haven't; I observed this after writing the example. What am I getting at here? In effect, the "given" keyword provides a superset of use cases to that of ":=". Dare I say it, but *explicit is better than implicit*. *Readability:* A smaller point is that I don't feel that ":=" is very readable. If we had to use an operator, I think $= is better, but me reasoning for this is weak. I think it derives from my observation that ":=" is slow to distinguish from "=". Regards, Angus Hollands On Fri, 11 May 2018 at 17:45 <python-ideas-request@python.org> wrote:
Send Python-ideas mailing list submissions to python-ideas@python.org
To subscribe or unsubscribe via the World Wide Web, visit https://mail.python.org/mailman/listinfo/python-ideas or, via email, send a message with subject or body 'help' to python-ideas-request@python.org
You can reach the person managing the list at python-ideas-owner@python.org
When replying, please edit your Subject line so it is more specific than "Re: Contents of Python-ideas digest..."
Today's Topics:
1. Re: Inline assignments using "given" clauses (Steven D'Aprano) 2. Re: Inline assignments using "given" clauses (Guido van Rossum) 3. Re: Inline assignments using "given" clauses (Guido van Rossum) 4. Re: Inline assignments using "given" clauses (Steven D'Aprano) 5. Re: Inline assignments using "given" clauses (Tim Peters)
----------------------------------------------------------------------
Message: 1 Date: Sat, 12 May 2018 02:17:24 +1000 From: Steven D'Aprano <steve@pearwood.info> To: python-ideas@python.org Subject: Re: [Python-ideas] Inline assignments using "given" clauses Message-ID: <20180511161724.GX9562@ando.pearwood.info> Content-Type: text/plain; charset=us-ascii
On Fri, May 11, 2018 at 12:37:43PM +0100, Rhodri James wrote:
Consider:
while (cmd := get_command()).token != CMD_QUIT: cmd.do_something()
vs:
while cmd.token != CMD_QUIT given cmd = get_command(): cmd.do_something()
Okay, considered. I think the first is preferable.
Much earlier in the PEP 572 discussion, I strongly argued in favour of the
expr as name
syntax on the basis that the most important part of the overall expression is "expr", not the assignment target, and therefore that should come first. Even though I have accepted that "as" is not viable, I still believe that it is preferable to have the expression first, or if not first, at least as close to the left as we can get it.
This "given" syntax puts the expr part all the way to the far right of the line. A line which is made all the longer for needing to use "given" and redundantly state the target name.
It's like we're trying to maximize the distance the eye has to travel back and forth when reading.
I have to read to the end of the line before I have any idea where cmd has come from or what it is. The fact that it comes from a "given" expression comes as a surprise at the end of the line.
Now obviously this doesn't matter if I'm reading lines of code in careful detail, but I don't do that all the time. I skim code far more than I read it in careful detail, and the closer things are to the left, the more likely I am to see them while skimming. The further out they are, the easier they are to miss.
I think that "given" will *literally* make reading harder, in that the eye has to travel further to spot the relevant expression while skimming over code. As I said, I don't think it makes any difference when reading closely in detail. But most of my reading of code is skimming to find the relevant line or section, and then read closely. I would probably skim a hundred lines for every one I read closely.
We read more code than we write, but writing is important too. I think the verbosity of "given" (six chars versus two) and the redundancy of needing to repeat the name of the target even if you only use it once will soon make using this syntax seem like a chore.
-- Steve
------------------------------
Message: 2 Date: Fri, 11 May 2018 12:25:46 -0400 From: Guido van Rossum <guido@python.org> To: Greg Ewing <greg.ewing@canterbury.ac.nz> Cc: python-ideas <python-ideas@python.org> Subject: Re: [Python-ideas] Inline assignments using "given" clauses Message-ID: <CAP7+vJLR9N4N1g4U3T-DgYGMHAM_a= inDi7dFiF7s5R7DrAQXw@mail.gmail.com> Content-Type: text/plain; charset="utf-8"
Maybe you could propose some kind of syntax using "whereas"? (It can be used as a preamble.)
On Fri, May 11, 2018 at 3:05 AM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Guido van Rossum wrote:
I'd need well-reasoned explanations
My reasoning is essentially the same as what I've already said about "where". To summarise, "given" sounds like something an English-speaking mathematician would write, whereas ":=" doesn't even have an obvious pronunciation. Some variation on "given" just seems greatly more pythonic to me.
-- Greg
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)

On Sat, May 12, 2018 at 7:12 AM, Angus Hollands <goosey15@gmail.com> wrote:
e.g
while value.in_use given value = get_next_pool_item(): print(value.refcount())
Instead of
while (value:=get_next_pool_item()) and value.in_use: print(value.refcount())
In addition, the two cases are not identical - if the API is such that get_next_pool_item should not return None values, this wouldn't be caught in the ":=" approach, unless you add more conditions. Yes, you could argue that I've cherry picked an example here. Actually, I haven't; I observed this after writing the example.
What am I getting at here? In effect, the "given" keyword provides a superset of use cases to that of ":=". Dare I say it, but explicit is better than implicit.
I'm not sure what you're getting at here. To make your two versions equivalent, you'd need to write them like this: while value and value.in_use given value = get_next_pool_item(): print(value.refcount()) while (value:=get_next_pool_item()) and value.in_use: print(value.refcount()) or like this: while value.in_use given value = get_next_pool_item(): print(value.refcount()) while (value:=get_next_pool_item()).in_use: print(value.refcount()) There, now they're identical. There's no superset or subset of use cases. And I have no idea how that connects with the oft-misused "explicit is better than implicit". (I'm still fairly sure that "explicit" and "strongly typed" are both synonyms for "stuff I like", with their antonyms "implicit" and "weakly typed" both being synonyms for "stuff I don't like". Years of discussion have not disproven this theory yet.) ChrisA

On Sat, May 12, 2018 at 07:44:24AM +1000, Chris Angelico wrote:
(I'm still fairly sure that "explicit" and "strongly typed" are both synonyms for "stuff I like", with their antonyms "implicit" and "weakly typed" both being synonyms for "stuff I don't like". Years of discussion have not disproven this theory yet.)
That's certainly how they are used the great majority of the time. A bit like how "strawman argument" is mostly used to mean "dammit, you just spotted an unwelcome consequence and/or flaw in my position which I have no counter too". :-) -- Steve

On 2018-05-11 22:12, Angus Hollands wrote:
while (value:=get_next_pool_item()) and value.in_use: print(value.refcount())
Just as a heads-up, I believe the prescribed way of doing that is: while (value := get_next_pool_item()).in_use: Of course you'd need additional mess to do something else with value. I don't like the asymmetry here: while (value := get_next_pool_item()).in_use and value is not blah:
Secondly, it reads in the order that matters. When reading the first line, one encounters what the condition is evaluating *first*, and then the implementation details (m=p.match) second. It reads as one would describe a mathematical equation in a paper, and clearly separates *what you're interested in* from *what it depends upon*. This is what I particularly dislike about the ":=" operator approach, the value, and name it is bound to, are unrelated at the point of evaluation, yet are right next to each other. It's visual noise. In the example above, the reader has to read the entire line when trying to find the loop condition.
I'm inclined to agree. But several people have argued that this is more readable than the alternative. I don't buy the reasoning, but they still like it better, and there's probably no point in going any further into this aspect. I doubt people are going to be convinced.
What am I getting at here? In effect, the "given" keyword provides a superset of use cases to that of ":=". Dare I say it, but *explicit is better than implicit*.
I'm not sure that it's strictly a superset. It's arguably the reverse, since it's restricted to statements with a condition rather than arbitrary expressions. I think the more important thing is that it's--subjectively--better at the subset of use cases that people seem to actually have (as listed in the OP).
*Readability:* A smaller point is that I don't feel that ":=" is very readable. If we had to use an operator, I think $= is better, but me reasoning for this is weak. I think it derives from my observation that ":=" is slow to distinguish from "=".
Clearly the objectively best choice is "<-".

On Sat, May 12, 2018 at 7:56 AM, Ed Kellett <e+python-ideas@kellett.im> wrote:
*Readability:* A smaller point is that I don't feel that ":=" is very readable. If we had to use an operator, I think $= is better, but me reasoning for this is weak. I think it derives from my observation that ":=" is slow to distinguish from "=".
Clearly the objectively best choice is "<-".
It's already legal syntax. That's an advantage, right? ChrisA

On 5/11/18 5:12 PM, Angus Hollands wrote:
*Readability:* A smaller point is that I don't feel that ":=" is very readable. If we had to use an operator, I think $= is better, but me reasoning for this is weak. I think it derives from my observation that ":=" is slow to distinguish from "=".
:= would prevent you from using assignment expressions inside f-strings, which could be argued is a good thing. To demonstrate, and just for giggles, this works in 3.6, and appears to have the desired behavior: -------------------------- class X: def __init__(self, value): self.value = value def __str__(self): return str(self.value) def __format__(self, fmt): assert fmt[0] == '=' self.value = eval(fmt[1:]) return '' x = X(3) print(x) f'{x:=4}' # Behold! print(x) -------------------------- Produces: 3 4 I kid, of course. Eric

2018-05-12 11:36 GMT+03:00 Angus Hollands <goosey15@gmail.com>:
Concerning my previous email,
Yes, my mistake. I'd forgotten (ironically) that the whole point is that it's an expression itself.
So
while (value:=get_next_pool_item()).in_use: print(value.refcount()) would be the appropriate analogue.
Consequently, my example is invalid. A better example would have been where one needs to access more than one attribute of the expression
while (node.x, node.y) > (5.0, 5.0) given node = get_neighbours(node): pass
In addition, this form gives you all the advantages of tuple unpacking: while (x, y) > (5.0, 5.0) given x, y = get_neighbours(node): pass There was some criticism about the length of the `given`, maybe it is possible to _repurpose_ `with` keyword: while (x, y) > (5.0, 5.0) with x, y = get_neighbours(node): pass In this context, the with statement and with expression are clearly distinguishable both for the parser and for the person. But maybe many will find this as a bad style ... since the semantics are too different. With kind regards, -gdg
participants (6)
-
Angus Hollands
-
Chris Angelico
-
Ed Kellett
-
Eric V. Smith
-
Kirill Balunov
-
Steven D'Aprano