> 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
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution