Re: Make for/while loops nameable.

I agree with you that this feature can — and certainly will — be abused. But I don't think that's relevant: when you think about it, any feature can be abused. Example : variable are meant to allow a programmer to keep a reference to some data, using a meaningful label. However, have you never seen a piece of code where the variable names are so obscure you can't easily guess what they point to ? I guess you did . But variable aren't removed from any language for this reason ! The feature we're discussing now is, I think, a useful tool to be used parsimoniously when the need arises, like any other. I can see several advantages to having it in the language, as opposed to building an ad-hoc function : * performance : a call is not free, even less in Python * readability : defining a function or similar moves the code away from where it is actually needed. In the case of functions called only once, such as these, this loss in readability can't be balanced against the advantages of writing a function. That would be an example a misused feature imo. I don't think big nested loops are often reusable. Globally, I think the indexed/labeled loops would be a useful tool in imperative programming. Some other code styles, such as functional programming, wouldn't need it, but isn't Python's policy to remain an open language to allow you to code however you like it best ? Think of other features, such as coroutines: they are a powerful tool, yet dangerous and easily misused (I'm mostly talking about the `yield` expression, witch can be used both to throw some output at the caller /and/ to request input). However, coroutines /did/ make it into Python, and I think they should remain there, because they can really be useful and powerful if used with care ! The same reasoning applies to our case. TL;DR : consenting adults : the blame for misusing a feature is on the user, not on the language designer, especially when the feature is opt-in Regards, Alexis

I like the idea of named breaks, but I *hate* the idea of numerically labeled breaks, whether numbered from the inside or from the outside. On the occasions—which are actually relatively frequent—that I want to break all the way out of an inner loop, I wind up using a sentinel STOP variable. I know how to do that, but it's somewhat cumbersome with a few lines of distracting code and a few pointless variables. However, when I think about it, it's not a number of LEVELS I want, but a particular identified loop that I want to escape from. And yes, writing a function can be an approach to this. However, it's also often cumbersome to reason through exactly every name that needs to be captured as arguments, and often needs refactoring as the code is developed. Abusing a try/except structure is another approach. The problem with numbers, beyond just being harder to count out, is that they are fragile under refactoring. Maybe I start with: for thing in collection_1: for other in collection_2: if property(thing, other): break 2 # <- hypothetical numbered break But then I realize I need: for thing in collection_1: for other in collection_2: for more in other.stuff: if property(thing, other): break 2 # <- hypothetical numbered break (oops, wrong now!) With names this is just straightforward: for thing in collection_1 NAMED things: for other in collection_2 NAMED others: for more in other.stuff: if property(thing, other): break things # <- hypothetical named break My capitalized keyword is deliberately not optimal since I'm not trying to bikeshed syntax. But it doesn't matter whether I add or remove the loop over other.stuff for this to work. Of course if I find myself nesting loops 7 levels deep, I desperately need refactoring. This feature could certainly be abused, like almost all others. But for 3-4 levels of loops, refactoring to functions is often "heavier" than a 'break_things' sentinel Boolean variable. I've gotten in the habit of using itertools.product() when it is possible. But it isn't always possible... in fact, my example above is just a case because the 3rd level loop loops over something dependent on the object of the 2nd level loop. And product() is never going to work with while loops, which can also benefit from a nested break sometimes. Overall, I'm definitely +1 on adding named breaks. It depends on the syntax somewhat, but the concept would be useful. On the other hand, it's come up numerous times over the years, and never quite happened. And I've written programs perfectly well without it. -- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.

Le 04/12/2020 à 21:18, David Mertz a écrit :
I agree with you that the point of `break x` — labeled or numbered — is to target a specific loop to "jump" to, but I'm not comfortable with introducing labels to Python. The reason for this is that they look too much like identifiers, and should, therefore, be bound to /something/, following Python's motto of "everything is an object". I don't see what kind of an object could be bound to that, what kind of API it would expose, how it could be manipulated/re-affected, it behavior out of the loop… On the other hand, we could have them be just labels, not identifiers, and forbid them to be used out of `break x` statements. But then we'd have two kinds of "used-defined names" evolving in separate dimensions; I don't think that's a good thing, it would add too much confusion. Maybe we could distinguish the two with some kind of semantics bound in the name, start the loop labels with ":" or whatever; I don't like this take on things either, but I like it better. Now, the point of refactoring — good point indeed ! But I expect this kind of problem already arises with regular breaks ? However, I think that when adding code like you did (adding/removing a loop to nested loops), you should read through all of the inner code, if only to prevent variable name collisions, so there is really no problem for conscientious programmers. Of course, if you're not cautious, you risk bugging your program's control flow — from experience, the hardest to debug — but then, we're back at the dangerous tool/responsible use argument: if you can't handle it, don't use it, however the tool should be there for those of us who can. I also think that in most cases, the problem could be solved with a plain old comment next to the loop, saying "warning, there exists a long break to this loop". All this to say I'm not opposed to naming loops, but I really much rather would have them numbered (outwards, if possible). Regards, Alexis

On Sat, Dec 5, 2020 at 11:05 AM Alexis Masson <a.masson555@ntymail.com> wrote:
I don't really care how the labels are spelled, just that they are spelled on the line that defines the loop. For example: while not processed() as 1: for x in the_stuff as 37: if all_done(x): break 1 Numbers like 1 and 37 are pretty clearly not binding targets. By convention, using numbers 1, 2, 3, 4 for successive levels might be good. But it wouldn't break directly if you added or removed a nested loop as long as you kept its number. That said, allowing slightly more descriptive names than "2" seems sorta nice. Using sigils to make it "obviously not a binding target" is way too much ugly for the limited need. I don't think this feels Pythonic, but if the labels were exclusively one-digit positive integers, that would limit named loop depth to 9. That is a good thing. Anything over 4 levels is questionable, anything over 6 is monstrous. So that's extra leeway for anything that is actually a good idea. But I think "use a natural number" and rely on "we're all adults here" is more likely to fit in Python. -- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.

How about reserving unicode numeric superscript characters 0..9 as label identifiers only to be used for loop & break, etc. Then the example below would become: while¹ not processed(): for x in the_stuff as 37: if all_done(x): break¹ Steve Barnes From: David Mertz <mertz@gnosis.cx> Sent: 05 December 2020 17:32 To: Alexis Masson <a.masson555@ntymail.com> Cc: python-ideas <python-ideas@python.org> Subject: [Python-ideas] Re: Make for/while loops nameable. On Sat, Dec 5, 2020 at 11:05 AM Alexis Masson <a.masson555@ntymail.com<mailto:a.masson555@ntymail.com>> wrote: I agree with you that the point of `break x` — labeled or numbered — is to target a specific loop to "jump" to, but I'm not comfortable with introducing labels to Python. The reason for this is that they look too much like identifiers, and should, therefore, be bound to something, following Python's motto of "everything is an object". I don't see what kind of an object could be bound to that, what kind of API it would expose, how it could be manipulated/re-affected, it behavior out of the loop… I don't really care how the labels are spelled, just that they are spelled on the line that defines the loop. For example: while not processed() as 1: for x in the_stuff as 37: if all_done(x): break 1 Numbers like 1 and 37 are pretty clearly not binding targets. By convention, using numbers 1, 2, 3, 4 for successive levels might be good. But it wouldn't break directly if you added or removed a nested loop as long as you kept its number. That said, allowing slightly more descriptive names than "2" seems sorta nice. Using sigils to make it "obviously not a binding target" is way too much ugly for the limited need. I don't think this feels Pythonic, but if the labels were exclusively one-digit positive integers, that would limit named loop depth to 9. That is a good thing. Anything over 4 levels is questionable, anything over 6 is monstrous. So that's extra leeway for anything that is actually a good idea. But I think "use a natural number" and rely on "we're all adults here" is more likely to fit in Python. -- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.

I like how you two mixed "numbered loops" and "labeled loops" into "numbers as labels" 😂 Yeah, I could go with that: either free naming and a convention to use digits, or restricting the charset somewhat. I wouldn't go with "unicode numeric superscript characters 0..9", though, for the simple reason that I can't type them easily, with the exception of "²" (on an AZERTY keyboard). And now I have a syntactic remark: by reading your example code, I just realized I don't like the `as` keyword there, because `as` usually binds a value to a variable (`with ... as ...`, `import ... as ...`, `except ... as ...`). I don't have any better idea right now, though, but I'll let you know if I find anything better 😉 Cheers, Alexis

On Sat, Dec 5, 2020 at 6:20 PM Steve Barnes <GadgetSteve@live.co.uk> wrote:
It's cute. And really quite readable. But it's not writeable. I had to press 'ctrl-shift-u 0 0 B 2 enter' to fix the 2 superscript you omitted (the 'as' was still there). But that's not uniform, other people with different OS's and different keyboard drivers will need to press different combos, none of them just a keystroke. Sure, various specific text editors might be configurable to allow other different shortcuts for it, but that emphasizes the point, not mitigates it.

David Mertz writes:
It's cute.
Cute is for bunnies and Ghibli anime. :-)
And really quite readable. But it's not writeable.
Heh. They look like misplaced apostrophes on my screen in my English buffers. I'd have to go to a 1.3x (maybe 1.5x) font to be sure what I'm looking at. And I'm an Emacs user, so if I wanted to use this feature (I don't though, I think it's ugly), I'd rebind Ctrl-<single digit> to the superscripts. (I have a little sympathy for non-Emacs users, but really, America, wake up. Characters are good, use more!) So my reaction is the opposite of yours. I prefer the arbitrary tag, where I read for i in iterable as breakable: as "with (for i in iterable) as breakable" in my head. Although I'm basically -0.0 on the whole idea, in my experience anyway it's YAGNI. Steve -- ________________________________________________________________________ ________________________________________________________________________ What are those straight lines? "Diversity rules."

