Updating PEP 315: do-while loops

Am working on PEP 315 again, simplifying the proposal by focusing on a more standard do-while loop. The two motivating cases are: 1) A pattern of repeated code blocks before and inside a traditional while-loop. Here's an example from random.sample(): j = _int(random() * n) while j in selected: j = _int(random() * n) 2) A pattern of ending a "while True:" loop with an "if not <cond>: break". Here's an example from random.normalvariate(): while 1: u1 = random() u2 = 1.0 - random() z = NV_MAGICCONST*(u1-0.5)/u2 zz = z*z/4.0 if zz <= -_log(u2): break The challenge has been finding a syntax that fits well with the patterns in the rest of the language. It seems that every approach has it's own strengths and weaknesses. 1) One approach uses the standard do-while name but keeps usual python style formatting by putting the condition at the top instead of the bottom where it is in C and Java: do ... while j in selected: j = _int(random() * n) do ... while zz > -_log(u2): u1 = random() u2 = 1.0 - random() z = NV_MAGICCONST*(u1-0.5)/u2 zz = z*z/4.0 This syntax uses the ellipsis for an additional mental cue reminding the reader that the enclosed code block is executed before the condition is evaluated. It is not unlike the with-statement which has both enter and exit behaviors signaled by a single leading keyword and an indented block. Also, code-at-the-top approach fits well with the way the compiler would generate code with the condition test being preceding the loop body: 0 SETUP_LOOP 30 (to 33) 3 JUMP_ABSOLUTE 18 6 LOAD_NAME 0 (x) 9 LOAD_NAME 1 (y) 12 COMPARE_OP 0 (<) 15 POP_JUMP_IF_FALSE 33 18 <loop body> ... 30 JUMP_ABSOLUTE 6 33 LOAD_CONST 1 (None) 36 RETURN_VALUE I like the ellipsis form because of the mental cue it provides, but the proposal still works with any other spelling variant: do_while <cond>: do while <cond>: do_body_first_and_then_loop_if_the_test_condition_is_true <cond>: 2) Another approach is to put the test at the end. Something like: do: j = _int(random() * n) :while j in selected do: j = _int(random() * n) while j in selected These seem syntactically weird to me and feel more like typos than real python code. I'm sure there are many ways to spell the last line, but in the two years since I first worked on the PEP, I haven't found any condition-at-the-end syntax that FeelsRight(tm). Probably everyone has an opinion on these, but I don't any think those could be called bike-shedding. The spelling does matter and hopefully the cleanest solution can be found. It would be really great if we were to get some decent spelling of do-while in the language. Raymond P.S. I've punted on the more complex PEP-315 variants with repeated setup code: <setup code> while <condition>: <loop body> <setup code> It was hard enough to get a decent spelling for do-while. Also, I've punted on the while-cond-with-assignment case that is being handled by other proposals such as: while f.read(20) as data != '': ...

Raymond Hettinger wrote:
Am working on PEP 315 again, simplifying the proposal by focusing on a more standard do-while loop.
I posted this to python-dev before I saw your post over here. Sorry about that. My message was: You might want to note in the PEP that the problem that's being solved is known as the "loop and a half" problem, at least for your motivating case #2. http://www.cs.duke.edu/~ola/patterns/plopd/loops.html#loop-and-a-half

[Eric Smith]
Thanks. When I update the PEP, will add that link. Wanted to get people's reaction here first. Do the condition-at-the-top format work for everyone or can someone think-up a condition-at-the-bottom approach the doesn't suck with respect to existing Python syntax. Raymond

On Sat, Apr 25, 2009 at 7:39 PM, Raymond Hettinger <python@rcn.com> wrote:
Condition at the bottom sucks for similar reasons as why ... def foo(...): (many lines of code) foo = classmethod(foo) Having your condition, modification, etc., at the end is just plain annoying. I like my conditions up top. I agree with you that the 3-suite do/while/else option is just plain ugly. I also agree that the "do while <condition>:" variants are currently the prettiest, but it still doesn't feel quite right to me. When confronted with these things, I usually go for the "while True" or "while first or <condition>" versions, and throw a comment just before the condition in the "while True" variant so my eye is drawn there. - Josiah

On Sun, Apr 26, 2009 at 1:20 AM, Steven D'Aprano <steve@pearwood.info> wrote:
"Weird" is an understatement; I'd rather say outright misleading. There's nothing in "do ... while cond" that even hints that the condition is not checked the first time. George

On Sun, 26 Apr 2009 04:22:31 pm George Sakkis wrote:
Well, not quite. The ellipsis is a hint that there's something unusual going on. I agree that the meaning of the ellipsis is not intuitive, but that's not important. There's nothing intuitive about loops in the first place :) I could live with ellipsis, although it's not my first preference. I'd call it a syntactic wart, like writing a tuple of one item as (object,). -- Steven D'Aprano

Le Sat, 25 Apr 2009 22:06:14 -0700, Josiah Carlson <josiah.carlson@gmail.com> s'exprima ainsi:
You're wrong:
is a bad syntax only because nothing tells you that, to understand foo's meaning properly, you need first to take something into account that is written after its def. Where as in the following do: <suite> until <condition> (or any footer variant) the header "do:" warns you about the loop's logic: loop at least once, then check for an exit condition -- logically written at the loop's exit point ;-) If the real issue you have in mind is that the loop's body may not fit inside the editor window, well... Denis ------ la vita e estrany

spir writes:
Not for me. The real issue is that people think top-down, which has a double meaning. First, we write sequences of statements from top-to-bottom on the page, and read them the same way. Second, we construct applications by starting with a high-level abstraction, and gradually add complexity by implementing lower and lower level functionality.[1] Note that C, and many other languages, implement some kind of include functionality in large part to allow you to declare many identifiers *early* but without interfering with the correspondence of top-down *reading habits* to top-down *design*. So as I'm reading down the page, it's natural for the structured concept (high level decision of what to do) to be above the detailed implementation (lower level decisions about how to do it). Having the complete condition available at the top of a structure is something I've always wanted from do ... while or repeat ... until (but didn't know that until Raymond pointed it out<wink>), because it's not enough to know that this is a structure. Rather, having the condition available tells me (in many cases) whether this is a normally-once, occasionally many, "retry" kind of function, or a typically multi-iteration task that just happens to need to always be done at least once. That often affects my perception of the code enough to demand rereading if the condition is of the "wrong" kind based on my prior expectation. YMMV of course. Footnotes: [1] Yes, I'm aware that time-and-motion studies of programmers show that few programmers actually write programs that way. In fact most programmers, including excellent ones, switch back and forth between top-down, bottom-up, and other modes (inside-out?). What's important is that in many cases it makes it much easier to read and understand somebody else's program.

