[swift-evolution] Add a while clause to for loops

Xiaodi Wu xiaodi.wu at gmail.com
Tue Jun 7 19:54:06 CDT 2016


On Tue, Jun 7, 2016 at 6:26 PM, Tim Vermeulen <tvermeulen at me.com> wrote:

> Interesting. What if you put newlines before `where` and `while`? It’s
> hard to get the spacing right in a mailing list, but I tried it in Xcode
> and it looks really good to me (except for the compiler error it currently
> produces). Way better than the way I wrote it initially and the
> alternatives you mentioned.
>
> for number in fibonacci
>     where number % 2 == 0
>     while number < 4_000_000 { }
>

This last line here is indistinguishable from the beginning of a while
loop. I still think my examples above are superior, but even if you don't
want to operate on the sequence beforehand, we can rewrite your multiline
statement inside the loop in a crystal-clear way, no dollar signs or
anything:

```
for number in fibonacci {
  if number % 2 != 0 { continue }
  if number >= 4_000_000 { break }
}
```

Now the behavior is written out in full. It has the following advantages:

1) It is spelled out exactly what happens when a condition is met. I no
longer have to remember whether the word that describes breaking from a
loop uses a place analogy ("where") or a time analogy ("while" or "when").

(You cannot convince me that these words are intuitive when the meaning of
"where" changes by context in today's Swift. Now, if you want to propose
that these be named "breakif" and "continueif" instead, then I'd agree with
you that they're intuitive names, but then they'd also be really ugly.)

2) It is absolutely clear which if statement gets evaluated first. Of
course, we can make a rule for your proposed keyword, but there's no basis
for assuming a priori that such a statement must be evaluated from left to
right without consulting the Swift-specific rulebook. Take, for example,
Python `foo if bar else boo`. By contrast, the if statements shown above
are clearly evaluated one after the other and would be evaluated in the
same order in every language under the sun.

3) I have the flexibility to do something between the first if statement
and the second if statement, if I want. By placing the break statement at
the end of my loop, I could effectively choose to have one more iteration
than if I placed it at the beginning of my loop. There is nothing you can
do to mimic that choice with your proposed while clause, unless you want to
also propose a `for...in...repeat { } while` syntax.

4) This is the perhaps the important point. A beginning programmer--not any
of us, presumably, but we were all beginners once--can accomplish
everything that he or she desires without learning this new proposed
syntax. Almost all texts, I believe, teach if statements before loops, and
teach break and continue in the same breath as the loops themselves.

Each new piece of syntax is a challenge, and making connections between two
equivalent facilities can be very, very hard. I've witnessed truly
brilliant people who are starting out to code throw up their hands in
frustration because it feels unnecessarily cruel that they must learn
several synonyms for the same concept. One of them asked me recently, "If I
can write i = i + 1, why would I ever write i += 1 or i++? There must be
something special about ++, surely? Does it have any other uses?" He
concluded that he did not in fact understand assignment operators; I could
not convince him that he understood them perfectly adequately.

Adding sugar for visual elegance carries this cost to the learner, and it
is a real and non-trivial cost. Many beginners, I find, can accept that
they'll read function names that they haven't heard of; after all, they can
see that anyone can make a function named anything. But, a keyword or a
core part of the language's syntax? Please appreciate that every addition
there increases the learning curve for everyone.

Given that so many choices about how to write a loop are already available,
you haven't convinced me that having another one brings tangible benefit to
the language or to the code that I write. By contrast, I would cringe if I
saw something like `for number in fibonacci where number % 2 == 0 while
number<4_000_000 { }` written on one line (and there is an understandable
natural tendency to want to put a single statement all in one line). IMO,
the Swifty way, with its emphasis on not having multiple "dialects" of the
same language, would be to *decrease* the number of ways to write the same
loop to one or a small number of well-considered, generally useful
facilities. That's why I'm in favor of cutting down on sugar rather than
increasing it.


