Add a while clause to for loops

Gist: where.md · GitHub

Regularizing Where grammar
Proposal: TBD
Author: Brent Royal-Gordon, Erica Sadun
Status: TBD
Review manager: TBD
Introduction

This proposal fixes an inconsistency for where clause grammar in Swift language for-in loops.

Swift Evolution Discussion: Add a while clause to for loops

Motivation

Unlike in switch statements and do loops, a for-in loop's where-clause is separated from the pattern it modifies.

for case? pattern in expression where-clause? code-block

case-item-list → pattern where-clause? | pattern where-clause? , case-item-list

catch pattern? where-clause? code-block
This separation makes the clause harder to associate with the pattern, can confuse users as to whether it modifies the expression or the pattern, and represents an inconsistency in Swift's grammar. This proposal regularizes the grammar to match other uses.

Note where clauses in case conditions and optional bindings have been removed in SE-0099.

Though, in the 'catch' and 'case' scenarios, as you read the pattern you already know what the pattern is about making the 'where' clause easy to understand. In the case of the 'for' you have no clue of what the pattern is about until you reach the 'in', so it is only once you have read the 'in' expression that you can really understand impact of the 'where'-clause. So even though the 'for' do not have the same 'pattern where-clause' syntax as the others, the 'where' is probably where it belong for the 'for'

Dany

···

Le 9 juin 2016 à 15:05, Erica Sadun via swift-evolution <swift-evolution@swift.org> a écrit :
Detailed Design

Current:

for case? pattern in expression where-clause? code-block
Proposed:

for case? pattern where-clause? in expression code-block
Impact on Existing Code

Migration should be easily addressed with a simple fix-it.

Alternatives Considered

Not accepting this proposal

On Jun 8, 2016, at 9:23 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

This reads to me as “repeat the following block until this fails to be true”, the conditional binding in this case fails to be true if someCondition(value) isn’t true, so the loop ends. I think the key thing here is that the where clause is for the conditional binding and not the loop itself, so in this respect it behaves exactly like an if or guard statement. Meanwhile:

  for eachValue in theValues where someCondition(eachValue) { … }

Reads as “for everything in theValues do the following if someCondition(eachValue) is also true”, in other words this loop always tries to visit every element of the sequence (a while loop has no implicit awareness of the sequence, it’s really just an if statement that runs over and over). In this case the where clause is part of the loop itself. There may be an argument that where should be renamed on for loops to better distinguish this, but once you consider that there’s no pattern or conditional binding here I think it makes a reasonable amount of sense.

The original sin here was in connecting the `where` clause to the for loop's sequence expression, rather than its pattern. If `where` were positioned right after the loop variable:

  for eachValue where someCondition(eachValue) in theValues { … }

It would be much clearer that `where` constrains the values seen by the loop body.

I'm not sure why the `where` clause was placed where it is. I suspect it has something to do with the `where` clause potentially being more complex than the sequence expression, but I was not in the room where it happened, so that's idle speculation.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Unlike in switch statements and do loops, a for-in loop's where-clause is separated from the pattern it modifies.

(I think "do loops" is supposed to be "do-catch statements"?)

for case? pattern in expression where-clause? code-block

case-item-list → pattern where-clause? | pattern where-clause? , case-item-list

catch pattern? where-clause? code-block

This separation makes the clause harder to associate with the pattern, can confuse users as to whether it modifies the expression or the pattern, and represents an inconsistency in Swift's grammar. This proposal regularizes the grammar to match other uses.

I'm definitely in favor of this. (I should be—I'm listed as coauthor.)

While I've never struggled with the `where` clause—I always assumed it was a filter—it never read right to me. This way does. When I say it out loud, "for x where x less than 10 in numbers" simply seems *far* easier to understand than "for x in numbers where x less than 10". There's something about the way the "where" combines with "for" that clarifies the entire statement.

I also think this better matches the grammar of the `case` statements in a `switch`. Erica quotes the formal grammar above, but you can actually see this in running code: in a `switch` statement, a compound case with a `where` clause like:

  case .foo, .bar where baz():

Only applies the `where` clause to the last pattern (.bar, but not .foo). That's because the rule is that the `where` belongs to the *pattern*, not the entire statement.

As a question of the proposal's drafting—as opposed to the feature being proposed—I do think that we should include an example of code before and after the change. The isOdd example ought to do.

Note where clauses in case conditions and optional bindings have been removed in SE-0099.

I think there's actually a case to be made (no pun intended) for bringing `where` back in case conditions, but with an analogous movement of the clause's position. In other words, where (post-SE-0099) we have this production:

  case-condition → "case" pattern initializer

We would change it to:

  case-condition → "case" pattern where-clause? initializer

In use, this would look like:

  if case .some(let Point.cartesian(x, y)) where x < y = json["rect"]?["origin"].flatMap(.init(rawValue:)) { … }

Of course, the above could equally be written without a `where` clause:

  if case .some(let Point.cartesian(x, y)) = json["rect"]?["origin"].flatMap(.init(rawValue:)), x < y { … }

But nevertheless, I think it's a good idea. Why? Two reasons:

1. Consistency. If this proposal is accepted, all other `case` statements will be able to take a `where` clause in the exact same position.

2. Expressiveness. In its new position, the `where` clause is actually in the middle—not at the end—of the case condition. This makes its role much more clear: `where` in a case condition is for refining the pattern to reject things which can't quite be expressed purely as a pattern. With `where` in this position, you will not be tempted to use it for a truly unrelated condition, as you might if `where` were after the initializer.

You might be able to make an analogous argument for optional bindings, turning this:

  optional-binding-head → "let" pattern initializer

Into this:

  optional-binding-head → "let" pattern where-clause? initializer

With results like:

  if let x where x > 5 = optionalX { … }

I'm less convinced this is a good idea; there's no optional binding anywhere else in the language to be consistent with, the uses of a `where` clause are limited since an optional binding only captures one value anyway, and I don't think it makes much sense to complicate such a simple syntax.

···

--
Brent Royal-Gordon
Architechies

-1 makes no sense to me...

···

On Jun 9, 2016, at 9:05 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Gist: where.md · GitHub

Regularizing Where grammar
Proposal: TBD
Author: Brent Royal-Gordon, Erica Sadun
Status: TBD
Review manager: TBD
Introduction

This proposal fixes an inconsistency for where clause grammar in Swift language for-in loops.

Swift Evolution Discussion: Add a while clause to for loops

Motivation

Unlike in switch statements and do loops, a for-in loop's where-clause is separated from the pattern it modifies.

for case? pattern in expression where-clause? code-block

case-item-list → pattern where-clause? | pattern where-clause? , case-item-list

