-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