>
> > On Tue, Jun 7, 2016 at 5:11 PM, Tim Vermeulen<tvermeulen at me.com(mailto:
> tvermeulen at me.com)>wrote:
> > > I’ve been thinking about this for a bit now, and I think it would make
> most sense to evaluate these clauses from left to right. However, cases
> where the order matters are very uncommon, and I would rather have the
> power to choose which clause is evaluated first than to have a forced
> default order. Either way I don’t see this as a reason not to allow
> combining the two clauses because IMO it can lead to some very clean code.
> For instance, say we want to loop through all even fibonacci numbers below
> 4 million(see problem #2 from project euler), we could do this:
> > >
> > > `for number in fibonacci where number % 2 == 0 while number<4_000_000
> { }`
> > This statement looks like spaghetti to me. I would not at all support
> extending the language to permit it. Do you really think it's more readable
> than going step-by-step?
> >
> > ```
> > let numbers = fibonacci.prefix { $0<4_000_000 }
> > for number in numbers where number % 2 == 0 {
> > // ...
> > }
> > ```
> >
> > or just:
> >
> > ```
> > let numbers = fibonacci.prefix { $0<4_000_000 }
> > let evens = numbers.filter { $0 % 2 == 0 }
> > for number in evens {
> > // ...
> > }
> > ```
> >
> > >
> > > I could have ordered the two clauses in any way I want. If combining
> the clauses weren’t allowed, I’d have to put (at least) one of them inside
> the block, which would be a (minor) pain.
> > >
> > > I don’t currently have a very strong opinion about the order of
> evaluation, so I might be convinced otherwise. But combining the two
> clauses is so powerful that I don’t think it’s worth to get rid of just
> because of an edge case.
> > >
> > > >It may be workable if you can have only one or the other, but mixing
> and matching them as proposed above would be a world of hurt:
> > > >
> > > >```
> > > >for foo in bar where condition1 while condition2 { ... }
> > > >```
> > > >
> > > >If condition1 and condition2 both evaluate to true, then whether you
> continue or break would depend on the relative order of where and while;
> for generality, you would want to allow both `for...in...where...while` and
> `for...in...while...where`, and likely `for...in...while...where...while`,
> etc. There is nothing in the meaning of those words that would suggest that
> `while...where` behaves differently from `where...while`, etc. This is why
> words like "break" and "continue" are IMO far superior.
> > > >
> > > >
> > > >On Tue, Jun 7, 2016 at 2:34 PM, Erica Sadun<erica at ericasadun.com
> (mailto:erica at ericasadun.com)(mailto:erica at ericasadun.com)>wrote:
> > > >>
> > > >>>On Jun 7, 2016, at 1:16 PM, Tim Vermeulen via swift-evolution<
> swift-evolution at swift.org(mailto:swift-evolution at swift.org)(mailto:
> swift-evolution at swift.org)>wrote:
> > > >>>>The meaning of the proposed while is not at all a pair for where,
> since where clauses in while loops would do the same thing as while clauses
> in for loops. That's crazy.
> > > >>>
> > > >>>It sounds crazy, but it’s the nature of the while loop. A where
> clause in a while loop also has a different result than a where clause in a
> for loop.
> > > >>
> > > >>The where_clause appears in the for in statement
> > > >>
> > > >>for_in_statement : 'for' 'case'? pattern 'in' expression
> where_clause? code_block
> > > >>
> > > >>It's syntactic sugar because the expression can be already be
> limited through functional chaining of some sort or another. At the same
> time, it's nice and pleasant to have `where` and I'm not itching to throw
> it out. The same courtesy could be easily extend to `when` (because I don't
> really want to use the `while` keyword here, but I could easily be
> convinced otherwise because I don't have a strong stance either way):
> > > >>
> > > >>for_in_statement : 'for' 'case'? pattern 'in' expression
> (where_clause | when_clause)? code_block
> > > >>when_clause : 'when' expression
> > > >>
> > > >>and again it could be nice and pleasant to have, although not
> necessary. The question comes down to how much does the language benefit by
> this sugar.
> > > >>
> > > >>I'd say that in both cases, combining chaining and statements is
> marginallyless goodthan either using standalone chaining or statements
> without chaining. But as I say this, I know as a fact, I fully intend to
> use `sequence(_:, next:).take(while:)` with for0in statements, so I'm
> starting from a hypocritical vantage point.
> > > >>
> > > >>To summarize, I'm more +0.01 than I am -0.01 on this.
> > > >>
> > > >>-- E
> > > >>p.s. Sorry, wux
> > > >>
> > > >>
> > > >
> > > >
> > > >
> >
> >
> >
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160607/b3f7721c/attachment.html>


More information about the swift-evolution mailing list