Le Mon, 27 Apr 2009 01:36:29 +0900, "Stephen J. Turnbull" <stephen@xemacs.org> s'exprima ainsi: I do not understand the resistance against the "footer" options, there must be something I cannot figure out. So my favored pattern in case a header option would win the game is: until condition: <suite> I think * 'until' expresses far better an exit-condition than 'while' (even if this advantage is largely broken by the fact it is placed on top) * in this case(*) we don't need any weird nicety like '...' (I say 'weird' because the sign itself has nothing to do with the meaning we want it to carry.) (*) First because 'until' is new in python. Second because it carries a sense of 'end'.
Sure. While it certainly depends on individual (largely unconscious) strategies of programming and parsing. Anyway. How important is this really in practice? * Consider that to properly understand if..(else), if...elif...elif, try...except, for...(else) blocks, etc... we need to do the same kind of mental job. Who complains about it? * Worse: Many of these blocks have _optional_ clauses. Even worse: you're not even warned about it by the header. This does not trouble me so much... * Usually the loop fits in a page, so that your eyes can jump forward. (Otherwise there are other means, including folding and refactoring ;-) This is not joking. I do not find do: ... ... ... until cond more difficult to parse than for instance if cond: ... ... ... elif cond: ... ... ... elif cond: ... ... ... Anyway. Denis ------ la vita e estrany

spir writes:
I have no objection to "footer options", unless they involve new syntax. We already have while True: # suite if condition: continue and none of the proposals for footers improve readability over that, IMO. The argument that a do-while with the terminal condition at the head is a new structure and has some possible advantages in readability is correct; that's all I had to say. Let me now add "but it's weak, IMO." So I'm with Larry; we don't need this PEP. There may be some way to do it, but the current suggestions aren't attractive enough. Your suggestion of "until <condition>:" may be the best of the lot, but it's ambiguous for the same reasons "do ... while <condition>:" is. Many people will read that as "while not <condition>:".

[off list -- because it's a side-issue] Le Mon, 27 Apr 2009 13:58:39 +0900, "Stephen J. Turnbull" <stephen@xemacs.org> s'exprima ainsi: Thanks for your comments.
Your suggestion of "until <condition>:" may be the best of the lot,
Certainly not the best for all readers, but the simplest as of now ;-)
but it's ambiguous for the same reasons "do ... while <condition>:" is.
The source of ambiguity and even mislead is that it is placed on top while the condition is evaluated at end of loop. Anyway, it's an exit condition so it fits better down there IMO where ever it may be evaluated. The reason why I favor 'footer' options.
Many people will read that as "while not <condition>:".
My opinion about that is the following: * A new keyword is enough to force the reader to consider the code with attention and expect something different. * Obviously 'until' cannot have the same meaning as 'while' -- rather the opposite in fact. Don't you think so? (english is not my mother tongue) * The issues with '...' is that (1) it obfuscates the code (2) there is no link between the sense of ellipsis itself and the proposed semantics (*) Denis (*) It would be better used in ranges (python ':') or as line-continuation code (python '\'). ------ la vita e estrany

On Mon, Apr 27, 2009 at 3:19 AM, spir <denis.spir@free.fr> wrote:
As someone whose native language is English, I can say that Stephen's reading would indeed intuitively be considered the obvious logical opposite of a "while" loop; also, his interpretation matches the behavior of "until" loops in both Perl and Ruby. Cheers, Chris -- http://blog.rebertia.com