I like the idea of named breaks, but I *hate* the idea of numerically labeled breaks, whether numbered from the inside or from the outside. On the occasions—which are actually relatively frequent—that I want to break all the way out of an inner loop, I wind up using a sentinel STOP variable. I know how to do that, but it's somewhat cumbersome with a few lines of distracting code and a few pointless variables. However, when I think about it, it's not a number of LEVELS I want, but a particular identified loop that I want to escape from. And yes, writing a function can be an approach to this. However, it's also often cumbersome to reason through exactly every name that needs to be captured as arguments, and often needs refactoring as the code is developed. Abusing a try/except structure is another approach. The problem with numbers, beyond just being harder to count out, is that they are fragile under refactoring. Maybe I start with: for thing in collection_1: for other in collection_2: if property(thing, other): break 2 # <- hypothetical numbered break But then I realize I need: for thing in collection_1: for other in collection_2: for more in other.stuff: if property(thing, other): break 2 # <- hypothetical numbered break (oops, wrong now!) With names this is just straightforward: for thing in collection_1 NAMED things: for other in collection_2 NAMED others: for more in other.stuff: if property(thing, other): break things # <- hypothetical named break My capitalized keyword is deliberately not optimal since I'm not trying to bikeshed syntax. But it doesn't matter whether I add or remove the loop over other.stuff for this to work. Of course if I find myself nesting loops 7 levels deep, I desperately need refactoring. This feature could certainly be abused, like almost all others. But for 3-4 levels of loops, refactoring to functions is often "heavier" than a 'break_things' sentinel Boolean variable. I've gotten in the habit of using itertools.product() when it is possible. But it isn't always possible... in fact, my example above is just a case because the 3rd level loop loops over something dependent on the object of the 2nd level loop. And product() is never going to work with while loops, which can also benefit from a nested break sometimes. Overall, I'm definitely +1 on adding named breaks. It depends on the syntax somewhat, but the concept would be useful. On the other hand, it's come up numerous times over the years, and never quite happened. And I've written programs perfectly well without it. -- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.

