On Fri, 10 Sep 2021 09:36:36 +1200, dn via Python-list <python-list@python.org> declaimed the following:
>Why does Python not have a repeat-until loop construct? >(or should that be 'modern programming languages'?) > I would suspect Python's indentation for block structure would be the major hindrance. After all, all existing block constructs /open/ the block. if ...: block else: block elif ...: block try: block except ...: block for ...: block while ...: block def ...: block class ...: block so how would repeat: block until ... <NO BLOCK> fit the language. The alternative would be repeat until ...: block putting the condition at the top, even though it is only tested at the bottom (after processing <block> at least once). Granted, that IS the style used in REXX, where DO/END are generic block boundary marks, with the DO accepting all the loop constructs (FOR, WHILE, UNTIL) as optional parts. >This is a perennial question (one contributor calling it "immemorial"), >but there seem to be reasons why the Python Interpreter would find such >a construct awkward, or is otherwise unable to comply. If so, what does >one need to understand, in order to comprehend the (apparent) omission? > >NB I'm not asking 'how to do this with while?'. > > >TLDR; >- wherein the historical background is explored, a possible 'gap in >knowledge' exposed, alternative implementations discussed, PEP-proposals >critiqued, and related-questions (re-)asked at the end... > > >If the question itself doesn't appeal to you, perhaps some of the >discussion and web.refs (below) will. Happy Friday. Happy thinking! > > >The term "Structured Programming" was coined by Edsger W Dijkstra. It >proposed a number of "control structures" (which were largely >unavailable in the programming languages of that time): > >- sequence: a series of statements/routines to be executed in sequence >- selection: if...then, if...then...else..., case >- iteration: while, repeat (do...until), for >- recursion: a routine 'calling itself' as a cascade > >The 'content' or 'process' of each structure was a block (or in Python >terminology: a "suite") consisting of any/all of the above (thus >"nesting"). Python's indentation practice, today likely descended from >this concept. > > >Much of the development of the ideas behind Structured Programming that >followed the crystallisation of this list of constructs, were attempts >to mathematically (logically) 'prove' code as "correct". > >One of the ideas to (help?) make things more prove-able, was that each >block and construct have only one way in (entry), and one way out >(exit), eg (from Wikipedia) "The conditional statement should have at >least one true condition and each condition should have one exit point >at max ... Often it is recommended that each loop should only have one >entry point (and in the original structural programming, also only one >exit point, and a few languages enforce this)" which as they say, was an >idea later dropped/felt to be somewhat impracticable (but to which theme >I shall return...) > >Even in fairly modest Python constructs, we quickly repeal the one-in, >one-out philosophy because try...except operates by providing another >exit-path. > > >The 'structures' (or "constructs") of Structured Programming were >fore-runners of the Software Patterns and SOLID Principles >commonly-practised today. These ideas still hold the same goal of >trading a degree of abstraction for programming simplicity, possibly >testability, and improved quality. > >Today, Python offers almost all of the SP constructs. A form of >case/select is expected in v3.10. The continuing omission is repeat-until. > > >If you have not met such a code-component before, the idea of a >repeat...until (or do...until) might look like this: > > repeat: > code-suite > until condition > >Thus, the code-suite will be executed as many times as necessary, until >the condition is met. > > >In Python, we are used to while-loops, which can be expressed in the >same style as: > > while condition: > code-suite > >What's the difference? > >The answer is that the repeat's code-block MUST be executed at least >once. Whereas a while's code-suite could be totally ignored and not >executed at all! > >An analogy is to RegEx and its * and + repetitions: > >* means zero, one, or more matches >+ means (at least) one, or more matches > > >During the last weeks 'here', writing a while-loop was a topic of >conversation. A solution offered to the OP, can be described as: > > loop-init-code > while True: #in other words, loop forever > code-suite > if condition: > break > >Note three things: > >1 the while condition has been bastardised - there is no meaningful >condition, it is designed to loop without thought or control* > >2 the control condition within and ending the loop's suite exactly >replaces the until-condition of a repeat-until construct > >3 the cyclomatic-complexity of the multi-faceted construct is much >higher than of a 'pure' while-loop (or for-loop) > >NB "cyclomatic complexity" is an attempt to measure a program's >complexity based on the number of distinct paths or branches in the code >(please recall earlier comment about 'entry and exit'). > >* in one of the web.ref discussions, our own @Chris suggests taking >advantage of the 'truthiness' of data, and inserting some documentation: > > while 'there is data to process': > >Which is a considerable improvement over the bland 'loop forever' or >'loop until I tell you otherwise, according to criteria I won't reveal >until later' (an operating mode every?no teenager would accept, >on-principle! - including this one...) > > >This form is a regularly recommended as a 'solution' (see first Answer >to SO question). However, it is likely to require some set-up (which is >technically preceding, and therefore outside of the construct, yet the >construct is highly-dependent upon it. (this may be unavoidable, regardless) > >Most importantly (regretfully), another construct has been added at the >(middle or) end of the loop to perform work which has been displaced >from the while-condition. > >So, whereas a repeat...until is a single construct encapsulating its >code-suite, the while+True...if+condition-break, forms two constructs >'around' the code-suite - and in some circumstances the code-suite may >be effectively split into two by the positioning of the added if+condition. > >None of this is calculated to lead to 'the simple life' and soothe minds >into the Zen of Python! > > >Whereas most of this discussion is at the theoretical level, I have >spent several 'happy hours' hacking-away in the hope of finding a >practical solution to, or work-around for, this issue. Mostly >disappearing down the ast-and-exec rabbit-hole. > >In a sub-set of possible-solutions "the time has come [for] the walrus >[operator]" ('improving' a line from a Lewis Carroll poem). > >However, most solutions will require some retention of 'state'. >Accordingly, generators - which will also work in simpler cases. > >They in-turn led me all the way to a class. I'm still playing with that >progression... > >Sadly, am of the feeling that the 'cure' may be (as much criticised and) >more painful than 'the disease'... > > >Returning to an earlier point: for the 'pure' while-loop there is >exactly one way 'in' (entry), and one way 'out' (exit). The above, >loop-forever idea complicates matters, because when reading the code, >one's first understanding is that the while will control the indented >code-suite beneath - be its (single) exit. However, further reading >reveals that there is a second way 'out'. I should say, "theoretically", >because while True offers no escape - that implies it is no "exit" (the >"Hotel California" clause). So, reading the "while" creates an >expectation, but that must be immediately discarded when our eyes reach >the True-condition! > > >In the military, we were taught to always have a (current and >applicable) Escape Plan. If you've travelled by airplane/aeroplane you >will remember the crew giving a Safety Drill, and asking you to be aware >of your nearest exit should it be necessary to rapidly depart the plane >- and that "the closest may be behind you". These plans have virtue, >because in the chaos of an emergency, the time it takes to work-it-out >on-the-fly (hah!) may cost your life (no joke!). > >To be sure, the extra time and effort required to read a bastardised >Python while-loop is hardly likely to be a matter of life or death (I >sincerely hope!), but it is grounds for complaint or 'muttering'. > >When introducing trainees to recursion, I repeat over-and-over (and >-over) that the very first thing to do, is to code the termination >condition (the Escape Plan)! If you've ever coded recursive constructs, >you've almost certainly seen someone who hasn't followed this (simple) >plan - in your bath-room mirror... > >The same principle applies to any while+True construct. Left alone, it >will not stop. In this situation, having to prepare by thinking about an >escape-route is a fundamental flaw. When you start coding the loop, your >mind is more interested in the code-suite - the business of the loop! >Once that is coded we will (in the normal course) be ready to >think-about 'what happens next'. > >Sure, if one forgets the termination-clause, Python will save your life >- and it is likely that no-one else will notice. Does that make it >'right'? Doesn't it indicate that there's a 'smell' of something wrong? > >In programming, consideration of "cognitive load" very much applies. We >are unlikely to ever need to escape from a smoke-filled development >environment, but we do have (more than) enough to think-about when >coding. Indeed the essential virtue of Structured Programming, SOLID, >software patterns, etc, is to reduce cognitive load by offering >tried-and-tested solutions, templates/abstractions, re-usable code, etc. > > >Am I the first to query the omission of repeat-until? No - not by a >long-shot! Raymond Hettinger and Isaac Carroll proposed PEP 315 back in >2003. The BDFL said: «Please reject the PEP. More variations along these >lines won't make the language more elegant or easier to learn. They'd >just save a few hasty folks some typing while making others who have to >read/maintain their code wonder what it means.» It was rejected. > >Reading it now, the proposed syntax seems more than a little clumsy; but >makes me wonder why a more 'usual' repeat-until format wasn't, or >perhaps couldn't, be used (see Parser question). > > >@Raymond had (at least one) another 'go' in 2009. His comments included: >«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...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).» > >Curiously, the last approach illustrated therein was to open the loop >with do: and terminate it with while+condition. I'd certainly concur >that the idea of using "while" at the 'head' of a while-loop and also at >the 'foot' of a repeat-until construct, seems "weird" (also (Tm)?). Am >not sure, perhaps didn't research far-enough, to see why another >construct-name, eg "until" was not considered. Can you point-out where >that is discussed, or give a reason 'why'? > > >As recently as 2017, David Murray addressed this issue with PEP 548 >"More Flexible Loop Control". Again, with all the benefits conferred by >hind-sight, the idea seems clumsy: replacing the if+condition-break with >break-if+condition (in similar fashion to ternary conditional operators, >list-comprehensions, etc). It comes across as syntactic-sugar. It does >not address the 'bastardisation' and extra-construct's >cyclomatic-complexity rise. > >Many points raised 'here' appear in another post at about that time (see >web.refs FYI). > > >In many cases, another common recommendation follows the lines of: > > do-something > while the-something-is-ok: > do-more > do-something > >This deserves criticism due to its code-repetition and thus >contravention of the DRY principle (Do not Repeat Yourself) - the >cyclomatic-complexity of the 'bastardisation' has been removed, but I'm >still discomforted. Are you? > >As mentioned earlier, a major criticism is that something that is only >being done to establish the looping mechanism (is "closely-coupled") is >separate from, not (initially at least) an integral-part of the >construct. That said, please recall earlier allowance, that some >'initialisation' may be necessary. > > >Perhaps I missed it as life unfolded: has there been a paper/line of >research which discounted the need for repeat-until, assuming a while >construct was available? > >Is a repeat-until construct missing from other >modern-languages, or is that a particular choice made in Python's design? > > >A paper from Duke (University) makes reference to a "Loop and a Half" >structure, with the particular example of completing an action until >some "sentinel-value" is reached. They presage my comments (below) about >"priming" the loop, the "sentinel" text as an additional construct, >and/or duplicate code - and how all that adds-up to making "the loop >body harder to understand since it turns a read-and-process loop into a >process-and-read loop." With sundry further admissions, they rewrite >into the bastardised form which has become Python's accepted-solution. > > >Perhaps it was not possible before, but will it become feasible under >Python's new (v3.9+) PEG parser? > > > >Web.Refs: >https://en.wikipedia.org/wiki/Structured_programming >Dijkstra "Notes on Structured Programming" >https://dl.acm.org/doi/pdf/10.5555/1243380 >Single-Entry, Single-Exit >https://web.archive.org/web/20121114195652/http://msmvps.com/blogs/peterritchie/archive/2008/03/07/single-entry-single-exit-should-it-still-be-applicable-in-object-oriented-languages.aspx >https://en.wikipedia.org/wiki/Cyclomatic_complexity >Ned Batchelder's McCabe plug-in https://pypi.org/project/mccabe/ >The Walrus and the Carpenter by Lewis Carroll >https://poets.org/poem/walrus-and-carpenter >Python's Parser https://www.python.org/dev/peps/pep-0617/ >PEP 315 https://www.python.org/dev/peps/pep-0315/ >BDFL Rejection >https://mail.python.org/pipermail/python-ideas/2013-June/021610.html >Later discussion >https://mail.python.org/pipermail/python-ideas/2009-April/004306.html >and >https://mail.python.org/archives/list/python-id...@python.org/thread/2VUZ3J6C4GSHGBZJW62AY4HPEEBMXAT6/#2VUZ3J6C4GSHGBZJW62AY4HPEEBMXAT6 >PEP 548 https://www.python.org/dev/peps/pep-0548/ >BDFL Rejection >https://mail.python.org/pipermail/python-dev/2017-September/149232.html >Python-Ideas post >https://mail.python.org/archives/list/python-id...@python.org/thread/EDNARFL2RGOE53SLWPTD5ZLJQOYSVDCR/#EDNARFL2RGOE53SLWPTD5ZLJQOYSVDCR >Duke Paper >https://users.cs.duke.edu/~ola/patterns/plopd/loops.html#loop-and-a-half >RegEx in Python https://docs.python.org/3/library/re.html >and https://docs.python.org/3/howto/regex.html >"bastardise" (meaning 1) https://www.dictionary.com/browse/bastardize >https://stackoverflow.com/questions/743164/how-to-emulate-a-do-while-loop >DRY >https://code.tutsplus.com/tutorials/3-key-software-principles-you-must-understand--net-25161 > >-- >Regards, >=dn -- Wulfraed Dennis Lee Bieber AF6VN wlfr...@ix.netcom.com http://wlfraed.microdiversity.freeddns.org/ -- https://mail.python.org/mailman/listinfo/python-list