spir <denis.spir@free.fr> writes:
[off list -- because it's a side-issue]
Actually, you replied on-list, so I'll feel free to do the same :-)
You can't have it both ways, though: The keyword ‘until’ is already strongly associated with certain behaviour in other programming languages, which is presumably a strong reason to choose it for this instance. But it also has a strong English-language relationship with ‘while’.
That's exactly what Stephen's saying: “until <condition>” and “while not <condition>” are fungible in English.
Denis
Denis, could you please change your From field so that it shows the name “Denis Spir” which you're clearly happy to be known as? -- \ “It is the responsibility of intellectuals to tell the truth | `\ and to expose lies.” —Noam Chomsky, 1967-02-23 | _o__) | Ben Finney

Raymond Hettinger wrote:
I don't like the condition-at-the-top variants, with or without an ellipsis. (At first glance anyway.)
I couldn't quite parse that, but I sure hope it meant "I'm up for yet another round of discussion". I propose we allow an "if" suffix to "break" and "continue". So I'd spell your loop this way: do: j = _int(random() * n) continue if j in selected This would require there be no implied "while True" for a "do" block. If you hit the end of the "do" block, without following a continue, you'd just leave the block. So this program would be legal: do: print("wtf?") print("well, that was pointless") It would print two strings then exit. It would *not* loop. A little unexpected I guess. On the other hand this form gives a pleasing alternate spelling to "while True": do: continue It also provides a dandy spelling for early-exit programming, without nesting your ifs, or early return, or throwing an exception, or using gotos which thankfully we don't have: def user_typed_1_then_2(): do: print("enter 1, then 2") i = input() break if i != '1' i = input() break if i != '2' return True print("why do you mistreat me so? it was a simple request!") return False Maybe it's just me, but I think that spelling and those semantics are charmingly Pythonic. HTH, /larry/

I like the structure of the exising PEP 315 proposal, but not the selection of keywords. It seems confused -- what exactly are you repeating? The part after the "do", the part after the "while", or both? My suggestion is while: <statements> gives <condition>: <more statements> There could also be a degenerate form without the second suite: while: <statements> gives <condition> -- Greg

Raymond Hettinger wrote:
Though I would like a fair hearing for the "do ... while <cond>:" proposal. To my eyes, it has clear meaning
I think that's only because you already know what it's supposed to mean. To me, it's very far from clear that the "..." means "the expression after this really goes way down there after the suite". Also, anything which only addresses test-at-bottom and not test-half-way-through only covers a very small proportion of use cases, IMO. -- Greg

Raymond Hettinger <python@...> writes:
Yes. Absolutely.
Though I would like a fair hearing for the "do ... while <cond>:" proposal.
I find it disturbing. First, the condition appears on top but is only evaluated at the end of the loop. Second, it's not usual for synctatical tokens to be composed of several words ("do ... while" versus "while", "if", "for", etc.). Third, it introduces a semi-keyword ("do") which won't be used anywhere else, and abuses an existing object ("...") as synctatical glue. I'm in favor of not trying to solve this particular "problem". Keeping the grammar simple is valuable in itself (look at Javascript, C++...). Regards Antoine.

On Sat, Apr 25, 2009 at 7:35 PM, Larry Hastings <larry@hastings.org> wrote: <snip>
I don't see why the if-as-suffix is needed when we already have a one-liner for such situations (e.g.): if i != '1': break It's much more uniform and only one character longer. Cheers, Chris -- I have a blog: http://blog.rebertia.com

Chris Rebert wrote:
I certainly see your point. Let me take it a step further: the "do: ... while <condition>" construct isn't needed, given that it's already expressible with "while True: ... if not <condition>: break". It's true, "break if <condition>" and "continue if <condition>" are redundant constructs. But this debate is over refining our syntactic sugar--charting what is arguably a redundant construct. Therefore proposing redundant constructs for the sake of clarity is on the table. I think "break if <condition>" and "continue if <condition>" enhance readability; they make the control flow pop out at you more than "if <condition>: break" and "if <condition>: continue" do. "break if" and "continue if" have the advantage of following established Python syntactic precedent. /larry/

On Sat, Apr 25, 2009 at 8:39 PM, Larry Hastings <larry@hastings.org> wrote:
Except the ternary operator only allows for expressions, not statements; IIRC, GvR was somewhat reluctant about having any sort of ternary operator, so this further extension seems unlikely to me. Allowing general if-suffixes on things also seems a bit Perlish, imho. I find the current state of syntactic simplicity quite appealing. Regarding visibility, how is the last word of the last line in a loop body, further set off by the if's colon, not visible enough? I personally haven't ever had problems spotting the breaks in loop-and-a-half code. On another note, I actually do like the bare-while-as-while-True idea quite a bit. +1 on that. Cheers, Chris -- http://blog.rebertia.com

Chris Rebert wrote:
The suffix form of "if" can also be found in generator expressions and list/set/dict comprehensions. What all these constructs have in common is that they're expressions, which means they can be buried in the middle of other expressions. And being able to bury an "if" in there too is a boon to the programmer--one that the "if" *statement* could not grant. But this is a subtle point, easily lost on non-language-nerds. I work with an excellent programmer who nevertheless didn't grasp the distinction, even as I attempted to explain it to him. So, for most Python programmers, the language feels like "an if statement looks like this, and btw you can have ifs in the middles and ends of lines too sometimes". I hardly think the suffix form of "if" to "break" and "continue" would be the conceptual straw that broke the back of the language's cognitive load. As for GvR, I gave up trying to predict his reactions a long time ago. Though I admit I'd be very surprised if my proposal, or indeed any proposal, won favor; PEP 315 is long enough in the tooth I fear we are doomed to never find a majority. (A majority being defined here as either 2/3 majority of python-dev voters, or indeed the lone BDFL.) Witness PEP 3103, the switch statement; I think we'd all like to have one, if we could only figure out how to spell it. And I personally would value a switch statement more than do/while, /larry/

On Sat, Apr 25, 2009 at 8:39 PM, Larry Hastings <larry@hastings.org> wrote:
FYI, this was proposed a few months ago (see the first post here: http://mail.python.org/pipermail/python-ideas/2008-September/002083.html ). I was and still am -1 on any variant of "continue if <condition>" or "break if <condition>" for the same reasons I was then. They are a slippery slope (why not "(raise Exception) if <condition>"?), and they hide the control flow to the left of the line where we are used to seeing it on the right side of the line. - Josiah

Larry Hastings wrote:
Hmm, so why add another keyword like "do", instead of just giving "while" a default condition "True", so that you could write while: state = do_stuff_here() if predicate(state): break I think that the missing condition at the top makes it pretty clear that the end condition must be inside the loop. Stefan

Stefan Behnel <stefan_ml@behnel.de> writes:
Or that there isn't an end condition at all. I don't see how this indicator is any less clear than the current spelling:: while True: state = do_stuff_here() if predicate(state): break -- \ “I would rather be exposed to the inconveniences attending too | `\ much liberty than those attending too small a degree of it.” | _o__) —Thomas Jefferson | Ben Finney

I think a legitimate issue here is that break and continue can be hard to see sometimes when they're nested several layers deep. That makes while True: ... if x: break less clear than it could be. So let me throw out two straw proposals: (1) Allow break and continue to be spelled BREAK and CONTINUE which makes them stand out a bit more. while True: floob yuzz if wum: BREAK thnad (2) Allow a line containing break or continue to start with a -> token outside the normal indentation. while True: floob yuzz if wum: -> break thnad I don't expect either of these proposals to attract support. I present them to put a small focus on the issue of making break/continue more visible. --- Bruce

Raymond Hettinger wrote:
I think having the "while" start some blocks but end others is very weird indeed and makes it harder to keep stuff in my head. I'd prefer a bare "while:" with a more visible "break if" and possibly "continue if" expressions. Visualize the while, continue, and break with syntax highlighting. while: j = _int(random() 8 n) break if j not in selected Or if the reverse comparison is preferred you could do... while: j = _int(random() * n) continue if j in selected break Or we could allow break and continue in the if-else expression syntax ... while: ... continue if (j in selected) else break or.. while: ... break if (j not in selected) else continue In this last case the final 'else continue' can be dropped off sense it would be redundant: while: ... break if (j not in selected) That would give us back the first example again. By moving the break and continue out from under the if statement it makes it more visible and follows a common pattern of having lines start with keywords, which is very readable with syntax highlighting. It also still allows the break to be anywhere in the body which is more flexible. Because it is vary close to the current while syntax, is there really any need to rename the bare 'while' to 'do'? I just think of it as 'do for a while' or short for 'while True'. Cheers, Ron

Ron Adam wrote:
I'd prefer a bare "while:" with a more visible "break if" and possibly "continue if" expressions.
I do like "break if" and "continue if", unsurprisingly as I suggested them in parallel. I'm not sure about the "while:"; your "while: ... continue if ; break" strikes down some of the clarity we're trying to achieve here. /larry/

Larry Hastings wrote:
The bare "while:" can be a completely separate issue. And... while True: ... continue if condition break I'm not sure why you think that is less or not clear. It's just an possible to do if you add 'continue if'. <shrug> The equivalent would be: while True: ... break if (not condition) But this reverses the test and that could express what you are doing in a less clear way. Being able to do it both ways is a good thing I think. If you allow the if-else syntax to be used with flow control keywords then you have 6 possibilities. break if condition else pass break if condition else continue continue if condition else pass continue if condition else break pass if condition else break pass if condition else continue It makes sense to shorten some of these.. break if condition # 'else pass' doesn't do anything continue if condition # "" I'm not sure the others are needed, they may allow some problems to be expressed more explicitly. The nice thing about extending python this way is these expressions may also work in for loops and possibly other places. Because python is based as much on practical need rather than just what is possible to do. We may only need the two last shorter versions above. But I won't complain if all six of the longer versions above also work. ;-) In anycase Raymond was asking for ideas that are consistent with pythons syntax and I think these suggestions are. The one inconsistant thing is if-else expressions normally return a value, and in these cases, they either should raise a syntax error or return None if they are used on the right hand side of an expression. Ron

Ron Adam wrote:
But putting "if" after "break" doesn't make the exit point any more visible to my eyes. If anything, it makes it *less* visible. I think there's another thing at work here, too. Having an outdented clause for the exit point makes it very obvious that it is part of the syntax of the loop structure, and consequently that there is only *one* exit point from the loop. This is in contrast to break statements, which are much less disciplined and can occur more than once and inside nested statements. -- Greg

Le Sat, 25 Apr 2009 21:50:52 -0500, Ron Adam <rrr@ronadam.com> s'exprima ainsi:
But (1) This does not give any advantage compared to existing syntax if condition: break | continue (2) It does not solve the problem which, I guess, is to express the loop's logic obviously -- meaning in the header (or footer) instead of hidden somewhere in the body -- and nicely avoid numerous "while 1:" and "break" as a side-effect. What Raymond's proposals provide. What is not obvious at all is that: do ... while j in selected: j = _int(random() * n) means the assignment will be done at least once (and no NameError thrown). Where/how is it said? The above code is supposed to mean something like: do once then while j in selected: j = _int(random() * n) But maybe it's only me. Footer solutions (condition at end of loop) seem not to have Raymond's favor; still they are obvious in comparison: do: j = _int(random() * n) <your favored end condition syntax> j in selected Denis ------ la vita e estrany

On Sun, 26 Apr 2009 11:00:26 am Raymond Hettinger wrote:
A quick-and-dirty data point for you. Using Google code search, I found approx 80,000 hits for "lang:Pascal until" versus 129,000 hits for "lang:Pascal while". So the number of cases of test-at-top versus test-at-bottom loops are very roughly 3:2 in Pascal code indexed by Google. (Disclaimer: both searches revealed a number of false positives. I've made no attempt to filter them out.) For those who aren't familiar with Pascal, the test-at-top and test-at-bottom loops are spelled: WHILE condition DO BEGIN suite; END; and REPEAT suite; UNTIL condition; WHILE exits the loop when condition is False, and REPEAT...UNTIL exits when condition is True. So I think the first question we need to deal with is, should we be discussing do...while or do...until? Is there a consensus on the sense of the condition test? while True: # do...while, exit when condition is false suite if not condition: break or while True: # do...until, exit when condition is true suite if condition: break Or do we doom this proposal by suggesting variant spellings that cover both cases?
I'm not entirely comfortable with writing the test at the top of the loop, but having it executed at the bottom of the loop. But I'd prefer it to no do-loop at all. Oh, I should ask... will whitespace around the ellipsis be optional?
I don't like either of those variants. I agree, they feel strange. The only variant that I find natural is the Pascal-like: do: suite until condition where the "until" is outdented to be level with the do. My vote goes for this, although I'm probably biased by my familiarity with Pascal. How does that feel to you? I don't think I could live with do: suite while condition even though it isn't strictly ambiguous, nor does it prevent nesting a while-loop inside the do-loop: do: suite while flag: another_suite while condition But it looks disturbingly like an invalid line. If outdenting is impossible, then I could live with it being indented: do: suite until condition # or while if you prefer but what happens if there is indented code following the until/while? Syntax error, or does it just never get executed? My preferences, in order of most-liked to least-liked, with my score: (1) +1 do: suite until condition # exit loop when condition is true (2) +0.5 do...until condition: # exit loop when condition is true suite (3) +0.5 do...while condition: # exit loop when condition is false suite (4) +0 do: suite until/while condition mystery_suite # what happens here? (5) -0 The status quo, no do-loop at all. (6) -0.5 do: suite :while/until condition (7) -1 do: suite while condition -- Steven D'Aprano

Le Sun, 26 Apr 2009 16:42:36 +1000, Steven D'Aprano <steve@pearwood.info> s'exprima ainsi:
Same for me.
I could survive it because it is linked to the "do:" header. But the real drawback for me is that it should rather express the exit-condition, because it is placed at the end. Anyway, I would support the "while" version if "until" gets to much opposition (for introducing 2 new keywords instead of only 'do'). Both are much better imo than the header versions. Denis ------ la vita e estrany

On Sat, Apr 25, 2009 at 7:00 PM, Raymond Hettinger <python@rcn.com> wrote:
We can actually already do this, via a little used option of iter(): for data in iter(lambda: f.read(20), ''): .... The primary limitation is the need to use lambda (or functools.partial, but it's no better). The next limitation is that even with lambda you can only use an expression, not a statement. You can bite the bullet and define a function first, but that ends up being better accomplished with a generator, and in these cases it's often better still to use a while True: loop. Back to square one. I'm not sure all the reorderings are actually more *readable*, rather than just more appealing to write. -- Adam Olsen, aka Rhamphoryncus

Mathias Panzenböck <grosser.meister.morti@gmx.net> writes:
Already addressed. We already have a way of spelling that, which is ‘while True:’. There needs to be some significant gain to introduce a new spelling for the same thing. -- \ “Good morning, Pooh Bear”, said Eeyore gloomily. “If it is a | `\ good morning”, he said. “Which I doubt”, said he. —A. A. Milne, | _o__) _Winnie-the-Pooh_ | Ben Finney

Raymond Hettinger wrote:
Am working on PEP 315 again, simplifying the proposal by focusing on a more standard do-while loop.
I posted this to python-dev before I saw your post over here. Sorry about that. My message was: You might want to note in the PEP that the problem that's being solved is known as the "loop and a half" problem, at least for your motivating case #2. http://www.cs.duke.edu/~ola/patterns/plopd/loops.html#loop-and-a-half

[Eric Smith]
Thanks. When I update the PEP, will add that link. Wanted to get people's reaction here first. Do the condition-at-the-top format work for everyone or can someone think-up a condition-at-the-bottom approach the doesn't suck with respect to existing Python syntax. Raymond

On Sat, Apr 25, 2009 at 7:39 PM, Raymond Hettinger <python@rcn.com> wrote:
Condition at the bottom sucks for similar reasons as why ... def foo(...): (many lines of code) foo = classmethod(foo) Having your condition, modification, etc., at the end is just plain annoying. I like my conditions up top. I agree with you that the 3-suite do/while/else option is just plain ugly. I also agree that the "do while <condition>:" variants are currently the prettiest, but it still doesn't feel quite right to me. When confronted with these things, I usually go for the "while True" or "while first or <condition>" versions, and throw a comment just before the condition in the "while True" variant so my eye is drawn there. - Josiah

On Sun, Apr 26, 2009 at 1:20 AM, Steven D'Aprano <steve@pearwood.info> wrote:
"Weird" is an understatement; I'd rather say outright misleading. There's nothing in "do ... while cond" that even hints that the condition is not checked the first time. George

On Sun, 26 Apr 2009 04:22:31 pm George Sakkis wrote:
Well, not quite. The ellipsis is a hint that there's something unusual going on. I agree that the meaning of the ellipsis is not intuitive, but that's not important. There's nothing intuitive about loops in the first place :) I could live with ellipsis, although it's not my first preference. I'd call it a syntactic wart, like writing a tuple of one item as (object,). -- Steven D'Aprano

Le Sat, 25 Apr 2009 22:06:14 -0700, Josiah Carlson <josiah.carlson@gmail.com> s'exprima ainsi:
You're wrong:
is a bad syntax only because nothing tells you that, to understand foo's meaning properly, you need first to take something into account that is written after its def. Where as in the following do: <suite> until <condition> (or any footer variant) the header "do:" warns you about the loop's logic: loop at least once, then check for an exit condition -- logically written at the loop's exit point ;-) If the real issue you have in mind is that the loop's body may not fit inside the editor window, well... Denis ------ la vita e estrany

spir writes:
Not for me. The real issue is that people think top-down, which has a double meaning. First, we write sequences of statements from top-to-bottom on the page, and read them the same way. Second, we construct applications by starting with a high-level abstraction, and gradually add complexity by implementing lower and lower level functionality.[1] Note that C, and many other languages, implement some kind of include functionality in large part to allow you to declare many identifiers *early* but without interfering with the correspondence of top-down *reading habits* to top-down *design*. So as I'm reading down the page, it's natural for the structured concept (high level decision of what to do) to be above the detailed implementation (lower level decisions about how to do it). Having the complete condition available at the top of a structure is something I've always wanted from do ... while or repeat ... until (but didn't know that until Raymond pointed it out<wink>), because it's not enough to know that this is a structure. Rather, having the condition available tells me (in many cases) whether this is a normally-once, occasionally many, "retry" kind of function, or a typically multi-iteration task that just happens to need to always be done at least once. That often affects my perception of the code enough to demand rereading if the condition is of the "wrong" kind based on my prior expectation. YMMV of course. Footnotes: [1] Yes, I'm aware that time-and-motion studies of programmers show that few programmers actually write programs that way. In fact most programmers, including excellent ones, switch back and forth between top-down, bottom-up, and other modes (inside-out?). What's important is that in many cases it makes it much easier to read and understand somebody else's program.

