Nudging beginners towards a more accurate mental model for loop else clauses

(context for python-ideas: my recently checked in changes to the tutorial, that added the final paragraph to http://docs.python.org/tutorial/controlflow.html#break-and-continue-statemen...) On Fri, Jun 8, 2012 at 5:29 PM, Stephen J. Turnbull <stephen@xemacs.org> wrote:
Note: reply-to set to python-ideas.
Nick Coghlan writes:
> The inaccuracies in the analogy are why this is in the tutorial, not the > language reference. All 3 else clauses are really their own thing.
Nick, for the purpose of the tutorial, actually there are 4 else clauses: you need to distinguish *while* from *for*. It was much easier for me to get confused about *for*.
The only thing I'm trying to do with the tutorial update is to encourage beginners to be start thinking in terms of try/except/else when they first encounter for/break/else and while/break/else. That's it. Yes, ultimately once people fully understand how it works under the hood (including the loop-and-a-half construct for infinite while loops), they'll release it's actually closely related to conditionals as well, but anyone that places too much weight on the following obvious parallel is going to be confused for a long time. After all: if iterable: ... else: ... is *very* similar in appearance to: for x in iterable: ... else: ... I believe that parallel is 99% of the reason why people get confused about the meaning of the latter. The point of the tutorial update is to give readers a slight nudge towards thinking of the latter as: for x in iterable: ... except break: # Implicit in the semantics of loops pass else: ... Would it be worth adding the "except break:" clause to the language just to make it crystal clear what is actually going on? I don't think so, but it's still a handy way to explain the semantics while gently steering people away from linking for/else and if/else too closely. I actually agree all of the else clauses really *are* quite closely related (hence the consistent use of the same keyword), but the relationship is *not* the intuitively obvious one that comes to mind when you just look at the similarity in the concrete syntax specifically of for/else and if/else. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 08/06/2012 10:04, Nick Coghlan wrote:
(context for python-ideas: my recently checked in changes to the tutorial, that added the final paragraph to http://docs.python.org/tutorial/controlflow.html#break-and-continue-statemen...)
On Fri, Jun 8, 2012 at 5:29 PM, Stephen J. Turnbull<stephen@xemacs.org> wrote:
Note: reply-to set to python-ideas.
Nick Coghlan writes:
The inaccuracies in the analogy are why this is in the tutorial, not the language reference. All 3 else clauses are really their own thing.
Nick, for the purpose of the tutorial, actually there are 4 else clauses: you need to distinguish *while* from *for*. It was much easier for me to get confused about *for*. The only thing I'm trying to do with the tutorial update is to encourage beginners to be start thinking in terms of try/except/else when they first encounter for/break/else and while/break/else. That's it.
Yes, ultimately once people fully understand how it works under the hood (including the loop-and-a-half construct for infinite while loops), they'll release it's actually closely related to conditionals as well, but anyone that places too much weight on the following obvious parallel is going to be confused for a long time. After all:
if iterable: ... else: ...
is *very* similar in appearance to:
for x in iterable: ... else: ...
I believe that parallel is 99% of the reason why people get confused about the meaning of the latter.
The point of the tutorial update is to give readers a slight nudge towards thinking of the latter as:
for x in iterable: ... except break: # Implicit in the semantics of loops pass else: ...
Would it be worth adding the "except break:" clause to the language just to make it crystal clear what is actually going on? I don't think so, but it's still a handy way to explain the semantics while gently steering people away from linking for/else and if/else too closely. I actually agree all of the else clauses really *are* quite closely related (hence the consistent use of the same keyword), but the relationship is *not* the intuitively obvious one that comes to mind when you just look at the similarity in the concrete syntax specifically of for/else and if/else.
Cheers, Nick.
I think a better scheme would be to have more meaningful keywords or keyword-combinations, e.g. for x in iterable: # do stuff ifempty: # or perhaps ifnoiter: (also applicable to while loops) # do stuff #ifbreak: # do stuff #ifnobreak: # do stuff which would give all the flexibility while making it reasonably clear what was happening. Rob Cliffe

On Fri, Jun 8, 2012 at 7:44 PM, Rob Cliffe <rob.cliffe@btinternet.com> wrote:
I think a better scheme would be to have more meaningful keywords or keyword-combinations, e.g.
for x in iterable: # do stuff ifempty: # or perhaps ifnoiter: (also applicable to while loops) # do stuff #ifbreak: # do stuff #ifnobreak: # do stuff
which would give all the flexibility while making it reasonably clear what was happening.
The way to be clear would actually be to drop the feature altogether (as Guido has noted in the past). Then TOOWTDI becomes: x = _no_data = object() result = _not_found = object() for x in iterable: if acceptable(x): result = x break if x is _no_data: # No data! if result is _not_found: # Nothing interesting! # Found a result, process it That's never going to happen in Python though, due to backwards compatibility requirements. FWIW, I wrote an essay summarising some of the thoughts presented in these threads: http://readthedocs.org/docs/ncoghlan_devs-python-notes/en/latest/python_conc... Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 08/06/2012 12:53, Nick Coghlan wrote:
On Fri, Jun 8, 2012 at 7:44 PM, Rob Cliffe<rob.cliffe@btinternet.com> wrote:
I think a better scheme would be to have more meaningful keywords or keyword-combinations, e.g.
for x in iterable: # do stuff ifempty: # or perhaps ifnoiter: (also applicable to while loops) # do stuff #ifbreak: # do stuff #ifnobreak: # do stuff
which would give all the flexibility while making it reasonably clear what was happening. The way to be clear would actually be to drop the feature altogether (as Guido has noted in the past). Then TOOWTDI becomes:
x = _no_data = object() result = _not_found = object() for x in iterable: if acceptable(x): result = x break if x is _no_data: # No data! if result is _not_found: # Nothing interesting! # Found a result, process it
That's never going to happen in Python though, due to backwards compatibility requirements.
FWIW, I wrote an essay summarising some of the thoughts presented in these threads: http://readthedocs.org/docs/ncoghlan_devs-python-notes/en/latest/python_conc...
Cheers, Nick.
Fair enough, but I think my more compact versions are more readable. Rob

On Fri, Jun 8, 2012 at 10:05 PM, Rob Cliffe <rob.cliffe@btinternet.com> wrote:
Fair enough, but I think my more compact versions are more readable.
At the expense of adding 3 new keywords to the language for something that can already be handled with ordinary variable assignments. They would add no real expressive power to the language, so they just become another special case for newcomers to learn. Not a good trade-off. With the benefit of hindsight, we can also see that supporting the "else" clause on loops wasn't a good trade-off either (given the confusion it can cause). However, since the mistake has already been made, a lot of code out in the wild relies on it and those of us that quite like the construct are used to having it available, it's not worth the hassle of deprecating and removing it. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Rob Cliffe writes:
On 08/06/2012 10:04, Nick Coghlan wrote:
Would it be worth adding the "except break:" clause to the language just to make it crystal clear what is actually going on?
-1 I understood what "except break:" was supposed to mean when I read it the first time, but now I don't any more.
I don't think so, but it's still a handy way to explain the semantics while gently steering people away from linking for/else and if/else too closely.
My main point about documentation is that for/else and if/else should not be linked directly, but rather via while/else.
I think a better scheme would be to have more meaningful keywords or keyword-combinations, e.g.
for x in iterable: # do stuff ifempty: # or perhaps ifnoiter: (also applicable to while loops) # do stuff #ifbreak: # do stuff #ifnobreak: # do stuff
which would give all the flexibility while making it reasonably clear what was happening.
Sure, but that's way overboard for something that's only rarely useful.

On Fri, 08 Jun 2012 21:53:18 +0900 "Stephen J. Turnbull" <stephen@xemacs.org> wrote:
My main point about documentation is that for/else and if/else should not be linked directly, but rather via while/else.
Right. That was the most enlightening comment I saw in this thread. Writing the if/else and while/else out as: if condition: # code to run if condition is true else: # code to run if condition is false while condition: # code to run while condition is true else: # code to run when condition is false Seems obvious enough to me. For is a little bit harder, but still a straightforward if you think about it in terms of the while. for x in iterable: # code to run while there are objects left in iterable else: # code to run when there are no objects left in iterable. <mike -- Mike Meyer <mwm@mired.org> http://www.mired.org/ Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

Just to add another attempt at explaining the for/else confusion, the analogy that keeps it straight in my mind is that the "else" is really paired with the "if .. break" inside the loop: http://nedbatchelder.com/blog/201110/forelse.html --Ned. On 6/8/2012 5:04 AM, Nick Coghlan wrote:
(context for python-ideas: my recently checked in changes to the tutorial, that added the final paragraph to http://docs.python.org/tutorial/controlflow.html#break-and-continue-statemen...)
On Fri, Jun 8, 2012 at 5:29 PM, Stephen J. Turnbull<stephen@xemacs.org> wrote:
Note: reply-to set to python-ideas.
Nick Coghlan writes:
The inaccuracies in the analogy are why this is in the tutorial, not the language reference. All 3 else clauses are really their own thing.
Nick, for the purpose of the tutorial, actually there are 4 else clauses: you need to distinguish *while* from *for*. It was much easier for me to get confused about *for*. The only thing I'm trying to do with the tutorial update is to encourage beginners to be start thinking in terms of try/except/else when they first encounter for/break/else and while/break/else. That's it.
Yes, ultimately once people fully understand how it works under the hood (including the loop-and-a-half construct for infinite while loops), they'll release it's actually closely related to conditionals as well, but anyone that places too much weight on the following obvious parallel is going to be confused for a long time. After all:
if iterable: ... else: ...
is *very* similar in appearance to:
for x in iterable: ... else: ...
I believe that parallel is 99% of the reason why people get confused about the meaning of the latter.
The point of the tutorial update is to give readers a slight nudge towards thinking of the latter as:
for x in iterable: ... except break: # Implicit in the semantics of loops pass else: ...
Would it be worth adding the "except break:" clause to the language just to make it crystal clear what is actually going on? I don't think so, but it's still a handy way to explain the semantics while gently steering people away from linking for/else and if/else too closely. I actually agree all of the else clauses really *are* quite closely related (hence the consistent use of the same keyword), but the relationship is *not* the intuitively obvious one that comes to mind when you just look at the similarity in the concrete syntax specifically of for/else and if/else.
Cheers, Nick.

Le 08/06/2012 11:04, Nick Coghlan a écrit :
The only thing I'm trying to do with the tutorial update is to encourage beginners to be start thinking in terms of try/except/else when they first encounter for/break/else and while/break/else. That's it.
I don't see why you're trying to draw that analogy, since a loop has nothing in common with a try block. For the record, when I was a Python beginner, I had zero problem understanding the for/else construct, and it even struck me as very useful ("oh, they've thought about a clean and easy way to write search-and-break loops"). I don't think it's useful to think of beginners as people having comprehension problems. Besides, if you don't understand something up front, there's always the possibility to come back later. Regards Antoine.

2012/6/8 Antoine Pitrou <solipsis@pitrou.net>:
Le 08/06/2012 11:04, Nick Coghlan a écrit :
The only thing I'm trying to do with the tutorial update is to encourage beginners to be start thinking in terms of try/except/else when they first encounter for/break/else and while/break/else. That's it.
I don't see why you're trying to draw that analogy, since a loop has nothing in common with a try block. For the record, when I was a Python beginner, I had zero problem understanding the for/else construct, and it even struck me as very useful ("oh, they've thought about a clean and easy way to write search-and-break loops").
I don't think it's useful to think of beginners as people having comprehension problems. Besides, if you don't understand something up front, there's always the possibility to come back later.
Regards
Antoine.
+1. I also didn't have problems while I was learning python, and always found for/else very expressive as a statement. for/else is not immediately clear, meaning it is mandatory to read the doc in order to understand what it does and what to expect, but once you do that then you're done. --- Giampaolo http://code.google.com/p/pyftpdlib/ http://code.google.com/p/psutil/ http://code.google.com/p/pysendfile/

Nick Coghlan wrote:
for x in iterable: ... except break: # Implicit in the semantics of loops pass else: ...
Would it be worth adding the "except break:" clause to the language just to make it crystal clear what is actually going on? I don't think so, but it's still a handy way to explain the semantics while gently
I agree that it is *not* worthwhile. The main reason is that "except break" would add a new and different form of confusion (or at least complication): what happens when you return or raise from inside the loop rather than break? If "except break" *only* executes after a break (like it says!) that opens the door to "except return" and "except raise". Bleh. I really don't think we need this level of complication in loops. But if "except break" runs on *any* early exit from the loop (break, return or raise), then the name is misleading and confusing and we now have a new and exciting education problem to replace the old one. (Albeit probably a simpler problem.)
steering people away from linking for/else and if/else too closely. I actually agree all of the else clauses really *are* quite closely related (hence the consistent use of the same keyword), but the
I'm not so sure that they are that close, except in the trivial sense of having two alternatives, "A happens, otherwise B happens". In the case of for/else, the A is implied (a break, return or raise), which makes it rather different from if/else where both alternatives are explicit. Despite the similarity with try/else, I think it is quite a stretch to link the semantics of for/else with the word "else". It simply is not a good choice of keyword. If it were, we wouldn't be having this discussion. Although it would have cost an additional keyword, I think that for/else and while/else should have been written as for/then and while/then, since that accurately describes what they do (unless you're Dutch *wink*). for x in seq: ... then: ... There would be no implication that the "then" clause is executed *instead of* the loop part, instead the natural implication is that it is executed *after* the loop. (And then we could have introduced an "else" clause to do what people expect the else clause to do, namely run if the loop doesn't run.) -- Steven

On Fri, Jun 8, 2012 at 12:04 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
(context for python-ideas: my recently checked in changes to the tutorial, that added the final paragraph to
http://docs.python.org/tutorial/controlflow.html#break-and-continue-statemen... )
If we're on that subject then I think this
Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement.
Loop statements may have an else clause; it is executed immediately after
Doesn't hit the "break" nail on the head fast and hard enough in my opinion. I'd replace it with something like: the loop but is skipped if the loop was terminated by a break statement. Yuval

On 6/8/2012 6:34 PM, Yuval Greenfield wrote:
Loop statements may have an else clause; it is executed immediately after the loop but is skipped if the loop was terminated by a break statement.
As I said in my reply on pydev, that is misleading. The else clause executes if and when the loop condition is false. Period. Simple rule. It will not execute if the loop is exited by break OR if the loop is exited by return OR if the loop is exited by raise OR if the loop never exits. (OR is the loop is aborted by external factors.) As far as else is concerned, there is nothing special about break exits compared to return or raise exits. But Nick's doc addition and your alternative imply otherwise. One could read Nick's statement and your paraphrase as suggesting that the else will by executed if the loop is exited by return (like the finally of try) or raise (like the except of try). And that is wrong. -- Terry Jan Reedy

On Jun 9, 2012 10:16 AM, "Terry Reedy" <tjreedy@udel.edu> wrote:
On 6/8/2012 6:34 PM, Yuval Greenfield wrote:
Loop statements may have an else clause; it is executed immediately after the loop but is skipped if the loop was terminated by a break statement.
As I said in my reply on pydev, that is misleading. The else clause
executes if and when the loop condition is false. Period. Simple rule.
It will not execute if the loop is exited by break OR if the loop is
exited by return OR if the loop is exited by raise OR if the loop never exits. (OR is the loop is aborted by external factors.) As far as else is concerned, there is nothing special about break exits compared to return or raise exits.
But Nick's doc addition and your alternative imply otherwise. One could
read Nick's statement and your paraphrase as suggesting that the else will by executed if the loop is exited by return (like the finally of try) or raise (like the except of try). And that is wrong. An else clause on a try statement doesn't execute in any of those cases either. I'm not assuming beginners are idiots, I'm assuming they're making a perfectly logical connection that happens to be wrong. Cheers, Nick. -- Sent from my phone, thus the relative brevity :)
-- Terry Jan Reedy
_______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas

Terry Reedy wrote:
On 6/8/2012 6:34 PM, Yuval Greenfield wrote:
Loop statements may have an else clause; it is executed immediately after the loop but is skipped if the loop was terminated by a break statement.
As I said in my reply on pydev, that is misleading.
Why is it misleading? It is *incomplete* insofar as it assumes the reader understands that (in the absence of try...finally) a return or raise will immediately exit the current function regardless of where in the function that return/raise happens to be. I think that's a fair assumption to make. Other than that, Yuval's description seems both correct and simple to me. It precisely matches the semantics of for/else and while/else without introducing any additional complexity. The only thing it doesn't do is rationalise why the keyword is called "else" instead of a less confusing name.
The else clause executes if and when the loop condition is false. Period. Simple rule.
What is "the loop condition" in a for-loop? If you mean "when the iterable is false (empty)", that's simply incorrect, and is precisely the common error that many people make. If on the other hand you are talking about the reader mentally converting a for-loop to an imaginary while-loop in their head, I hardly call that "simple". It wouldn't be simple even if for/else loops actually were implemented internally as a while loop. If you mean something else, I have no idea what that could possibly be.
It will not execute if the loop is exited by break OR if the loop is exited by return OR if the loop is exited by raise OR if the loop never exits. (OR is the loop is aborted by external factors.) As far as else is concerned, there is nothing special about break exits compared to return or raise exits.
Right. Do we really need to explicitly document all of that under for/else? Surely we are allowed to assume a certain basic level of understanding of Python semantics -- not every page of the docs has to cover the fundamentals. This kind of reminds me of the scene in "Red Dwarf" where Holly the computer is explaining to Lister that he is the last surviving crew member of the skip and that everyone else is dead. Paraphrasing: Holly: They're all dead. Everybody's dead, Dave. Lister: Peterson isn't, is he? Holly: Everybody's dead, Dave! Lister: Not Chen! Holly: Yes, Chen. Everyone. Everybody's dead, Dave! Lister: Rimmer? Holly: He's dead, Dave. Everybody is dead. Everybody is dead, Dave. Lister: Wait. Are you trying to tell me everybody's dead? Yes Dave, a return will exit a for-loop without executing the code that follows. *wink* -- Steven

On Fri, Jun 8, 2012 at 10:31 PM, Steven D'Aprano <steve@pearwood.info> wrote:
Why is it misleading? It is *incomplete* insofar as it assumes the reader understands that (in the absence of try...finally) a return or raise will immediately exit the current function regardless of where in the function that return/raise happens to be. I think that's a fair assumption to make.
How can the reader understand that, when the reader doesn't know that return or raise exist yet? The assumption that the reader understands basic Python is unreasonable. This is the tutorial. As I understand the objection, it is misleading in that it puts the focus on the wrong thing. It says "it's skipped by a break", as if that were special. It's skipped by a lot of things that aren't mentioned, the really interesting thing is when it _isn't_ skipped, which is glossed over. It is implied that this happens whenever it is exited by anything other than break, but of course that isn't true, and you have to think "well, what about return and raise?" However, as mentioned above, no student will ever think about return and raise, because those constructs have not been introduced yet. I wonder if they will just internalize "except not when left by break"? That would be awful! Anyway, I'm not really an expert on writing technical documentation I would expect that it's better to not force the reader to remember information and think about implications, if we can say flat-out exactly what happens. Even if they can do it successfully, surely it is annoying? If you want to mention break up-front, why not reverse the clause order? Currently the phrasing is this: Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement. It could also be (something like) this: Loop statements may have an else clause; it is not executed when the loop is terminated by a break statement; it is only executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while). This reads backwards to me, because the clarifying information is listed before the main fact. Also it's a terrible sentence (my fault, the original was long but didn't have three independent clauses). But hey. Or you could split it up into two sentences: Loop statements may have an else clause, which is only executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while). The else clause is *not* executed when the loop is terminated by a ``break``, or any other control flow construct you will see. And so on. Lots of room to play around with how the information gets across without sacrificing the core fact of how else works. If that core fact is unworkable and does more harm than good, then I guess it has to go, though. Lying-to-children is a well-worn and useful didactic technique. -- Devin

Devin Jeanpierre wrote:
On Fri, Jun 8, 2012 at 10:31 PM, Steven D'Aprano <steve@pearwood.info> wrote:
Why is it misleading? It is *incomplete* insofar as it assumes the reader understands that (in the absence of try...finally) a return or raise will immediately exit the current function regardless of where in the function that return/raise happens to be. I think that's a fair assumption to make.
How can the reader understand that, when the reader doesn't know that return or raise exist yet? The assumption that the reader understands basic Python is unreasonable. This is the tutorial.
If the reader doesn't know that return or raise exist, they are hardly going to draw conclusions about the behaviour of for/else when a return or raise is reached. If they do know about return and raise, they should understand that return and raise skip everything, not just for/else.
As I understand the objection, it is misleading in that it puts the focus on the wrong thing. It says "it's skipped by a break", as if that were special. It's skipped by a lot of things that aren't mentioned, the really interesting thing is when it _isn't_ skipped, which is glossed over.
I don't think it is glossed over at all. You cut out my quote of Yuval's description, here it is again: Loop statements may have an else clause; it is executed immediately after the loop but is skipped if the loop was terminated by a break statement. The "really interesting thing" is the first thing about the else clause mentioned: it is executed immediately after the loop. The break statement *really is special* and deserves to be singled out for mention. The break statement is the only way to exit the *entire* for-loop construct (including the else) without exiting the entire function, or halting execution.
It is implied that this happens whenever it is exited by anything other than break, but of course that isn't true,
I strongly disagree that it implies anything of the sort. We're should assume the readers are beginners, but not idiots. Ignoring try/finally blocks, which are special, we can assume that the reader has (or will have once they actually learn about functions and exceptions) a correct understanding of the behaviour of return and raise. - If the loop is exited by a return, *nothing* following the return is executed. That includes the else block. - If execution is halted by an exception (including raise), *nothing* following the exception is executed. That includes the else block. - If execution is halted by an external event that halts or interrupts the Python process, *nothing* following executes. That includes the else block. - If the loop never completes, *nothing* following the loop executes. That includes the else block. To continue the analogy with the "Red Dwarf" quote I made earlier: "What about assignments after the loop?" "No, they aren't executed. Nothing is executed." "Well what about print statements?" "No Dave, print statements aren't executed. Nothing is executed." "How about the len() function?" "No Dave, nothing is executed." "What, not even the else clause?" Unless we think that the average beginner to Python is as dumb as Dave Lister from Red Dwarf, I don't think we need worry that they will imagine that for/else blocks behave like try/finally. Somehow we've gone from trying to fix an actual, real-life problem where people assume that the else block executes if the loop sequence is empty, to arguing how best to solve the entirely hypothetical problem that people might imagine that else blocks have the special behaviour of try/finally. -- Steven

On Sat, Jun 9, 2012 at 1:49 PM, Steven D'Aprano <steve@pearwood.info> wrote:
- If the loop is exited by a return, *nothing* following the return is executed. That includes the else block.
- If execution is halted by an exception (including raise), *nothing* following the exception is executed. That includes the else block.
- If execution is halted by an external event that halts or interrupts the Python process, *nothing* following executes. That includes the else block.
- If the loop never completes, *nothing* following the loop executes. That includes the else block.
To continue the analogy with the "Red Dwarf" quote I made earlier:
Please stop mocking your own writing. I wrote nothing like the above. I said that maybe we should be specific and correct with when else is called. I didn't say that we should be exhaustive for when it is not. In fact, the example explanations I gave were not exhaustive. -- Devin

Devin Jeanpierre wrote: [...]
Please stop mocking your own writing. I wrote nothing like the above.
I said that maybe we should be specific and correct with when else is called. I didn't say that we should be exhaustive for when it is not. In fact, the example explanations I gave were not exhaustive.
You explicitly worried that users will conclude that the else block will run "except not when left by break", and stated that the description given earlier implies that for/else behaves like try/finally (i.e. that the else clause is *only* skipped on a break, but not return or raise). There is no evidence that users somehow get the impression that for/else behaves like try/finally, and I find it completely implausible that they will do so in the future. If I'm wrong, the docs can be revised, but until then, in my opinion worrying about this is a documentation case of YAGNI. The current documentation for for/else is already specific and correct. The real-life problem Nick is trying to solve is that many people think that the else clause implies that it behaves like if/else, and Nick is trying to nudge users to think of try/else instead. I think that's a worthy goal. Worrying about users reading the tutorial and concluding that for/else will run when you exit with a return, not so much. -- Steven

On Sat, Jun 9, 2012 at 11:03 PM, Steven D'Aprano <steve@pearwood.info> wrote:
There is no evidence that users somehow get the impression that for/else behaves like try/finally, and I find it completely implausible that they will do so in the future. If I'm wrong, the docs can be revised, but until then, in my opinion worrying about this is a documentation case of YAGNI.
The current documentation for for/else is already specific and correct. The real-life problem Nick is trying to solve is that many people think that the else clause implies that it behaves like if/else, and Nick is trying to nudge users to think of try/else instead. I think that's a worthy goal. Worrying about users reading the tutorial and concluding that for/else will run when you exit with a return, not so much.
You are confused. A) I was arguing in favor of the current documentation, written by Nick Coghlan. You were arguing in favor of Yuval's thing. You appear to have forgotten this, and are now agreeing with me. B) Obviously there is no empirical evidence for anything, because Yuval's thing is unpublished, and the current documentation was added two days ago to the dev branch of the docs. -- Devin

Devin Jeanpierre wrote:
On Sat, Jun 9, 2012 at 11:03 PM, Steven D'Aprano <steve@pearwood.info> wrote:
There is no evidence that users somehow get the impression that for/else behaves like try/finally, and I find it completely implausible that they will do so in the future. If I'm wrong, the docs can be revised, but until then, in my opinion worrying about this is a documentation case of YAGNI.
The current documentation for for/else is already specific and correct. The real-life problem Nick is trying to solve is that many people think that the else clause implies that it behaves like if/else, and Nick is trying to nudge users to think of try/else instead. I think that's a worthy goal. Worrying about users reading the tutorial and concluding that for/else will run when you exit with a return, not so much.
You are confused.
Perhaps I am.
A) I was arguing in favor of the current documentation, written by Nick Coghlan. You were arguing in favor of Yuval's thing. You appear to have forgotten this, and are now agreeing with me.
The context which has been lost is that Terry Reedy objected to Yuval's description of for/else. I replied to Terry's objection, disagreeing, and you replied to me, (apparently) disagreeing with my reply. Do you blame me for thinking you were agreeing with Terry? I think that our positions are probably closer than our disagreements might suggest.
B) Obviously there is no empirical evidence for anything, because Yuval's thing is unpublished, and the current documentation was added two days ago to the dev branch of the docs.
We have anecdotal evidence that many people expect that for/else will execute the else clause when the for loop is empty. We have no anecdotal evidence, or any other evidence, that anyone excepts that the else clause runs if you return out of the loop. -- Steven

On Sun, Jun 10, 2012 at 10:04 PM, Steven D'Aprano <steve@pearwood.info> wrote:
We have anecdotal evidence that many people expect that for/else will execute the else clause when the for loop is empty.
We have no anecdotal evidence, or any other evidence, that anyone excepts that the else clause runs if you return out of the loop.
Right. We also need to remember that this entire discussion started with a complaint regarding an apparent internal inconsistency in the language, because the else clauses on if statements and loops don't mean exactly the same thing. When you read the tutorial, it introduces the first two forms together, but the third form (try/except/else) doesn't show up until a later chapter on exception handling. This was quite possibly one of the factors leading people to make a perfectly reasonable intuitive leap that happens to be wrong. All my docs addition is designed to do is discourage readers from making that incorrect intuitive leap. They will still need to learn how the else clauses interact with other constructs, like exceptions and early returns, but those details aren't relevant to building a fence across the tempting-but-wrong path from "if <iterable>/else" to "for x in <iterable>/else". It's a tricky educational problem to be sure, and if it wasn't for backwards compatibility requirements, there would be a strong temptation to just drop the else clause from loops entirely. The versions that use sentinel values instead aren't *that* complicated, and have the virtue of being explicit. However, that's not going to happen (it would break too much code without a sufficiently compelling justification), so making small tweaks to the relevant tutorial docs (that will hopefully be picked up by Python instructors and other learning and teching resources) is a reasonable way forward. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

I hope this isn't too off-topic, but is the tutorial supposed to exhaustively explain the python language? Because if not, then the for-else/while-else clause may be a good thing to move to an appendix. Yuval

On 10/06/2012 16:50, Yuval Greenfield wrote:
I hope this isn't too off-topic, but is the tutorial supposed to exhaustively explain the python language?
Because if not, then the for-else/while-else clause may be a good thing to move to an appendix.
The for-else/while-else clause is part of the core language, so it should be explained.

On Sun, Jun 10, 2012 at 7:04 PM, MRAB <python@mrabarnett.plus.com> wrote:
On 10/06/2012 16:50, Yuval Greenfield wrote:
I hope this isn't too off-topic, but is the tutorial supposed to exhaustively explain the python language?
Because if not, then the for-else/while-else clause may be a good thing to move to an appendix.
The for-else/while-else clause is part of the core language, so it should be explained.
If we want a dust of a chance to deprecate for-else/while-else in python 6, circa 2031, then we should at least move it to the back of the tutorial. I'm not suggesting to completely delete the text, just to nudge it to the end. This clause is most definitely not a common pattern in python. Personally I've never seen it in the wild and most pythonistas I've spoken with have never heard of the construct. Yuval

On Mon, Jun 11, 2012 at 1:50 AM, Yuval Greenfield <ubershmekel@gmail.com> wrote:
I hope this isn't too off-topic, but is the tutorial supposed to exhaustively explain the python language?
Because if not, then the for-else/while-else clause may be a good thing to move to an appendix.
It's supposed to arm people well enough to cope with at least *reading* most code they're likely to encounter. Since for/else is the idiomatic way to write a search loop, even beginners really should learn how to read it. For more esoteric stuff like metaclasses where the philosophy of "If you're wondering whether or not you need it, you don't need it" applies, then the tutorial can safely skip it. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Sat, Jun 9, 2012 at 10:48 AM, Steven D'Aprano <steve@pearwood.info> wrote:
Loops exit at the top, not the bottom. This is most obvious when you think about a while loop:
while condition: ...
Of course you have to be at the top of the loop for the while to check condition, not the bottom. For-loops are not quite so obvious, but execution has to return back to the top of the loop in order to check whether or not the sequence is exhausted.
Whether or not it is the *simplest* way to think about for/else, talking about exhaustion of the list (iterable) is correct.
If you want to talk about exhaustion of the list then you need to talk differently about the while loop. Documentation is usually written for non-experts. When I taught intro to programming, the mental model that most students had was nowhere near as strong as most people on this list. The concept 'loop exits normally' would be much easier for them to understand. On Sat, Jun 9, 2012 at 10:49 AM, Steven D'Aprano <steve@pearwood.info> wrote:
Ignoring try/finally blocks, which are special, we can assume that the reader has (or will have once they actually learn about functions and exceptions) a correct understanding of the behaviour of return and raise.
- If the loop is exited by a return, *nothing* following the return is executed. That includes the else block.
- If execution is halted by an exception (including raise), *nothing* following the exception is executed. That includes the else block.
- If execution is halted by an external event that halts or interrupts the Python process, *nothing* following executes. That includes the else block.
- If the loop never completes, *nothing* following the loop executes. That includes the else block.
You've written four different ways of saying 'loop does not exit normally' vs. saying once 'loop exits normally'. When you emphasize *nothing* above, it strongly suggests they all mean the same thing. If you *don't* ignore try/finally, then they don't. I don't think documentation needs to cover every case, but if you're going to write stuff in bold letters (or italic or whatever), then readers expect you're covering all the bases and not ignoring special cases. That may not be your intent but that's the way people read things. Again, docs are written for non-experts. Holly: He's dead, Dave. Everybody is dead. Everybody is dead, Dave. Lister: Wait. Are you trying to tell me everybody's dead? Holly: Yup. Well, except for Dracula who was executing a try/finally. He's undead and probably going to kill you too. But I didn't want to bother you with that minor detail. :-) --- Bruce Follow me: http://www.twitter.com/Vroo http://www.vroospeak.com

Hi, (sent from my phone) On Jun 8, 2012 11:35 PM, "Yuval Greenfield" <ubershmekel@gmail.com> wrote:
On Fri, Jun 8, 2012 at 12:04 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
(context for python-ideas: my recently checked in changes to the tutorial, that added the final paragraph to
http://docs.python.org/tutorial/controlflow.html#break-and-continue-statemen... )
If we're on that subject then I think this
Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement.
Doesn't hit the "break" nail on the head fast and hard enough in my opinion. I'd replace it with something like:
Loop statements may have an else clause; it is executed immediately after the loop but is skipped if the loop was terminated by a break statement.
Yes. This is why I've been suggesting for a while that we call these constructs for/break/else and while/break/else. As Terry says, this is not the whole truth but you'd have to have a warped mind not to extrapolate the correct behaviour when there is a return or raise in the loop body. Arnaud

On Fri, Jun 8, 2012 at 3:34 PM, Yuval Greenfield <ubershmekel@gmail.com> wrote:
Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement.
I don't think talking about exhaustion of the list is the simplest way to think about this. Isn't it the distinction whether the loop exits at the bottom or in the middle? On Sat, Jun 9, 2012 at 12:46 AM, Arnaud Delobelle <arnodel@gmail.com> wrote:
As Terry says, this is not the whole truth but you'd have to have a warped mind not to extrapolate the correct behaviour when there is a return or raise in the loop body.
If we can express this in a way that is the whole truth that's better. And leaving out a very common scenario like return in a loop and an less common one like raise. Asking readers of technical documentation to extrapolate frequently leads to incorrect assumptions. Go read the docs on msdn if you don't agree with that. Here's my take: Loop statements may have an else clause which is executed when the loop exits normally (control flows off the bottom of the loop). If the loop exits from the middle (through break, return, raise or something else), then the else is not executed. It may help to think of the else as being paired with an "if ... break" in the middle of the loop. If the break is not executed then the else will be. Likewise I would reword the comparison to try. In particular I would remove the negative reference to if as I think that's misleading. The else clause of a loop can also be thought of as similar to the else clause of a try statement. A try statement’s else clause runs when no exception, break or return occurs and the try exits normally, and a loop’s else clause runs when no break or return occurs and the loop exits normally. For more on the try statement and exceptions, see Handling Exceptions. Note that this corrects the error in the current docs which says "a try statement’s else clause runs when no exception occurs" which is not true if you exit the try via break or return. --- Bruce Follow me: http://www.twitter.com/Vroo http://www.vroospeak.com

Bruce Leban wrote:
On Fri, Jun 8, 2012 at 3:34 PM, Yuval Greenfield <ubershmekel@gmail.com> wrote:
Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement.
I don't think talking about exhaustion of the list is the simplest way to think about this. Isn't it the distinction whether the loop exits at the bottom or in the middle?
[Aside: I believe that isn't Yuval's description above. As I understand it, he is quoting the current docs.] Loops exit at the top, not the bottom. This is most obvious when you think about a while loop: while condition: ... Of course you have to be at the top of the loop for the while to check condition, not the bottom. For-loops are not quite so obvious, but execution has to return back to the top of the loop in order to check whether or not the sequence is exhausted. Whether or not it is the *simplest* way to think about for/else, talking about exhaustion of the list (iterable) is correct.
On Sat, Jun 9, 2012 at 12:46 AM, Arnaud Delobelle <arnodel@gmail.com> wrote:
As Terry says, this is not the whole truth but you'd have to have a warped mind not to extrapolate the correct behaviour when there is a return or raise in the loop body.
If we can express this in a way that is the whole truth that's better. And leaving out a very common scenario like return in a loop and an less common one like raise. Asking readers of technical documentation to extrapolate frequently leads to incorrect assumptions. Go read the docs on msdn if you don't agree with that.
Should we ask readers to extrapolate what happens when the for loop variable is a keyword (e.g. "for None in sequence"), or explicitly mention what happens? Should we ask readers to extrapolate what happens when the for loop sequence doesn't actually exist, or explicitly tell them that they get a NameError and the loop doesn't run? Should we do this for every single function? Frankly, you cannot avoid asking readers to extrapolate, because there is an infinite number of things that they could do, and you cannot possibly document them all. -- Steven

Nick Coghlan dixit (2012-06-08, 19:04):
for x in iterable: ... except break: # Implicit in the semantics of loops pass else: ...
Would it be worth adding the "except break:" clause to the language just to make it crystal clear what is actually going on? I don't think so, but it's still a handy way to explain the semantics while gently steering people away from linking for/else and if/else too closely.
IMHO a better option would be a separate keyword, e.g. 'broken': for x in iterable: ... broken: ... else: ... And not only to make the 'else' more understandable. I found, in a few situations, that such a 'broken' clause would be really useful, making my code easier to read and maintain. There were some relatively complex, parsing-related, code structures... stopped = False for x in iterable: ... if condition1: stopped = True break ... if contition2: stopped = True break ... if contition3: stopped = True break ... if stopped: do_foo() else: do_bar() It would have been nice to be able to do: for x in iterable: ... if condition1: break ... if contition2: break ... if contition3: break ... broken: do_foo() else: do_bar() Cheers. *j

On 09/06/2012 17:01, Jan Kaliszewski wrote:
Nick Coghlan dixit (2012-06-08, 19:04):
for x in iterable: ... except break: # Implicit in the semantics of loops pass else: ...
Would it be worth adding the "except break:" clause to the language just to make it crystal clear what is actually going on? I don't think so, but it's still a handy way to explain the semantics while gently steering people away from linking for/else and if/else too closely.
IMHO a better option would be a separate keyword, e.g. 'broken':
for x in iterable: ... broken: ... else: ...
And not only to make the 'else' more understandable. I found, in a few situations, that such a 'broken' clause would be really useful, making my code easier to read and maintain. There were some relatively complex, parsing-related, code structures...
stopped = False for x in iterable: ... if condition1: stopped = True break ... if contition2: stopped = True break ... if contition3: stopped = True break ... if stopped: do_foo() else: do_bar()
[snip] That can be re-written as: stopped = True for x in iterable: ... if condition1: break ... if condition2: break ... if condition3: break ... else: stopped = False if stopped: do_foo() else: do_bar()
participants (15)
-
Antoine Pitrou
-
Arnaud Delobelle
-
Bruce Leban
-
Devin Jeanpierre
-
Giampaolo Rodolà
-
Jan Kaliszewski
-
Mike Meyer
-
MRAB
-
Ned Batchelder
-
Nick Coghlan
-
Rob Cliffe
-
Stephen J. Turnbull
-
Steven D'Aprano
-
Terry Reedy
-
Yuval Greenfield