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

Xiaodi Wu xiaodi.wu at gmail.com
Wed Jun 8 18:43:58 CDT 2016


On Wed, Jun 8, 2016 at 6:05 PM, Tim Vermeulen <tvermeulen at me.com> wrote:

> > Since Swift strives to be an opinionated language without dialects,
> there shouldn't be more "choice" but rather one general solution, IMO.
>
> I agree with you on this in general, but this proposal isn’t just about
> adding choices to the language. At least, that’s not the point. It doesn’t
> add any new functionality per se, but there might be value in making a
> common coding pattern more shorter and more intuitive. `guard` is a lot
> more powerful than `where` or `while` as discussed here, but such clauses
> can possibly make code more readable. You don’t have to agree with me that
> those clauses are actually more readable than using `guard`, but that’s the
> way I see it and in my opinion it’s about more than just having more
> options.
>
> In fact, if this proposal is accepted, one could consider `guard
> someCondition else { continue/break }` at the start of a for loop to be a
> code smell because a `where`/`while` clause could have been used instead.
> So using those clauses would then be the general solution to the problem,
> which is in line with what you said Swift strives to be.
>
> To me this issue feels similar to adding `guard` despite already having
> `if` (if we disregard `guard let` for a second). Now you can write both
> `guard someCondition else { return }` and `if !someCondition { return }`,
> but I would consider the first one the general solution and the second one
> code smell. This pattern is so common that adding `guard` was justified.
>

Well, we shouldn't ignore `guard let`, because there's a major difference
between `guard let` and `if let`: the scope in which the unwrapping takes
place. When `guard` was introduced, it solved a real problem encountered in
everyday use: the `if let` pyramid of doom. If you'll recall, before the
introduction of `guard`, unwrapping several optionals in a row resulted in
code that was deeply indented and very unsightly to read. By contrast, I'm
arguing that the code being written currently is actually quite pretty
without `while` clauses in `for` loops, so I don't think the same parallels
apply.

There's another major feature of `guard`: it's not just a drop-in
replacement for `if !` because it enforces exiting the scope. Using `guard`
means you get an error when you forget to write something that exists the
scope, and I've been saved by that feature more than a few times. By
contrast, `where` or `while` does not enforce any comparable limits on what
comes after it (one conceivable limit, which apparently can't be enforced
by the grammar, is a semantic relationship between what's being looped over
and the boolean assertion that follows), nor does it even guarantee that
the loop will terminate! (I can write, for example, `for i in
sequence(first: 0, next: { $0 }) where 2 < 4 { ... }` and there's nothing
you could do to warn me!


>
> > On Wed, Jun 8, 2016 at 1:58 PM, Tim Vermeulen<tvermeulen at me.com(mailto:
> tvermeulen at me.com)>wrote:
> > > That’s why I said “potentially less elegant”, some people might prefer
> `where` over `guard`. This proposal would give them the choice (in very
> specific situations) to use `where` rather than `guard` if they don’t want
> to sacrifice performance.
> > Since Swift strives to be an opinionated language without dialects,
> there shouldn't be more "choice" but rather one general solution, IMO.
> Since `guard` doesn't sacrifice performance and is the most general, I
> would oppose adding the option of `while` to offer more choice.
> >
> > >
> > > >On Wed, Jun 8, 2016 at 1:35 PM, Tim Vermeulen via swift-evolution<
> swift-evolution at swift.org(mailto:swift-evolution at swift.org)(mailto:
> swift-evolution at swift.org)>wrote:
> > > >>This is a really strong argument in my opinion. If we don’t add a
> `while` to for loops, then in some situations we will have to rewrite a
> `where` clause to something potentially less elegant, given that we don’t
> want to give up performance.
> > > >I disagree. I argue that what you call "less elegant", namely if (or
> guard) inside the loop, is the most elegant solution.
> > > >
> > > >>
> > > >>>IMO `.prefix` is just not the equal alternative for as proposed
> `while` :
> > > >>>in case of 'while' expression `number<4_000_000` will be calculated
> > > >>>*only* for those who `number % 2 == 0`. In case of `prefix` - the
> > > >>>expression will be processed for each `number` and only after this
> filtered
> > > >>>by `number % 2`. Let's assume we need to check for some
> > > >>>veryExpensiveTest(number):
> > > >>>
> > > >>>for number in fibonacci where number % 2 == 0 while
> > > >>>veryExpensiveTest(number) {}
> > > >>>
> > > >>>let numbers = fibonacci.prefix { veryExpensiveTest($0) }
> > > >>>for number in numbers where number % 2 == 0 {}
> > > >>>
> > > >>>So, `while` for `for` loops just can't be always replaced with
> `prefix`
> > > >>>
> > > >>>On 08.06.2016 2:02, Xiaodi Wu via swift-evolution wrote:
> > > >>>>On Tue, Jun 7, 2016 at 5:11 PM, Tim Vermeulen<tvermeulen at me.com
> (mailto:tvermeulen at me.com)(mailto: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)
> > > >>>><mailto: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)
> > > >>>><mailto: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
> > > >>>>
> > > >>>>
> > > >>>>
> > > >>>>
> > > >>>>_______________________________________________
> > > >>>>swift-evolution mailing list
> > > >>>>swift-evolution at swift.org(mailto:swift-evolution at swift.org
> )(mailto:swift-evolution at swift.org)
> > > >>>>https://lists.swift.org/mailman/listinfo/swift-evolution
> > > >>>
> > > >>>
> > > >>>
> > > >>_______________________________________________
> > > >>swift-evolution mailing list
> > > >>swift-evolution at swift.org(mailto:swift-evolution at swift.org)(mailto:
> swift-evolution at swift.org)
> > > >>https://lists.swift.org/mailman/listinfo/swift-evolution
> > > >
> > > >
> > > >
> >
> >
> >
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160608/84dfecdd/attachment.html>


More information about the swift-evolution mailing list