Le 04/12/2020 à 21:18, David Mertz a écrit :
I agree with you that the point of `break x` — labeled or numbered — is to target a specific loop to "jump" to, but I'm not comfortable with introducing labels to Python. The reason for this is that they look too much like identifiers, and should, therefore, be bound to /something/, following Python's motto of "everything is an object". I don't see what kind of an object could be bound to that, what kind of API it would expose, how it could be manipulated/re-affected, it behavior out of the loop… On the other hand, we could have them be just labels, not identifiers, and forbid them to be used out of `break x` statements. But then we'd have two kinds of "used-defined names" evolving in separate dimensions; I don't think that's a good thing, it would add too much confusion. Maybe we could distinguish the two with some kind of semantics bound in the name, start the loop labels with ":" or whatever; I don't like this take on things either, but I like it better. Now, the point of refactoring — good point indeed ! But I expect this kind of problem already arises with regular breaks ? However, I think that when adding code like you did (adding/removing a loop to nested loops), you should read through all of the inner code, if only to prevent variable name collisions, so there is really no problem for conscientious programmers. Of course, if you're not cautious, you risk bugging your program's control flow — from experience, the hardest to debug — but then, we're back at the dangerous tool/responsible use argument: if you can't handle it, don't use it, however the tool should be there for those of us who can. I also think that in most cases, the problem could be solved with a plain old comment next to the loop, saying "warning, there exists a long break to this loop". All this to say I'm not opposed to naming loops, but I really much rather would have them numbered (outwards, if possible). Regards, Alexis

On Sat, Dec 5, 2020 at 11:05 AM Alexis Masson <a.masson555@ntymail.com> wrote:
I don't really care how the labels are spelled, just that they are spelled on the line that defines the loop. For example: while not processed() as 1: for x in the_stuff as 37: if all_done(x): break 1 Numbers like 1 and 37 are pretty clearly not binding targets. By convention, using numbers 1, 2, 3, 4 for successive levels might be good. But it wouldn't break directly if you added or removed a nested loop as long as you kept its number. That said, allowing slightly more descriptive names than "2" seems sorta nice. Using sigils to make it "obviously not a binding target" is way too much ugly for the limited need. I don't think this feels Pythonic, but if the labels were exclusively one-digit positive integers, that would limit named loop depth to 9. That is a good thing. Anything over 4 levels is questionable, anything over 6 is monstrous. So that's extra leeway for anything that is actually a good idea. But I think "use a natural number" and rely on "we're all adults here" is more likely to fit in Python. -- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.