Le Mon, 27 Apr 2009 01:36:29 +0900, "Stephen J. Turnbull" <stephen@xemacs.org> s'exprima ainsi: I do not understand the resistance against the "footer" options, there must be something I cannot figure out. So my favored pattern in case a header option would win the game is: until condition: <suite> I think * 'until' expresses far better an exit-condition than 'while' (even if this advantage is largely broken by the fact it is placed on top) * in this case(*) we don't need any weird nicety like '...' (I say 'weird' because the sign itself has nothing to do with the meaning we want it to carry.) (*) First because 'until' is new in python. Second because it carries a sense of 'end'.
Sure. While it certainly depends on individual (largely unconscious) strategies of programming and parsing. Anyway. How important is this really in practice? * Consider that to properly understand if..(else), if...elif...elif, try...except, for...(else) blocks, etc... we need to do the same kind of mental job. Who complains about it? * Worse: Many of these blocks have _optional_ clauses. Even worse: you're not even warned about it by the header. This does not trouble me so much... * Usually the loop fits in a page, so that your eyes can jump forward. (Otherwise there are other means, including folding and refactoring ;-) This is not joking. I do not find do: ... ... ... until cond more difficult to parse than for instance if cond: ... ... ... elif cond: ... ... ... elif cond: ... ... ... Anyway. Denis ------ la vita e estrany

