-1

An important goal in Swift is “clarity at the point of use,” it appears in the 
API Design Guidelines as one of the fundamentals, but also pervades the design 
of the Swift language itself.

I think that removing the `where` clause from Swift’s `for` loops will reduce 
clarity of intent for many programmers.

It may be that `for`/`where` and `for`/`guard`/`continue` express the same 
result for the compiler, but an identical compiled output does not necessarily 
equate to an identical intent on behalf of the programmer. I do not believe 
that it has ever been a Swift goal that “there is only way one way to do it.”

Please consider the following example code:

  for line in lines where !line.isEmpty() {
    …
  }

The intent of this code is clear to the reader. A set of lines from a line is 
being iterated, and filtered such that the code within the loop is only 
operating on those that are not empty. This is a fairly common pattern when 
working with files, for example. It may be that the code within the loop would 
work perfectly well even in the case of empty lines, the programmer simply does 
not wish to consider them.

Filtered iteration is a common pattern throughout programming, other uses 
include things like iterating the set of “updated objects” in Core Data, etc.

Now consider the equivalent avoiding a `where` clause, and replacing it with 
`guard`:

  for line in lines {
    guard !line.isEmpty() else { continue }
    …
  }

To the compiler it is the same code, but to the programmer this may have a very 
different intent.

`guard` in Swift is _not_ simply a generic `unless` statement, the name was 
chosen very specifically and less harsh names rejected. `guard` belongs closer 
to the family of `assert` and `precondition` than it does to `if`. `guard` is 
used to provide an expression of pattern that *must* be true for the code 
following it to operate without error or other unintended side-effects. This is 
why the `else` block must, in some way, exit the containing scope of the 
`guard`.

In first example the code makes it clear, to me, that it is normal for the set 
of lines to contain non-empty, and empty lines. But in the second example, to 
me, the code makes it clear that non-empty lines are not expected in the set, 
and would cause the code to error if they were present; the code guards against 
this by stepping over them—following Postel’s Law.

My opinion would be that the correct way of preserving the intent of the first 
example would be either:

  for line in lines {
    if !line.isEmpty() {
      …
    }
  }

or:

  for line in lines.lazy.filter({ !$0.isEmpty() }) {
    …
  }

The second case is particularly troubling here because it’s “hard to get right” 
from a performance point of view. The clarity of `for`/`where` wins over all of 
these.

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

Reply via email to