> 
> First, notice how your comment is related to the `where` clause but is 
> actually sitting on top of the loop itself. Second, one of these two tests 
> visits every element while the other doesn't, and it took me three reads 
> before I could see that because I literally couldn't find the `where` clause 
> the first two times I scanned through your code. This is a false 
> "parallelism," causing the very thing that *isn't* parallel to disappear from 
> the reader's eye. Compare instead this alternative, which also flips the 
> boolean assertion:
> 
> ```
> func testNaiveAncestor() {
>   for position in testPositions {
>     // the root has no ancestor
>     if position.isRootPosition { continue }
>     // (notice how I don't even have to comment that we're skipping root,
>     // because the code says it explicitly for me)
> 
>     XCTAssertLessThan(position.naiveAncestor(), position)
>   }
> }
> ```

Fair enough; I added the comment when posting here…the original didn’t have 
one, because the where clause also explicitly says we’re skipping that one. 

To me the parallelism I care about is the following paraphrase:

  for testItem in allRelevantPossibilities { someBasicPropertyHolds(testItem) }
  for testItem in allRelevantPossibilities { someOtherPropertyHolds(testItem) }

…and adding the logic to skip the irrelevant option into the definition of 
“allRelevantPossibilities” seems to do a better job preserving the structural 
similarity I care about than throwing in some ad-hoc looking imperative logic 
into the test logic itself (even though functionally both are equivalent).

> 
> Now, with my rewriting, the part of your test that is strictly similar to the 
> other test looks parallel, and the one part that isn't at all similar (the 
> skipping part) stands out explicitly and is now self-documenting code.
> 
> 
> So `where` here, IMHO, isn’t *clearly* clearer in `testNaiveAncestor()`, but 
> it lets `testNaiveAncestor()` and `testNaiveSuccessor()` (etc.) be 
> *systemically*-clearer, as it were.
> 
> Second Pattern: relatedly, I find code is much clearer when `guard` is 
> only-ever used for early exits and early returns. 
> 
> There’s no requirement to *not* use `guard` and `continue` together, but if 
> one is willing to stick to `guard` == “early exit / early return” it makes 
> code much easier to read and audit.
> 
> In a handful of places I have for loops that would need both `continue`-style 
> conditionals and also early-exit conditionals; having `where` means I can 
> stick to using `guard` for early-exits, whereas without it I’d have extra 
> nesting or mixed “early-exit” guard and “continue” guard. 
> 
> I don't know what to say here. The fact is that `guard` with `continue` is a 
> documented and advertised feature of Swift; it's not simply supported by 
> accident. Of course, you're free to choose not to use that feature at all. 
> And it is true that, currently, `where` allows you to avoid that feature at 
> the top of a `for` loop, but anywhere else inside the loop and you'll have to 
> deal with extra nesting if you choose to reject `guard` with `continue`.

The same is true of `where`, no? It’s a part of the language, documented, free 
to use or not to use when or if you find it helpful (or not).

In the same way you seem to have with `where` after a for-in — and would prefer 
to not have to check for it, etc. — I have a hard time following mixed 
break/continue/return usage, and prefer to not have to check the body of each 
guard clause’s `else` to figure out the control flow.

I can understand that you find that difficulty baffling; I find your 
difficulties `where` baffling, but can accept they are sincere.

> IIUC, one of the motivating reasons for introducing `guard` was to solve the 
> pyramid of doom problem. So you're rejecting the intended solution for extra 
> nesting, at least in certain circumstances, a choice you're obviously free to 
> make in your own code. But when it comes to designing a language for 
> everyone, the fact that some users reject the intended solution would be 
> grounds for re-examining that solution (i.e. `guard`), but the mantra here 
> has always been one solution where possible and not multiple. So this 
> certainly cannot be a justification for another feature (i.e. `where`) which 
> only incidentally provides a substitute solution in certain situations.

Personally I found this use of `where` completely unremarkable and something I 
never thought about until I saw this discussion gaining momentum; I was already 
used to similar constructs so it just seemed useful in a familiar and 
thoroughly-uninteresting way.

That said, I could get behind excising it if there was serious interest in 
eventually building-in something like LINQ or comprehensions (etc.); `where` 
after a for-in is at this point a bit of an odd duckling.

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to