spir writes:
I have no objection to "footer options", unless they involve new syntax. We already have while True: # suite if condition: continue and none of the proposals for footers improve readability over that, IMO. The argument that a do-while with the terminal condition at the head is a new structure and has some possible advantages in readability is correct; that's all I had to say. Let me now add "but it's weak, IMO." So I'm with Larry; we don't need this PEP. There may be some way to do it, but the current suggestions aren't attractive enough. Your suggestion of "until <condition>:" may be the best of the lot, but it's ambiguous for the same reasons "do ... while <condition>:" is. Many people will read that as "while not <condition>:".

[off list -- because it's a side-issue] Le Mon, 27 Apr 2009 13:58:39 +0900, "Stephen J. Turnbull" <stephen@xemacs.org> s'exprima ainsi: Thanks for your comments.
Your suggestion of "until <condition>:" may be the best of the lot,
Certainly not the best for all readers, but the simplest as of now ;-)
but it's ambiguous for the same reasons "do ... while <condition>:" is.
The source of ambiguity and even mislead is that it is placed on top while the condition is evaluated at end of loop. Anyway, it's an exit condition so it fits better down there IMO where ever it may be evaluated. The reason why I favor 'footer' options.
Many people will read that as "while not <condition>:".
My opinion about that is the following: * A new keyword is enough to force the reader to consider the code with attention and expect something different. * Obviously 'until' cannot have the same meaning as 'while' -- rather the opposite in fact. Don't you think so? (english is not my mother tongue) * The issues with '...' is that (1) it obfuscates the code (2) there is no link between the sense of ellipsis itself and the proposed semantics (*) Denis (*) It would be better used in ranges (python ':') or as line-continuation code (python '\'). ------ la vita e estrany

On Mon, Apr 27, 2009 at 3:19 AM, spir <denis.spir@free.fr> wrote:
As someone whose native language is English, I can say that Stephen's reading would indeed intuitively be considered the obvious logical opposite of a "while" loop; also, his interpretation matches the behavior of "until" loops in both Perl and Ruby. Cheers, Chris -- http://blog.rebertia.com

spir <denis.spir@free.fr> writes:
[off list -- because it's a side-issue]
Actually, you replied on-list, so I'll feel free to do the same :-)
You can't have it both ways, though: The keyword ‘until’ is already strongly associated with certain behaviour in other programming languages, which is presumably a strong reason to choose it for this instance. But it also has a strong English-language relationship with ‘while’.
That's exactly what Stephen's saying: “until <condition>” and “while not <condition>” are fungible in English.
Denis
Denis, could you please change your From field so that it shows the name “Denis Spir” which you're clearly happy to be known as? -- \ “It is the responsibility of intellectuals to tell the truth | `\ and to expose lies.” —Noam Chomsky, 1967-02-23 | _o__) | Ben Finney

Raymond Hettinger wrote:
I don't like the condition-at-the-top variants, with or without an ellipsis. (At first glance anyway.)
I couldn't quite parse that, but I sure hope it meant "I'm up for yet another round of discussion". I propose we allow an "if" suffix to "break" and "continue". So I'd spell your loop this way: do: j = _int(random() * n) continue if j in selected This would require there be no implied "while True" for a "do" block. If you hit the end of the "do" block, without following a continue, you'd just leave the block. So this program would be legal: do: print("wtf?") print("well, that was pointless") It would print two strings then exit. It would *not* loop. A little unexpected I guess. On the other hand this form gives a pleasing alternate spelling to "while True": do: continue It also provides a dandy spelling for early-exit programming, without nesting your ifs, or early return, or throwing an exception, or using gotos which thankfully we don't have: def user_typed_1_then_2(): do: print("enter 1, then 2") i = input() break if i != '1' i = input() break if i != '2' return True print("why do you mistreat me so? it was a simple request!") return False Maybe it's just me, but I think that spelling and those semantics are charmingly Pythonic. HTH, /larry/

I like the structure of the exising PEP 315 proposal, but not the selection of keywords. It seems confused -- what exactly are you repeating? The part after the "do", the part after the "while", or both? My suggestion is while: <statements> gives <condition>: <more statements> There could also be a degenerate form without the second suite: while: <statements> gives <condition> -- Greg

Raymond Hettinger wrote:
Though I would like a fair hearing for the "do ... while <cond>:" proposal. To my eyes, it has clear meaning
I think that's only because you already know what it's supposed to mean. To me, it's very far from clear that the "..." means "the expression after this really goes way down there after the suite". Also, anything which only addresses test-at-bottom and not test-half-way-through only covers a very small proportion of use cases, IMO. -- Greg

Raymond Hettinger <python@...> writes:
Yes. Absolutely.
Though I would like a fair hearing for the "do ... while <cond>:" proposal.
I find it disturbing. First, the condition appears on top but is only evaluated at the end of the loop. Second, it's not usual for synctatical tokens to be composed of several words ("do ... while" versus "while", "if", "for", etc.). Third, it introduces a semi-keyword ("do") which won't be used anywhere else, and abuses an existing object ("...") as synctatical glue. I'm in favor of not trying to solve this particular "problem". Keeping the grammar simple is valuable in itself (look at Javascript, C++...). Regards Antoine.

On Sat, Apr 25, 2009 at 7:35 PM, Larry Hastings <larry@hastings.org> wrote: <snip>
I don't see why the if-as-suffix is needed when we already have a one-liner for such situations (e.g.): if i != '1': break It's much more uniform and only one character longer. Cheers, Chris -- I have a blog: http://blog.rebertia.com

Chris Rebert wrote:
I certainly see your point. Let me take it a step further: the "do: ... while <condition>" construct isn't needed, given that it's already expressible with "while True: ... if not <condition>: break". It's true, "break if <condition>" and "continue if <condition>" are redundant constructs. But this debate is over refining our syntactic sugar--charting what is arguably a redundant construct. Therefore proposing redundant constructs for the sake of clarity is on the table. I think "break if <condition>" and "continue if <condition>" enhance readability; they make the control flow pop out at you more than "if <condition>: break" and "if <condition>: continue" do. "break if" and "continue if" have the advantage of following established Python syntactic precedent. /larry/

On Sat, Apr 25, 2009 at 8:39 PM, Larry Hastings <larry@hastings.org> wrote:
Except the ternary operator only allows for expressions, not statements; IIRC, GvR was somewhat reluctant about having any sort of ternary operator, so this further extension seems unlikely to me. Allowing general if-suffixes on things also seems a bit Perlish, imho. I find the current state of syntactic simplicity quite appealing. Regarding visibility, how is the last word of the last line in a loop body, further set off by the if's colon, not visible enough? I personally haven't ever had problems spotting the breaks in loop-and-a-half code. On another note, I actually do like the bare-while-as-while-True idea quite a bit. +1 on that. Cheers, Chris -- http://blog.rebertia.com

Chris Rebert wrote:
The suffix form of "if" can also be found in generator expressions and list/set/dict comprehensions. What all these constructs have in common is that they're expressions, which means they can be buried in the middle of other expressions. And being able to bury an "if" in there too is a boon to the programmer--one that the "if" *statement* could not grant. But this is a subtle point, easily lost on non-language-nerds. I work with an excellent programmer who nevertheless didn't grasp the distinction, even as I attempted to explain it to him. So, for most Python programmers, the language feels like "an if statement looks like this, and btw you can have ifs in the middles and ends of lines too sometimes". I hardly think the suffix form of "if" to "break" and "continue" would be the conceptual straw that broke the back of the language's cognitive load. As for GvR, I gave up trying to predict his reactions a long time ago. Though I admit I'd be very surprised if my proposal, or indeed any proposal, won favor; PEP 315 is long enough in the tooth I fear we are doomed to never find a majority. (A majority being defined here as either 2/3 majority of python-dev voters, or indeed the lone BDFL.) Witness PEP 3103, the switch statement; I think we'd all like to have one, if we could only figure out how to spell it. And I personally would value a switch statement more than do/while, /larry/

On Sat, Apr 25, 2009 at 8:39 PM, Larry Hastings <larry@hastings.org> wrote:
FYI, this was proposed a few months ago (see the first post here: http://mail.python.org/pipermail/python-ideas/2008-September/002083.html ). I was and still am -1 on any variant of "continue if <condition>" or "break if <condition>" for the same reasons I was then. They are a slippery slope (why not "(raise Exception) if <condition>"?), and they hide the control flow to the left of the line where we are used to seeing it on the right side of the line. - Josiah

Larry Hastings wrote:
Hmm, so why add another keyword like "do", instead of just giving "while" a default condition "True", so that you could write while: state = do_stuff_here() if predicate(state): break I think that the missing condition at the top makes it pretty clear that the end condition must be inside the loop. Stefan

Stefan Behnel <stefan_ml@behnel.de> writes:
Or that there isn't an end condition at all. I don't see how this indicator is any less clear than the current spelling:: while True: state = do_stuff_here() if predicate(state): break -- \ “I would rather be exposed to the inconveniences attending too | `\ much liberty than those attending too small a degree of it.” | _o__) —Thomas Jefferson | Ben Finney

I think a legitimate issue here is that break and continue can be hard to see sometimes when they're nested several layers deep. That makes while True: ... if x: break less clear than it could be. So let me throw out two straw proposals: (1) Allow break and continue to be spelled BREAK and CONTINUE which makes them stand out a bit more. while True: floob yuzz if wum: BREAK thnad (2) Allow a line containing break or continue to start with a -> token outside the normal indentation. while True: floob yuzz if wum: -> break thnad I don't expect either of these proposals to attract support. I present them to put a small focus on the issue of making break/continue more visible. --- Bruce

Raymond Hettinger wrote:
I think having the "while" start some blocks but end others is very weird indeed and makes it harder to keep stuff in my head. I'd prefer a bare "while:" with a more visible "break if" and possibly "continue if" expressions. Visualize the while, continue, and break with syntax highlighting. while: j = _int(random() 8 n) break if j not in selected Or if the reverse comparison is preferred you could do... while: j = _int(random() * n) continue if j in selected break Or we could allow break and continue in the if-else expression syntax ... while: ... continue if (j in selected) else break or.. while: ... break if (j not in selected) else continue In this last case the final 'else continue' can be dropped off sense it would be redundant: while: ... break if (j not in selected) That would give us back the first example again. By moving the break and continue out from under the if statement it makes it more visible and follows a common pattern of having lines start with keywords, which is very readable with syntax highlighting. It also still allows the break to be anywhere in the body which is more flexible. Because it is vary close to the current while syntax, is there really any need to rename the bare 'while' to 'do'? I just think of it as 'do for a while' or short for 'while True'. Cheers, Ron

Ron Adam wrote:
I'd prefer a bare "while:" with a more visible "break if" and possibly "continue if" expressions.
I do like "break if" and "continue if", unsurprisingly as I suggested them in parallel. I'm not sure about the "while:"; your "while: ... continue if ; break" strikes down some of the clarity we're trying to achieve here. /larry/

Larry Hastings wrote:
The bare "while:" can be a completely separate issue. And... while True: ... continue if condition break I'm not sure why you think that is less or not clear. It's just an possible to do if you add 'continue if'. <shrug> The equivalent would be: while True: ... break if (not condition) But this reverses the test and that could express what you are doing in a less clear way. Being able to do it both ways is a good thing I think. If you allow the if-else syntax to be used with flow control keywords then you have 6 possibilities. break if condition else pass break if condition else continue continue if condition else pass continue if condition else break pass if condition else break pass if condition else continue It makes sense to shorten some of these.. break if condition # 'else pass' doesn't do anything continue if condition # "" I'm not sure the others are needed, they may allow some problems to be expressed more explicitly. The nice thing about extending python this way is these expressions may also work in for loops and possibly other places. Because python is based as much on practical need rather than just what is possible to do. We may only need the two last shorter versions above. But I won't complain if all six of the longer versions above also work. ;-) In anycase Raymond was asking for ideas that are consistent with pythons syntax and I think these suggestions are. The one inconsistant thing is if-else expressions normally return a value, and in these cases, they either should raise a syntax error or return None if they are used on the right hand side of an expression. Ron

Ron Adam wrote:
But putting "if" after "break" doesn't make the exit point any more visible to my eyes. If anything, it makes it *less* visible. I think there's another thing at work here, too. Having an outdented clause for the exit point makes it very obvious that it is part of the syntax of the loop structure, and consequently that there is only *one* exit point from the loop. This is in contrast to break statements, which are much less disciplined and can occur more than once and inside nested statements. -- Greg

Le Sat, 25 Apr 2009 21:50:52 -0500, Ron Adam <rrr@ronadam.com> s'exprima ainsi:
But (1) This does not give any advantage compared to existing syntax if condition: break | continue (2) It does not solve the problem which, I guess, is to express the loop's logic obviously -- meaning in the header (or footer) instead of hidden somewhere in the body -- and nicely avoid numerous "while 1:" and "break" as a side-effect. What Raymond's proposals provide. What is not obvious at all is that: do ... while j in selected: j = _int(random() * n) means the assignment will be done at least once (and no NameError thrown). Where/how is it said? The above code is supposed to mean something like: do once then while j in selected: j = _int(random() * n) But maybe it's only me. Footer solutions (condition at end of loop) seem not to have Raymond's favor; still they are obvious in comparison: do: j = _int(random() * n) <your favored end condition syntax> j in selected Denis ------ la vita e estrany

On Sun, 26 Apr 2009 11:00:26 am Raymond Hettinger wrote:
A quick-and-dirty data point for you. Using Google code search, I found approx 80,000 hits for "lang:Pascal until" versus 129,000 hits for "lang:Pascal while". So the number of cases of test-at-top versus test-at-bottom loops are very roughly 3:2 in Pascal code indexed by Google. (Disclaimer: both searches revealed a number of false positives. I've made no attempt to filter them out.) For those who aren't familiar with Pascal, the test-at-top and test-at-bottom loops are spelled: WHILE condition DO BEGIN suite; END; and REPEAT suite; UNTIL condition; WHILE exits the loop when condition is False, and REPEAT...UNTIL exits when condition is True. So I think the first question we need to deal with is, should we be discussing do...while or do...until? Is there a consensus on the sense of the condition test? while True: # do...while, exit when condition is false suite if not condition: break or while True: # do...until, exit when condition is true suite if condition: break Or do we doom this proposal by suggesting variant spellings that cover both cases?
I'm not entirely comfortable with writing the test at the top of the loop, but having it executed at the bottom of the loop. But I'd prefer it to no do-loop at all. Oh, I should ask... will whitespace around the ellipsis be optional?
I don't like either of those variants. I agree, they feel strange. The only variant that I find natural is the Pascal-like: do: suite until condition where the "until" is outdented to be level with the do. My vote goes for this, although I'm probably biased by my familiarity with Pascal. How does that feel to you? I don't think I could live with do: suite while condition even though it isn't strictly ambiguous, nor does it prevent nesting a while-loop inside the do-loop: do: suite while flag: another_suite while condition But it looks disturbingly like an invalid line. If outdenting is impossible, then I could live with it being indented: do: suite until condition # or while if you prefer but what happens if there is indented code following the until/while? Syntax error, or does it just never get executed? My preferences, in order of most-liked to least-liked, with my score: (1) +1 do: suite until condition # exit loop when condition is true (2) +0.5 do...until condition: # exit loop when condition is true suite (3) +0.5 do...while condition: # exit loop when condition is false suite (4) +0 do: suite until/while condition mystery_suite # what happens here? (5) -0 The status quo, no do-loop at all. (6) -0.5 do: suite :while/until condition (7) -1 do: suite while condition -- Steven D'Aprano

Le Sun, 26 Apr 2009 16:42:36 +1000, Steven D'Aprano <steve@pearwood.info> s'exprima ainsi:
Same for me.
I could survive it because it is linked to the "do:" header. But the real drawback for me is that it should rather express the exit-condition, because it is placed at the end. Anyway, I would support the "while" version if "until" gets to much opposition (for introducing 2 new keywords instead of only 'do'). Both are much better imo than the header versions. Denis ------ la vita e estrany

On Sat, Apr 25, 2009 at 7:00 PM, Raymond Hettinger <python@rcn.com> wrote:
We can actually already do this, via a little used option of iter(): for data in iter(lambda: f.read(20), ''): .... The primary limitation is the need to use lambda (or functools.partial, but it's no better). The next limitation is that even with lambda you can only use an expression, not a statement. You can bite the bullet and define a function first, but that ends up being better accomplished with a generator, and in these cases it's often better still to use a while True: loop. Back to square one. I'm not sure all the reorderings are actually more *readable*, rather than just more appealing to write. -- Adam Olsen, aka Rhamphoryncus

Mathias Panzenböck <grosser.meister.morti@gmx.net> writes:
Already addressed. We already have a way of spelling that, which is ‘while True:’. There needs to be some significant gain to introduce a new spelling for the same thing. -- \ “Good morning, Pooh Bear”, said Eeyore gloomily. “If it is a | `\ good morning”, he said. “Which I doubt”, said he. —A. A. Milne, | _o__) _Winnie-the-Pooh_ | Ben Finney
participants (17)
-
Adam Olsen
-
Antoine Pitrou
-
Ben Finney
-
Bruce Leban
-
Chris Rebert
-
Eric Smith
-
George Sakkis
-
Greg Ewing
-
Josiah Carlson
-
Larry Hastings
-
Mathias Panzenböck
-
Raymond Hettinger
-
Ron Adam
-
spir
-
Stefan Behnel
-
Stephen J. Turnbull
-
Steven D'Aprano