How about reserving unicode numeric superscript characters 0..9 as label identifiers only to be used for loop & break, etc. Then the example below would become: while¹ not processed(): for x in the_stuff as 37: if all_done(x): break¹ Steve Barnes From: David Mertz <mertz@gnosis.cx> Sent: 05 December 2020 17:32 To: Alexis Masson <a.masson555@ntymail.com> Cc: python-ideas <python-ideas@python.org> Subject: [Python-ideas] Re: Make for/while loops nameable. On Sat, Dec 5, 2020 at 11:05 AM Alexis Masson <a.masson555@ntymail.com<mailto:a.masson555@ntymail.com>> wrote: I agree with you that the point of `break x` — labeled or numbered — is to target a specific loop to "jump" to, but I'm not comfortable with introducing labels to Python. The reason for this is that they look too much like identifiers, and should, therefore, be bound to something, following Python's motto of "everything is an object". I don't see what kind of an object could be bound to that, what kind of API it would expose, how it could be manipulated/re-affected, it behavior out of the loop… I don't really care how the labels are spelled, just that they are spelled on the line that defines the loop. For example: while not processed() as 1: for x in the_stuff as 37: if all_done(x): break 1 Numbers like 1 and 37 are pretty clearly not binding targets. By convention, using numbers 1, 2, 3, 4 for successive levels might be good. But it wouldn't break directly if you added or removed a nested loop as long as you kept its number. That said, allowing slightly more descriptive names than "2" seems sorta nice. Using sigils to make it "obviously not a binding target" is way too much ugly for the limited need. I don't think this feels Pythonic, but if the labels were exclusively one-digit positive integers, that would limit named loop depth to 9. That is a good thing. Anything over 4 levels is questionable, anything over 6 is monstrous. So that's extra leeway for anything that is actually a good idea. But I think "use a natural number" and rely on "we're all adults here" is more likely to fit in Python. -- The dead increasingly dominate and strangle both the living and the not-yet born. Vampiric capital and undead corporate persons abuse the lives and control the thoughts of homo faber. Ideas, once born, become abortifacients against new conceptions.

I like how you two mixed "numbered loops" and "labeled loops" into "numbers as labels" 😂 Yeah, I could go with that: either free naming and a convention to use digits, or restricting the charset somewhat. I wouldn't go with "unicode numeric superscript characters 0..9", though, for the simple reason that I can't type them easily, with the exception of "²" (on an AZERTY keyboard). And now I have a syntactic remark: by reading your example code, I just realized I don't like the `as` keyword there, because `as` usually binds a value to a variable (`with ... as ...`, `import ... as ...`, `except ... as ...`). I don't have any better idea right now, though, but I'll let you know if I find anything better 😉 Cheers, Alexis

On Sat, Dec 5, 2020 at 6:20 PM Steve Barnes <GadgetSteve@live.co.uk> wrote:
It's cute. And really quite readable. But it's not writeable. I had to press 'ctrl-shift-u 0 0 B 2 enter' to fix the 2 superscript you omitted (the 'as' was still there). But that's not uniform, other people with different OS's and different keyboard drivers will need to press different combos, none of them just a keystroke. Sure, various specific text editors might be configurable to allow other different shortcuts for it, but that emphasizes the point, not mitigates it.

David Mertz writes:
It's cute.
Cute is for bunnies and Ghibli anime. :-)
And really quite readable. But it's not writeable.
Heh. They look like misplaced apostrophes on my screen in my English buffers. I'd have to go to a 1.3x (maybe 1.5x) font to be sure what I'm looking at. And I'm an Emacs user, so if I wanted to use this feature (I don't though, I think it's ugly), I'd rebind Ctrl-<single digit> to the superscripts. (I have a little sympathy for non-Emacs users, but really, America, wake up. Characters are good, use more!) So my reaction is the opposite of yours. I prefer the arbitrary tag, where I read for i in iterable as breakable: as "with (for i in iterable) as breakable" in my head. Although I'm basically -0.0 on the whole idea, in my experience anyway it's YAGNI. Steve -- ________________________________________________________________________ ________________________________________________________________________ What are those straight lines? "Diversity rules."
participants (6)
-
Alexis Masson
-
Aveheuzed
-
David Mertz
-
Rob Cliffe
-
Stephen J. Turnbull
-
Steve Barnes