catch pattern? where-clause? code-block
This separation makes the clause harder to associate with the pattern, can confuse users as to whether it modifies the expression or the pattern, and represents an inconsistency in Swift's grammar. This proposal regularizes the grammar to match other uses.

Note where clauses in case conditions and optional bindings have been removed in SE-0099.

Detailed Design

Current:

for case? pattern in expression where-clause? code-block
Proposed:

for case? pattern where-clause? in expression code-block
Impact on Existing Code

Migration should be easily addressed with a simple fix-it.

Alternatives Considered

Not accepting this proposal

On Jun 8, 2016, at 9:23 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

This reads to me as “repeat the following block until this fails to be true”, the conditional binding in this case fails to be true if someCondition(value) isn’t true, so the loop ends. I think the key thing here is that the where clause is for the conditional binding and not the loop itself, so in this respect it behaves exactly like an if or guard statement. Meanwhile:

  for eachValue in theValues where someCondition(eachValue) { … }

Reads as “for everything in theValues do the following if someCondition(eachValue) is also true”, in other words this loop always tries to visit every element of the sequence (a while loop has no implicit awareness of the sequence, it’s really just an if statement that runs over and over). In this case the where clause is part of the loop itself. There may be an argument that where should be renamed on for loops to better distinguish this, but once you consider that there’s no pattern or conditional binding here I think it makes a reasonable amount of sense.

The original sin here was in connecting the `where` clause to the for loop's sequence expression, rather than its pattern. If `where` were positioned right after the loop variable:

  for eachValue where someCondition(eachValue) in theValues { … }

It would be much clearer that `where` constrains the values seen by the loop body.

I'm not sure why the `where` clause was placed where it is. I suspect it has something to do with the `where` clause potentially being more complex than the sequence expression, but I was not in the room where it happened, so that's idle speculation.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

> 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 6:05 PM, Tim Vermeulen <tvermeulen@me.com> wrote:

> On Wed, Jun 8, 2016 at 1:58 PM, Tim Vermeulen<tvermeulen@me.com(mailto: > tvermeulen@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@swift.org(mailto:swift-evolution@swift.org)(mailto: > swift-evolution@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@me.com > (mailto:tvermeulen@me.com)(mailto:tvermeulen@me.com) > > > >>>><mailto:tvermeulen@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@ericasadun.com > (mailto:erica@ericasadun.com)(mailto:erica@ericasadun.com) > > > >>>><mailto:erica@ericasadun.com>(mailto:erica@ericasadun.com > > > >>>><mailto:erica@ericasadun.com>)>wrote:
> > >>>>>>
> > >>>>>>>On Jun 7, 2016, at 1:16 PM, Tim Vermeulen via swift-evolution<
swift-evolution@swift.org(mailto:swift-evolution@swift.org)(mailto:
swift-evolution@swift.org)
> > >>>><mailto:swift-evolution@swift.org>(mailto:
swift-evolution@swift.org
> > >>>><mailto:swift-evolution@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@swift.org(mailto:swift-evolution@swift.org
)(mailto:swift-evolution@swift.org)
> > >>>>https://lists.swift.org/mailman/listinfo/swift-evolution
> > >>>
> > >>>
> > >>>
> > >>_______________________________________________
> > >>swift-evolution mailing list
> > >>swift-evolution@swift.org(mailto:swift-evolution@swift.org)(mailto:
swift-evolution@swift.org)
> > >>https://lists.swift.org/mailman/listinfo/swift-evolution
> > >
> > >
> > >
>
>
>

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.

I do not think shortness is that much a valid criteria (sorry), but expressiveness of the intention matter. A 'guard' send a message of a condition to be protected from; 'where' carry a notion of conditional criteria; while 'while' suggest a on going state.

Having the choice between if/guard/where/while can be seen as confusing to newbie (which one should I use, why does this code use one over the other), but for an experimented developer it can help carry the intention behind the code better than a comment could.

Dany

···

Le 8 juin 2016 à 19:05, Tim Vermeulen via swift-evolution <swift-evolution@swift.org> a écrit :

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.

On Wed, Jun 8, 2016 at 1:58 PM, Tim Vermeulen<tvermeulen@me.com(mailto:tvermeulen@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@swift.org(mailto:swift-evolution@swift.org)(mailto:swift-evolution@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@me.com(mailto:tvermeulen@me.com)(mailto:tvermeulen@me.com) >>>>>>> <mailto:tvermeulen@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@ericasadun.com(mailto:erica@ericasadun.com)(mailto:erica@ericasadun.com) >>>>>>> <mailto:erica@ericasadun.com>(mailto:erica@ericasadun.com >>>>>>> <mailto:erica@ericasadun.com>)>wrote:

On Jun 7, 2016, at 1:16 PM, Tim Vermeulen via swift-evolution<swift-evolution@swift.org(mailto:swift-evolution@swift.org)(mailto:swift-evolution@swift.org) >>>>>>> <mailto:swift-evolution@swift.org>(mailto:swift-evolution@swift.org >>>>>>> <mailto:swift-evolution@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@swift.org(mailto:swift-evolution@swift.org)(mailto:swift-evolution@swift.org)
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org(mailto:swift-evolution@swift.org)(mailto:swift-evolution@swift.org)
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I’m not sure I follow, how would the two be different?

···

There might be value in entertaining the idea of unifying constructs such that they all allow arbitrary combinations of for/while/where/etc.

Such as:

while !stopped for unit in workToDo where unit.passesTest(condition) { unit.process() }

Which would mean something different than:

for unit in workToDo while !stopped where unit.passesTest(condition) { unit.process() }

And yes, they are very similar visually but differ subtly in meaning, but the same can be said about different sentences in english that might all share the same words and differ only by their order. It’s not exactly a foreign concept! I don’t know of any languages that are quite so expressive as that might be. Are there advantages to something more outlandish like this? I don’t know. I’m not proposing it directly, just thinking out loud, I guess.

l8r
Sean

> On Jun 8, 2016, at 4:44 PM, Haravikk via swift-evolution<swift-evolution@swift.org>wrote:
>
>
> > On 8 Jun 2016, at 17:11, Xiaodi Wu<xiaodi.wu@gmail.com>wrote:
> > > On Wed, Jun 8, 2016 at 3:38 AM, Haravikk<swift-evolution@haravikk.me>wrote:
> > > Yes this could be handled by an if/guard statement with continue, and while as proposed here could be done with the same plus a break, but these things come up so often that it just makes a lot of sense to get it all neatly onto one line.
> >
> > As I pointed out above with Tim's example, putting it all on one line is absolutely not 'neat'--it reads like spaghetti. That is one major beef I have with this proposal: that it *encourages* writing on one line too many things that, whether you use `where` or not, are much more clearly written on multiple lines. If writing everything on one line is for you the major advantage of this proposal, we could agree on everything else and I would be very much opposed to this proposal on that basis alone.
>
> I’m not proposing that every single loop have all of its conditions crushed onto one line, just like I wasn’t when discussing where on the condition clause thread. The usefulness of where and the proposed while is in the common, simple cases, for example:
>
> for eachValue in theValues while eachValue<100 where eachValue % 2 == 0 { … }
>
> The alternatives would be:
>
> for eachValue in theValues {
> guard eachValue<100 else { break }
> guard eachValue % 2 == 0 else { continue }
> …
> }
> for eachValue in theValues.prefix(while: { $0<100 }).filter({ $0 % 2 == 0 }) { … } // Could also be on multiple lines
>
> The former wastes vertical space for what it does IMO; it’s fine if the conditions were more complicated, but since they’re not where/while is ideal. The second isn’t terrible, but it’s a pretty noisy way to handle common loop conditions.
>
> The use of where/while isn’t about eliminating either of these alternatives, they’re absolutely useful in cases where their drawbacks become advantages. For example the inline guards are great when the conditions are more complex, and necessary if you want to do more than the simple cases allow. The second form is best when you need more than the two methods, alternate methods, or you have predicates you can pass in directly, although personally when I do this I tend to do the chinning on its own lines outside of the loop, leaving me with a loop of: for eachValue in theFilteredValues { … } or whatever.
>
> > Closures are--I'm sure you'd agree--a far more advanced concept than loops. Concepts like closing over a variable are very, very hard. Many useful things can be written without using closures. Not so many things could do without loops. It very much matters that a learner might feel that he or she cannot understand everything about a loop with the handwavy explanation that it'll "come later”.
>
> Not my point at all; my point was about the shorthand for closures not closure as a whole, you can’t learn the closure shorthands without first learning what a closure is. In exactly the same way where/while are just be shorthands for inline if/guard, you don’t need to learn about these clauses to make a functioning loop if you know how to do it with your if/guard statements. In fact it’s better to learn it in this order as once you know what each clause is a shorthand form of (if/guard continue or break) then you know exactly what it does already.
>
>
> Ignoring for a moment that you’re opposed to the where clause in general, what would your thoughts be on only permitting one of where/while in a for? i.e- you would be able to do only one of:
>
> for eachValue in theValues where eachValue % 2 == 0 { … }
> for eachValue in theValues while eachValue<100 { … }
>
> But not have both a where and a while on the same line. This eliminates the question mark around the order they are applied in, while still giving us the ability to essentially switch the behaviour of the where from continue to break. I’m not decided whether I want both in a single statement or if I just want to be able to choose between them. It also limits how much goes on one line as you have to use an inline condition to achieve both for a single loop.
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

I think the idea here is for a change from the first to the second of:

  for eachValue in theValues where eachValue.isOdd { … }
  for eachValue where eachValue.isOdd in theValues { … }

I’m kind of split on this for a few reasons. The first is that it doesn’t ready quite as well plain like this, however I find it looks a bit better like:

  for (eachValue where eachValue.isOdd) in theValues { … }

Just to clarify that what we’re looking for in theValues is “eachValue where eachValue.isOdd”, though I could probably learn to read it like this without parenthesis. That said, parenthesis lines up nicely with assignment of tuples like:

  for (eachKey, eachValue where eachValue > 5) in theKeyValuePairs { … }

But I’m not as sure how to adapt it to the pattern matching variation:

  for (case .Some(let value) where value > 5) in theValues { … }

It may be harder to declare with parenthesis support like this, I’m not sure. I think whether or not they’re required in order to declare the where clause it may be worth considering allowing parenthesis for slightly more complex cases where it will help to visually group these parts.

I’m actually curious whether moving the where clause closer could be a good (probably future) opportunity to borrow from the closure shorthand:

  for (eachKey, eachValue where $1 > 5) in theKeyValuePairs { … }

i.e- we allow the same shorthand variable names to avoid having to reuse eachKey/eachValue in the condition. Not something that needs to be added to the proposal now, but something it could make possible which may not be as good an idea with the current positioning, so is worth considering. I raise this especially because it would likely work best with support for parenthesis to make it completely clear what the shorthand belongs to, it’s also the kind of possible improvement that would interest me more vs just keeping it as-is.

Given that today’s been a bit of a whirlwind for the where clause I’m not sure where to fall on this yet. I’m definitely more in favour of this compared to removing the where clause entirely, but I still quite like it as it is now. Just wanted to lend some thoughts for the time being.

···

On 9 Jun 2016, at 20:25, Brandon Knope via swift-evolution <swift-evolution@swift.org> wrote:

Can you include an example? I find it hard to visualize for case? pattern where-clause? in expression code-block

Thanks,
Brandon

On Jun 9, 2016, at 3:05 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Gist: where.md · GitHub

Regularizing Where grammar

Proposal: TBD
Author: Brent Royal-Gordon <https://github.com/brentdax&gt;, Erica Sadun <http://github.com/erica&gt;
Status: TBD
Review manager: TBD
<where.md · GitHub

This proposal fixes an inconsistency for where clause grammar in Swift language for-in loops.

Swift Evolution Discussion: Add a while clause to for loops <http://thread.gmane.org/gmane.comp.lang.swift.evolution/19772/focus=20143&gt;
<where.md · GitHub

Unlike in switch statements and do loops, a for-in loop's where-clause is separated from the pattern it modifies.

for case? pattern in expression where-clause? code-block

case-item-list → pattern where-clause? | pattern where-clause? , case-item-list

catch pattern? where-clause? code-block
This separation makes the clause harder to associate with the pattern, can confuse users as to whether it modifies the expression or the pattern, and represents an inconsistency in Swift's grammar. This proposal regularizes the grammar to match other uses.

Note where clauses in case conditions and optional bindings have been removed in SE-0099 <https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md&gt;\.

<where.md · GitHub Design

Current:

for case? pattern in expression where-clause? code-block
Proposed:

for case? pattern where-clause? in expression code-block
<where.md · GitHub on Existing Code

Migration should be easily addressed with a simple fix-it.

<where.md · GitHub Considered

Not accepting this proposal

On Jun 8, 2016, at 9:23 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This reads to me as “repeat the following block until this fails to be true”, the conditional binding in this case fails to be true if someCondition(value) isn’t true, so the loop ends. I think the key thing here is that the where clause is for the conditional binding and not the loop itself, so in this respect it behaves exactly like an if or guard statement. Meanwhile:

  for eachValue in theValues where someCondition(eachValue) { … }

Reads as “for everything in theValues do the following if someCondition(eachValue) is also true”, in other words this loop always tries to visit every element of the sequence (a while loop has no implicit awareness of the sequence, it’s really just an if statement that runs over and over). In this case the where clause is part of the loop itself. There may be an argument that where should be renamed on for loops to better distinguish this, but once you consider that there’s no pattern or conditional binding here I think it makes a reasonable amount of sense.

The original sin here was in connecting the `where` clause to the for loop's sequence expression, rather than its pattern. If `where` were positioned right after the loop variable:

  for eachValue where someCondition(eachValue) in theValues { … }

It would be much clearer that `where` constrains the values seen by the loop body.

I'm not sure why the `where` clause was placed where it is. I suspect it has something to do with the `where` clause potentially being more complex than the sequence expression, but I was not in the room where it happened, so that's idle speculation.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

> Unlike in switch statements and do loops, a for-in loop's where-clause
is separated from the pattern it modifies.

(I think "do loops" is supposed to be "do-catch statements"?)

> for case? pattern in expression where-clause? code-block
>
> case-item-list → pattern where-clause? | pattern where-clause? ,
case-item-list
>
> catch pattern? where-clause? code-block
>
> This separation makes the clause harder to associate with the pattern,
can confuse users as to whether it modifies the expression or the pattern,
and represents an inconsistency in Swift's grammar. This proposal
regularizes the grammar to match other uses.

I'm definitely in favor of this. (I should be—I'm listed as coauthor.)

While I've never struggled with the `where` clause—I always assumed it was
a filter—it never read right to me. This way does. When I say it out loud,
"for x where x less than 10 in numbers" simply seems *far* easier to
understand than "for x in numbers where x less than 10". There's something
about the way the "where" combines with "for" that clarifies the entire
statement.

I also think this better matches the grammar of the `case` statements in a
`switch`. Erica quotes the formal grammar above, but you can actually see
this in running code: in a `switch` statement, a compound case with a
`where` clause like:

        case .foo, .bar where baz():

Only applies the `where` clause to the last pattern (.bar, but not .foo).
That's because the rule is that the `where` belongs to the *pattern*, not
the entire statement.

As a question of the proposal's drafting—as opposed to the feature being
proposed—I do think that we should include an example of code before and
after the change. The isOdd example ought to do.

> Note where clauses in case conditions and optional bindings have been
removed in SE-0099.

I think there's actually a case to be made (no pun intended) for bringing
`where` back in case conditions, but with an analogous movement of the
clause's position. In other words, where (post-SE-0099) we have this
production:

        case-condition → "case" pattern initializer

We would change it to:

        case-condition → "case" pattern where-clause? initializer

In use, this would look like:

        if case .some(let Point.cartesian(x, y)) where x < y =
json["rect"]?["origin"].flatMap(.init(rawValue:)) { … }

I'm concerned here about the `x < y = json...` part of this, if not for the
parser then for the human reader. I like the thought in principle though.

···

On Fri, Jun 10, 2016 at 2:10 PM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

Of course, the above could equally be written without a `where` clause:

        if case .some(let Point.cartesian(x, y)) =
json["rect"]?["origin"].flatMap(.init(rawValue:)), x < y { … }

But nevertheless, I think it's a good idea. Why? Two reasons:

1. Consistency. If this proposal is accepted, all other `case` statements
will be able to take a `where` clause in the exact same position.

2. Expressiveness. In its new position, the `where` clause is actually in
the middle—not at the end—of the case condition. This makes its role much
more clear: `where` in a case condition is for refining the pattern to
reject things which can't quite be expressed purely as a pattern. With
`where` in this position, you will not be tempted to use it for a truly
unrelated condition, as you might if `where` were after the initializer.

You might be able to make an analogous argument for optional bindings,
turning this:

        optional-binding-head → "let" pattern initializer

Into this:

        optional-binding-head → "let" pattern where-clause? initializer

With results like:

        if let x where x > 5 = optionalX { … }

I'm less convinced this is a good idea; there's no optional binding
anywhere else in the language to be consistent with, the uses of a `where`
clause are limited since an optional binding only captures one value
anyway, and I don't think it makes much sense to complicate such a simple
syntax.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

-1

if case .some(let Point.cartesian(x, y)) where x < y = json["rect"]?["origin"].flatMap(.init(rawValue:)) { … }

This: `where x < y = json[…]` is *very* unreadable and probably unparseable as it is missing a delimiter between the boolean expression and the expression behind the assignment operator. And I do not count the assignment operator as a sufficient delimiter.

I’ll give the following counterargument against this shuffling around of `where` or trying to eliminate it:

  if case .some(let x) = someExpression where x > 0

  for x in xs where x > 0

These are actually very consistent in their grammar when you look at it like follows:

  <target> <assignment op> <source> where <condition>

  if case .some(let x) = someExpression where x > 0

  target: `case .some(let x)`
  assignment op: `=`
  source: `someExpression`
  condition: `x > 0`

  for x in xs where x > 0

  target: `x`
  assignment op: `in`
  source: `xs`
  condition: `x > 0`

That is I’m arguing the `where` grammar is already quite regular and should not be changed. And yes, I’d like to have `where` back in `case`. And `let`.

-Thorsten

···

Am 10.06.2016 um 21:10 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>:

They would differ in, what at least to me, seems pretty logical ways:

while !stopped for unit in workToDo where unit.passesTest(condition) { unit.process() }

Would be:

while !stopped {
  for unit in workToDo where unit.passesTest(condition) {
    unit.process()
  }
}

for unit in workToDo while !stopped where unit.passesTest(condition) { unit.process() }

Would be:

for unit in workToDo {
  guard !stopped else { break }
  If unit.passesTest(condition) { unit.process() }
}

This would likely read even better if "where" was woven into the for:

for unit where unit.passesTest(condition) in workToDo while !stopped { unit.process() }

Or possibly reformatted to something like:

for unit
  where unit.passesTest(condition)
  in workToDo
  while !stopped
{ unit.process() }

l8r
Sean

···

Sent from my iPad

On Jun 8, 2016, at 5:42 PM, Tim Vermeulen <tvermeulen@me.com> wrote:

I’m not sure I follow, how would the two be different?

There might be value in entertaining the idea of unifying constructs such that they all allow arbitrary combinations of for/while/where/etc.

Such as:

while !stopped for unit in workToDo where unit.passesTest(condition) { unit.process() }

Which would mean something different than:

for unit in workToDo while !stopped where unit.passesTest(condition) { unit.process() }

And yes, they are very similar visually but differ subtly in meaning, but the same can be said about different sentences in english that might all share the same words and differ only by their order. It’s not exactly a foreign concept! I don’t know of any languages that are quite so expressive as that might be. Are there advantages to something more outlandish like this? I don’t know. I’m not proposing it directly, just thinking out loud, I guess.

l8r
Sean

On Jun 8, 2016, at 4:44 PM, Haravikk via swift-evolution<swift-evolution@swift.org>wrote:

On 8 Jun 2016, at 17:11, Xiaodi Wu<xiaodi.wu@gmail.com>wrote:

On Wed, Jun 8, 2016 at 3:38 AM, Haravikk<swift-evolution@haravikk.me>wrote:
Yes this could be handled by an if/guard statement with continue, and while as proposed here could be done with the same plus a break, but these things come up so often that it just makes a lot of sense to get it all neatly onto one line.

As I pointed out above with Tim's example, putting it all on one line is absolutely not 'neat'--it reads like spaghetti. That is one major beef I have with this proposal: that it *encourages* writing on one line too many things that, whether you use `where` or not, are much more clearly written on multiple lines. If writing everything on one line is for you the major advantage of this proposal, we could agree on everything else and I would be very much opposed to this proposal on that basis alone.

I’m not proposing that every single loop have all of its conditions crushed onto one line, just like I wasn’t when discussing where on the condition clause thread. The usefulness of where and the proposed while is in the common, simple cases, for example:

for eachValue in theValues while eachValue<100 where eachValue % 2 == 0 { … }

The alternatives would be:

for eachValue in theValues {
guard eachValue<100 else { break }
guard eachValue % 2 == 0 else { continue }

}
for eachValue in theValues.prefix(while: { $0<100 }).filter({ $0 % 2 == 0 }) { … } // Could also be on multiple lines

The former wastes vertical space for what it does IMO; it’s fine if the conditions were more complicated, but since they’re not where/while is ideal. The second isn’t terrible, but it’s a pretty noisy way to handle common loop conditions.

The use of where/while isn’t about eliminating either of these alternatives, they’re absolutely useful in cases where their drawbacks become advantages. For example the inline guards are great when the conditions are more complex, and necessary if you want to do more than the simple cases allow. The second form is best when you need more than the two methods, alternate methods, or you have predicates you can pass in directly, although personally when I do this I tend to do the chinning on its own lines outside of the loop, leaving me with a loop of: for eachValue in theFilteredValues { … } or whatever.

Closures are--I'm sure you'd agree--a far more advanced concept than loops. Concepts like closing over a variable are very, very hard. Many useful things can be written without using closures. Not so many things could do without loops. It very much matters that a learner might feel that he or she cannot understand everything about a loop with the handwavy explanation that it'll "come later”.

Not my point at all; my point was about the shorthand for closures not closure as a whole, you can’t learn the closure shorthands without first learning what a closure is. In exactly the same way where/while are just be shorthands for inline if/guard, you don’t need to learn about these clauses to make a functioning loop if you know how to do it with your if/guard statements. In fact it’s better to learn it in this order as once you know what each clause is a shorthand form of (if/guard continue or break) then you know exactly what it does already.

Ignoring for a moment that you’re opposed to the where clause in general, what would your thoughts be on only permitting one of where/while in a for? i.e- you would be able to do only one of:

for eachValue in theValues where eachValue % 2 == 0 { … }
for eachValue in theValues while eachValue<100 { … }

But not have both a where and a while on the same line. This eliminates the question mark around the order they are applied in, while still giving us the ability to essentially switch the behaviour of the where from continue to break. I’m not decided whether I want both in a single statement or if I just want to be able to choose between them. It also limits how much goes on one line as you have to use an inline condition to achieve both for a single loop.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think the idea here is for a change from the first to the second of:

  for eachValue in theValues where eachValue.isOdd { … }
  for eachValue where eachValue.isOdd in theValues { … }

I’m kind of split on this for a few reasons. The first is that it doesn’t ready quite as well plain like this, however I find it looks a bit better like:

  for (eachValue where eachValue.isOdd) in theValues { … }

for eachValue where eachValue.isOdd in theValues { ... }
for case .Some(let value) where value > 5 in theValues { ... }

vs

for eachValue in theValues where eachValue.isOdd {...}
for case .Some(let value) in theValues where value > 5 { ... }

It should be parseable without parens.

Just to clarify that what we’re looking for in theValues is “eachValue where eachValue.isOdd”, though I could probably learn to read it like this without parenthesis. That said, parenthesis lines up nicely with assignment of tuples like:

  for (eachKey, eachValue where eachValue > 5) in theKeyValuePairs { … }

for (eachKey, eachValue) where eachValue > 5 in theKeyValuePairs {... }

The where clause is distinct from the pattern

-- E

···

On Jun 9, 2016, at 1:57 PM, Haravikk <swift-evolution@haravikk.me> wrote:

I believe this is for case conditionals in for loops only. There is another proposal to remove where from for loops I believe.

I am curious, is there any conflict with the reasoning to move where here compared to the accepted SE-0081 "Move where clause to end of declaration" [swift-evolution-announce] [Accepted] SE-0081: Move where clause to end of declaration

We moved the where clause to before the body in one case (SE-0081) and now we are trying to move the where clause from before the body to right next to the variable.

In SE-0081: "With the proposed change, where clauses do not impede the main declaration and are also more easily formattable"

I know these are different uses but it is beginning to hurt my head where all the where clauses are suppose to go in different contexts

Brandon

···

Sent from my iPad

On Jun 9, 2016, at 3:57 PM, Haravikk <swift-evolution@haravikk.me> wrote:

I think the idea here is for a change from the first to the second of:

  for eachValue in theValues where eachValue.isOdd { … }
  for eachValue where eachValue.isOdd in theValues { … }

I’m kind of split on this for a few reasons. The first is that it doesn’t ready quite as well plain like this, however I find it looks a bit better like:

  for (eachValue where eachValue.isOdd) in theValues { … }

Just to clarify that what we’re looking for in theValues is “eachValue where eachValue.isOdd”, though I could probably learn to read it like this without parenthesis. That said, parenthesis lines up nicely with assignment of tuples like:

  for (eachKey, eachValue where eachValue > 5) in theKeyValuePairs { … }

But I’m not as sure how to adapt it to the pattern matching variation:

  for (case .Some(let value) where value > 5) in theValues { … }

It may be harder to declare with parenthesis support like this, I’m not sure. I think whether or not they’re required in order to declare the where clause it may be worth considering allowing parenthesis for slightly more complex cases where it will help to visually group these parts.

I’m actually curious whether moving the where clause closer could be a good (probably future) opportunity to borrow from the closure shorthand:

  for (eachKey, eachValue where $1 > 5) in theKeyValuePairs { … }

i.e- we allow the same shorthand variable names to avoid having to reuse eachKey/eachValue in the condition. Not something that needs to be added to the proposal now, but something it could make possible which may not be as good an idea with the current positioning, so is worth considering. I raise this especially because it would likely work best with support for parenthesis to make it completely clear what the shorthand belongs to, it’s also the kind of possible improvement that would interest me more vs just keeping it as-is.

Given that today’s been a bit of a whirlwind for the where clause I’m not sure where to fall on this yet. I’m definitely more in favour of this compared to removing the where clause entirely, but I still quite like it as it is now. Just wanted to lend some thoughts for the time being.

On 9 Jun 2016, at 20:25, Brandon Knope via swift-evolution <swift-evolution@swift.org> wrote:

Can you include an example? I find it hard to visualize for case? pattern where-clause? in expression code-block

Thanks,
Brandon

On Jun 9, 2016, at 3:05 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Gist: where.md · GitHub

Regularizing Where grammar
Proposal: TBD
Author: Brent Royal-Gordon, Erica Sadun
Status: TBD
Review manager: TBD
Introduction

This proposal fixes an inconsistency for where clause grammar in Swift language for-in loops.

Swift Evolution Discussion: Add a while clause to for loops

Motivation

Unlike in switch statements and do loops, a for-in loop's where-clause is separated from the pattern it modifies.

for case? pattern in expression where-clause? code-block

case-item-list → pattern where-clause? | pattern where-clause? , case-item-list

catch pattern? where-clause? code-block
This separation makes the clause harder to associate with the pattern, can confuse users as to whether it modifies the expression or the pattern, and represents an inconsistency in Swift's grammar. This proposal regularizes the grammar to match other uses.

Note where clauses in case conditions and optional bindings have been removed in SE-0099.

Detailed Design

Current:

for case? pattern in expression where-clause? code-block
Proposed:

for case? pattern where-clause? in expression code-block
Impact on Existing Code

Migration should be easily addressed with a simple fix-it.

Alternatives Considered

Not accepting this proposal

On Jun 8, 2016, at 9:23 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

This reads to me as “repeat the following block until this fails to be true”, the conditional binding in this case fails to be true if someCondition(value) isn’t true, so the loop ends. I think the key thing here is that the where clause is for the conditional binding and not the loop itself, so in this respect it behaves exactly like an if or guard statement. Meanwhile:

  for eachValue in theValues where someCondition(eachValue) { … }

Reads as “for everything in theValues do the following if someCondition(eachValue) is also true”, in other words this loop always tries to visit every element of the sequence (a while loop has no implicit awareness of the sequence, it’s really just an if statement that runs over and over). In this case the where clause is part of the loop itself. There may be an argument that where should be renamed on for loops to better distinguish this, but once you consider that there’s no pattern or conditional binding here I think it makes a reasonable amount of sense.

The original sin here was in connecting the `where` clause to the for loop's sequence expression, rather than its pattern. If `where` were positioned right after the loop variable:

  for eachValue where someCondition(eachValue) in theValues { … }

It would be much clearer that `where` constrains the values seen by the loop body.

I'm not sure why the `where` clause was placed where it is. I suspect it has something to do with the `where` clause potentially being more complex than the sequence expression, but I was not in the room where it happened, so that's idle speculation.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Def like this change, but Erica I'm wondering what changed your mind about
the syntax since the last I read you still were concerned by the ambiguity
between filtering and exiting.

···

On Thu, Jun 9, 2016 at 1:54 PM Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

On Jun 9, 2016, at 1:57 PM, Haravikk <swift-evolution@haravikk.me> wrote:

I think the idea here is for a change from the first to the second of:

for eachValue in theValues where eachValue.isOdd { … }
for eachValue where eachValue.isOdd in theValues { … }

I’m kind of split on this for a few reasons. The first is that it doesn’t
ready quite as well plain like this, however I find it looks a bit better
like:

for (eachValue where eachValue.isOdd) in theValues { … }

for eachValue where eachValue.isOdd in theValues { ... }
for case .Some(let value) where value > 5 in theValues { ... }

vs

for eachValue in theValues where eachValue.isOdd {...}
for case .Some(let value) in theValues where value > 5 { ... }

It should be parseable without parens.

Just to clarify that what we’re looking for in theValues is “eachValue
where eachValue.isOdd”, though I could probably learn to read it like this
without parenthesis. That said, parenthesis lines up nicely with assignment
of tuples like:

for (eachKey, eachValue where eachValue > 5) in theKeyValuePairs { … }

for (eachKey, eachValue) where eachValue > 5 in theKeyValuePairs {... }

The where clause is distinct from the pattern

-- E

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I am curious, is there any conflict with the reasoning to move where here compared to the accepted SE-0081 "Move where clause to end of declaration" [swift-evolution-announce] [Accepted] SE-0081: Move where clause to end of declaration

We moved the where clause to before the body in one case (SE-0081) and now we are trying to move the where clause from before the body to right next to the variable.

In SE-0081: "With the proposed change, where clauses do not impede the main declaration and are also more easily formattable"

I know these are different uses but it is beginning to hurt my head where all the where clauses are suppose to go in different contexts

I understand why this would confuse you, but there are practical reasons for them to be different.

The `where` clause is used in a couple different ways:

* In a declaration, it specifies additional type information—for instance, that an associated type conforms to a certain protocol or matches another associated type on a different type parameter.

  func append<C: Collection>(contentsOf other: C) where C.Iterator.Element == Self.Iterator.Element { … }

* In a `case` statement or other pattern, it specifies a Boolean condition which must be met for the pattern to match.

  switch value {
  case .result(let character) where emoji.contains(character):
    print("Hello, millennial!")
  case .result(_):
    print("Hello, fogey!")
  default:
    print("Goodbye, world!")
  }

Both versions of the clause refine a match to specify additional conditions; that's why they use the same keyword. But they're actually quite different.

In particular, one big difference between the two is that you can only attach one `where` clause to a declaration, but there might be many `where` clauses in a pattern match. For instance, in this example:

  enum X { case a, b }
  switch X.a {
  case .a, .b where 1 == 0:
    print("Matched")
  default:
    print("Didn't")
  }

The `where` clause only applies to the `.b`, not the `.a`, so the code will print "Matched". To make it print "Didn't", you'd have to write `case .a where 1 == 0, .b where 1 == 0`. In other words, the `where` clause is part of the specific pattern, not the `case` statement as a whole, and so it *must* be attached to the specific pattern.

The `for` syntax, however, does not respect this rule. It inserts the `in` clause between the pattern (the thing after the `for` keyword is a pattern too, even if it doesn't have a `case` keyword) and its `where` clause. That's not consistent with `switch`-`case`'s syntax, and in practice, I find it leads to confusion about what `where` does.

For a declaration, however, there can only be one `where` clause, and you can equally well view it as applying to only the generic parameter list (the old way) or to the entire declaration (the new way). There are several reasons why it's very convenient to put it at the end:

1. Declaration `where` clauses tend to involve lots of verbose names and be very long.
2. Declaration `where` clauses sometimes need to be attached to declarations with no generic parameter list, like `extension` and hopefully soon `associatedtype`.
3. Declaration `where` clauses embedded in a generic parameter list end up wedging a bunch of usually unimportant information between two of the most important parts of the declaration: the list of generic parameters and the list of regular parameters. This ends up turning the whole thing into a jumbled mess.

So even though both kinds of `where` clause do broadly similar things, they face very different constraints and pressures, and thus are positioned a little bit differently. That's unfortunate, but probably unavoidable.

The proposal being discussed in this thread is actually trying to improve this situation by making pattern `where` clauses in `for` loops match pattern `where` clauses in `switch` statements. In other words, I believe it actually improves consistency over the status quo. So if you're confused about where you put `where`, this should make it a little easier to keep things straight.

···

--
Brent Royal-Gordon
Architechies

-1

if case .some(let Point.cartesian(x, y)) where x < y =
json["rect"]?["origin"].flatMap(.init(rawValue:)) { … }

This: `where x < y = json[…]` is *very* unreadable and probably
unparseable as it is missing a delimiter between the boolean expression and
the expression behind the assignment operator. And I do not count the
assignment operator as a sufficient delimiter.

Agreed. Unless this readability issue can be solved, I'm concerned about
this particular reshuffling.

I’ll give the following counterargument against this shuffling around of
`where` or trying to eliminate it:

if case .some(let x) = someExpression where x > 0

for x in xs where x > 0

These are actually very consistent in their grammar when you look at it
like follows:

<target> <assignment op> <source> where <condition>

if case .some(let x) = someExpression where x > 0

target: `case .some(let x)`
assignment op: `=`
source: `someExpression`
condition: `x > 0`

for x in xs where x > 0

target: `x`
assignment op: `in`
source: `xs`
condition: `x > 0`

That is I’m arguing the `where` grammar is already quite regular and
should not be changed. And yes, I’d like to have `where` back in `case`.
And `let`.

The inconsistency argument is that this grammar is inconsistent with usage
when switching over cases, etc., not that it's inconsistent with usage in
`if`.

Principally, though, the problem is that `where` leaves implied but
unanswered the question of "what happens where not?" For an `if` statement
the answer is inherent to the word "if," but because it is inherent, it
becomes redundant and can be replaced with a comma. Likewise `while`. For a
`for` loop, `where` cannot be replaced with a comma because it is not
implied by the word `for`; but because it is not redundant, it is also not
inherent in the meaning of the word "where."

···

On Fri, Jun 10, 2016 at 3:15 PM, Thorsten Seitz via swift-evolution < swift-evolution@swift.org> wrote:

Am 10.06.2016 um 21:10 schrieb Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org>:

-Thorsten

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

-1

if case .some(let Point.cartesian(x, y)) where x < y = json["rect"]?["origin"].flatMap(.init(rawValue:)) { … }

This: `where x < y = json[…]` is *very* unreadable and probably unparseable as it is missing a delimiter between the boolean expression and the expression behind the assignment operator. And I do not count the assignment operator as a sufficient delimiter.

Agreed. Unless this readability issue can be solved, I'm concerned about this particular reshuffling.

I’ll give the following counterargument against this shuffling around of `where` or trying to eliminate it:

  if case .some(let x) = someExpression where x > 0

  for x in xs where x > 0

These are actually very consistent in their grammar when you look at it like follows:

  <target> <assignment op> <source> where <condition>

  if case .some(let x) = someExpression where x > 0

  target: `case .some(let x)`
  assignment op: `=`
  source: `someExpression`
  condition: `x > 0`

  for x in xs where x > 0

  target: `x`
  assignment op: `in`
  source: `xs`
  condition: `x > 0`

That is I’m arguing the `where` grammar is already quite regular and should not be changed. And yes, I’d like to have `where` back in `case`. And `let`.

The inconsistency argument is that this grammar is inconsistent with usage when switching over cases, etc., not that it's inconsistent with usage in `if`.

For switching the whole <assignment op> <source> part is not needed because the <source> is the expression behind `switch`.
I don’t have a problem if that part vanishes from the middle of the grammar instead of from the end.

Principally, though, the problem is that `where` leaves implied but unanswered the question of "what happens where not?" For an `if` statement the answer is inherent to the word "if," but because it is inherent, it becomes redundant and can be replaced with a comma.

I do not agree with that replacement but I’m glad that at least we didn’t end up with semicolons (shudder)...

Likewise `while`. For a `for` loop, `where` cannot be replaced with a comma because it is not implied by the word `for`; but because it is not redundant, it is also not inherent in the meaning of the word "where.“

I don’t agree. `for x in xs where condition` is crystal clear to me. It is just like a mathematical set with a condition reads:

{ x ∈ xs | x > 0 } reads as "x in xs where x greater than zero“

So why do you claim the meaning of `where` is unclear in a `for` loop?

-Thorsten

···

Am 10.06.2016 um 22:26 schrieb Xiaodi Wu <xiaodi.wu@gmail.com>:
On Fri, Jun 10, 2016 at 3:15 PM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Am 10.06.2016 um 21:10 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

-1 I have similar concerns on the mental ambiguity of the direction this is going.

···

On 10 Jun 2016, at 22:15, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

-1

Am 10.06.2016 um 21:10 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>:

if case .some(let Point.cartesian(x, y)) where x < y = json["rect"]?["origin"].flatMap(.init(rawValue:)) { … }

This: `where x < y = json[…]` is *very* unreadable and probably unparseable as it is missing a delimiter between the boolean expression and the expression behind the assignment operator. And I do not count the assignment operator as a sufficient delimiter.

I’ll give the following counterargument against this shuffling around of `where` or trying to eliminate it:

  if case .some(let x) = someExpression where x > 0

  for x in xs where x > 0

These are actually very consistent in their grammar when you look at it like follows:

  <target> <assignment op> <source> where <condition>

  if case .some(let x) = someExpression where x > 0

  target: `case .some(let x)`
  assignment op: `=`
  source: `someExpression`
  condition: `x > 0`

  for x in xs where x > 0

  target: `x`
  assignment op: `in`
  source: `xs`
  condition: `x > 0`

That is I’m arguing the `where` grammar is already quite regular and should not be changed. And yes, I’d like to have `where` back in `case`. And `let`.

-Thorsten

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

-1

if case .some(let Point.cartesian(x, y)) where x < y = json["rect"]?["origin"].flatMap(.init(rawValue:)) { … }

This: `where x < y = json[…]` is *very* unreadable and probably unparseable as it is missing a delimiter between the boolean expression and the expression behind the assignment operator. And I do not count the assignment operator as a sufficient delimiter.

This was why I mentioned parenthesis earlier, this example could be formatted more easily if the where clause moved into the case like so:

  if case .some(let Point.cartesian(x, y) where x < y) = json[“rect”]?[“origin”].flatMap(.init(rawValue:)) { … }

Though it’s probably a good candidate for spreading onto multiple lines in the first place, but I think that moving the where clause into the parenthesis clarifies its usage a bit if we have to move the where clause.

I’ll give the following counterargument against this shuffling around of `where` or trying to eliminate it:

  if case .some(let x) = someExpression where x > 0

  for x in xs where x > 0

These are actually very consistent in their grammar when you look at it like follows:

  <target> <assignment op> <source> where <condition>

  if case .some(let x) = someExpression where x > 0

  target: `case .some(let x)`
  assignment op: `=`
  source: `someExpression`
  condition: `x > 0`

  for x in xs where x > 0

  target: `x`
  assignment op: `in`
  source: `xs`
  condition: `x > 0`

That is I’m arguing the `where` grammar is already quite regular and should not be changed. And yes, I’d like to have `where` back in `case`. And `let`.

Still, I’m inclined to agree with this. I find the where clause coming last pretty logical as I interpret it as reading like “complete this pattern match only if this condition is true”, or in the for loop “do the following only if this condition is true”, the latter could be clarified if we could write it as for x in xs where x > 0 do { … }, though that’s how I tend to read blocks to begin with (I’d just like to be able to use the do keyword to make it explicit).

The other thing I like about keeping the where clause at the end is that it keeps the fine detail to last, allowing the more significant action/intent to be described first. It’s tricky, there are merits to both options, but I’d say that trailing where is more consistent with the new trailing where on functions; this similarly lets you declare the most significant piece first, then fill in the details later.

···

On 10 Jun 2016, at 21:15, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Am 10.06.2016 um 21:10 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Def like this change, but Erica I'm wondering what changed your mind about the syntax since the last I read you still were concerned by the ambiguity between filtering and exiting.

My concerns remain. However, after going back and forth with Brent,
wux, and others, I think my concerns could be addressed in style guides
and linters[1]. This draft introduces a major consistency win, I get to rail
against actually using the feature when I publicly opine[2], but when
used it will be better. (I'm quite curious to see hear from someone on the
core team whether this change is practical and whether it improves parsing
or makes it harder from the compiler's point of view.)

In any case, I reserve the right to argue from several different points of
view[1, ibid] to see how well each suggestion works (and to hear the feedback
and opinions of others) before settling on anything. Until it's a pull request, it's
not fixed. And even then, I still want to listen to arguments.

-- Erica

[1] As the Italians say, "La donna è mobile <https://en.wikipedia.org/wiki/La_donna_è_mobile&gt;&quot;, which translates to "women are furniture." Ask an Italian.
[2] I find a `where`-less `for-in` loop with `guard` statements to read the most clearly and offer the most maintainable approach. My computation tests show that it is in the top efficiency group.
[3] Nulla nota 3.

···

On Jun 9, 2016, at 3:18 PM, Rob Norback <rnorback@gmail.com> wrote:

On Thu, Jun 9, 2016 at 1:54 PM Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jun 9, 2016, at 1:57 PM, Haravikk <swift-evolution@haravikk.me <mailto:swift-evolution@haravikk.me>> wrote:

I think the idea here is for a change from the first to the second of:

  for eachValue in theValues where eachValue.isOdd { … }
  for eachValue where eachValue.isOdd in theValues { … }

I’m kind of split on this for a few reasons. The first is that it doesn’t ready quite as well plain like this, however I find it looks a bit better like:

  for (eachValue where eachValue.isOdd) in theValues { … }

for eachValue where eachValue.isOdd in theValues { ... }
for case .Some(let value) where value > 5 in theValues { ... }

vs

for eachValue in theValues where eachValue.isOdd {...}
for case .Some(let value) in theValues where value > 5 { ... }

It should be parseable without parens.

Just to clarify that what we’re looking for in theValues is “eachValue where eachValue.isOdd”, though I could probably learn to read it like this without parenthesis. That said, parenthesis lines up nicely with assignment of tuples like:

  for (eachKey, eachValue where eachValue > 5) in theKeyValuePairs { … }

for (eachKey, eachValue) where eachValue > 5 in theKeyValuePairs {... }

The where clause is distinct from the pattern

-- E

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Def like this change, but Erica I'm wondering what changed your mind about the syntax since the last I read you still were concerned by the ambiguity between filtering and exiting.

My concerns remain. However, after going back and forth with Brent,
wux, and others, I think my concerns could be addressed in style guides
and linters[1]. This draft introduces a major consistency win, I get to rail
against actually using the feature when I publicly opine[2], but when
used it will be better. (I'm quite curious to see hear from someone on the
core team whether this change is practical and whether it improves parsing
or makes it harder from the compiler's point of view.)

In any case, I reserve the right to argue from several different points of
view[1, ibid] to see how well each suggestion works (and to hear the feedback
and opinions of others) before settling on anything. Until it's a pull request, it's
not fixed. And even then, I still want to listen to arguments.

-- Erica

[1] As the Italians say, "La donna è mobile", which translates to "women are furniture." Ask an Italian.

Poor Verdi... he wouldn't recognize his rigoletto in that 'new' light...

···

On Jun 9, 2016, at 11:37 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 9, 2016, at 3:18 PM, Rob Norback <rnorback@gmail.com> wrote:

[2] I find a `where`-less `for-in` loop with `guard` statements to read the most clearly and offer the most maintainable approach. My computation tests show that it is in the top efficiency group.
[3] Nulla nota 3.

On Thu, Jun 9, 2016 at 1:54 PM Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 9, 2016, at 1:57 PM, Haravikk <swift-evolution@haravikk.me> wrote:

I think the idea here is for a change from the first to the second of:

  for eachValue in theValues where eachValue.isOdd { … }
  for eachValue where eachValue.isOdd in theValues { … }

I’m kind of split on this for a few reasons. The first is that it doesn’t ready quite as well plain like this, however I find it looks a bit better like:

  for (eachValue where eachValue.isOdd) in theValues { … }

for eachValue where eachValue.isOdd in theValues { ... }
for case .Some(let value) where value > 5 in theValues { ... }

vs

for eachValue in theValues where eachValue.isOdd {...}
for case .Some(let value) in theValues where value > 5 { ... }

It should be parseable without parens.

Just to clarify that what we’re looking for in theValues is “eachValue where eachValue.isOdd”, though I could probably learn to read it like this without parenthesis. That said, parenthesis lines up nicely with assignment of tuples like:

  for (eachKey, eachValue where eachValue > 5) in theKeyValuePairs { … }

for (eachKey, eachValue) where eachValue > 5 in theKeyValuePairs {... }

The where clause is distinct from the pattern

-- E

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution