A single, general looping construct? (was: why no "do : until"?)

Alex Martelli aleaxit at yahoo.com
Thu Jan 4 06:07:39 EST 2001


"William Sonna" <wlsonna at attglobal.net> wrote in message
news:0O7zTzsxerl5-pn2-SISYKtwXTOmR at bill...
    [snip]
> Object Rexx's implementation of the generalized do is the best I've
> seen (in a real language, that is).  Works like this:

Snipping: 5 different forms that _still_ don't cover the Knuth-identified
general case of "N-and-1/2-times" loops.  We clearly have a very
different idea of what the word "best" means.

*Entities are not to be multiplied needlessly* -- Occam's razor.  ONE
general loop form, plus one form for the very important case of loops
over a sequence, seems just right to me.


> while count < 100:   # zero or more iterations
    [snip]
> until count <=100:    # one or more iterations
    [snip]
> Which are all (with the exception of the ones that are identical or
> nearly identical) cleaner and even more Pythonic than Python.

We clearly also disagree on the adjectives 'clean' and 'Pythonic'.

Since when is it Pythonic to have many equivalent ways to express
one concept?  It's not Python that has both 'if' and 'unless', for
example -- it's Perl.  A language which has both while and until
for loops, but doesn't have both if and unless for non-loop tests,
is inconsistent; one which has one form in each case, and one which
systematically adopts multiple forms whenever feasible, are each
consistent, albeit with, clearly, VERY different underlying
philosophy.

"Do the simplest thing that can possibly work", "there should be
exactly one way to perform a task" -- that is one philosophy.  It
is Python.  The exactly-opposite philosophy, that of "the more
ways, the merrier", of exhuberant, post-modern richness, is Perl's.
How can you possibly call 'Pythonic' something which goes so
drastically against the grain of its basic tenets?

> Fortran had even simpler control structures than Python, and was a
> royal pain to use as a result.

Surely you're joking, Mr Sonna.  Have you ever USED Fortran?!  I
must have written and maintained many tens of thousands of lines in
it.  How can you POSSIBLY assert that the set of its control
structures is "simpler than Python's"?!  Simple GOTO, assigned
GOTO, computed GOTO, logical IF, arithmetic IF, the DO statement...
it's *brimming* with complexity!  (Let's not even get into
Fortran 77, which added even more).  Just the rules about what
kinds of GOTO's are acceptable across which kind of DO-statement
boundaries get longer to explain than the whole Python language!-)

Anyway, simplicity is not enough -- it's the simplest thing *that
can possibly work*.  A language with a truly simple set of control,
well, 'structures', was Snobol -- the single concept of 'success
and failure' (with backtracking), and conditional jumps controlled
by success and failure.  The simplicity of conditional jumps as
the only control-structure doesn't work well with the human mind's
limitations in keeping track of disparate things -- so the need to
wrap up those jumps into actual structures is well accepted today
as a working need.  (The success/failure concept, and backtracking
to go with it, are far from being proven failures -- Icon took
them from Snobol, wrapped them into actual structures, and was [is]
a very interesting language as a result; unfortunately, the set
of structures chosen emerged as far from simple -- just explaining
the difference between 'while' and 'every' to an Icon beginner is
a headache).

The need for structure is not under discussion: rather, the basic
guideline on how to pick the structures.  Some languages bask in
'being very expressive' by making you choose among umpteen ways
to express the same thing, a la Cobol's
    ADD CREAM TO ESPRESSO GIVING CAPPUCCINO
vs
    LET CAPPUCCINO = CREAM + ESPRESSO

I'm quite happy for people who like such "expressive richness"
to pursue it in the many languages that follow this "the more the
merrier" philosophy -- Cobol and Perl being just two reasonably
consistent examples of such redundance.  It would just be nice
if, in turn, such people accepted the existence of languages
based on a *diametrically opposite* underlying philosophy of
*language simplicity* as a value -- and Python is a pretty good
example of such a language (not _perfect_, of course -- it IS
a human endeavour, and thus fallible, witness the 'print>>'
construct... but, quite reasonably close).

Having several different and perfectly equivalent ways of
expressing the same thing, just because you believe that
    bleep() unless bloop();
is 'prettier' ('more directly expressive', whatever) than
    if not bloop(): bleep()
is Perl's way.  If you want Perl, you know where to find it.
Just don't try to Perl-ize Python... ONE Perl is enough (some
would say, far more than enough).


> Although I enjoy using Python and am generally supportive of its
> minimalist philosophy, I still believe that the "while 1:  buried
> break" is a step backwards.

I have repeatedly agreed that it's not optimal syntax, but
*adding* special-case syntax forms is exactly the wrong way
to go.  ONE general syntax form for general loops:
    loop <condition1>:
        <suite1>
    while <condition2>:
        <suite2>
would probably be ideal (with the loop-over-sequence form,
since it's *such* a common case, singled out).  But it ain't
gonna happen -- the gain is just too small for the disruption.


> Many of the classic algorithms are best expressed with "do until" or
> "repeat until" and translating them into Python requires the
> introduction of a uniquely inelegant language idiom.

I just don't think they're that many!  Out of all loops one needs
I think about 2/3 are best expressed as loop-over-sequence; of
the remainder, about 2/3 are best expressed as single-test ones
with the test at the start; of the remainder, about 2/3 need
the general test-in-the-middle form for best expression.  Just
a few % are left in the special-case form where test-at-the-end
would prove best.  Of course, one general loop form would cover
them all equally, so the exact numbers don't matter much (save
for the enormous frequency of loop-on-sequence, which justifies
singling it out).

I *particularly* disagree that Python's syntax for the general
loop is "uniquely inelegant", whatever you're trying to convey
with that "uniquely".  The spelling of the second clause in
the general loop form as (indented) 'if not <condition2>: break'
is, at worst, slightly awkward -- the very popularity of 'break'
as a keyword in other languages (and the extremely similar
semantics of equivalent keywords in languages which have a
slightly different syntax, such as Perl's "last") helps to
mitigate the perception of this awkwardness.

What WOULD be truly inelegant, in my view, would be to have a
zillion ways to express special cases of one general form; at
least we have the general form itself, albeit not with optimal
syntax sugar to accompany it.


Alex







More information about the Python-list mailing list