RonAvitzur
(Ron Avitzur)
1
What am I failing to understand that allows
guard samples.contains(where: { $0.isInfinite }) else { return }
and
guard (samples.contains{ $0.isInfinite }) else { return }
but not
guard samples.contains{ $0.isInfinite } else { return }
The last gives
Anonymous closure argument not contained in a closure
Cannot convert value of type '((Sample) throws -> Bool) throws -> Bool' to expected condition type 'Bool'
Closure expression is unused
Consecutive statements on a line must be separated by ';'
Expected 'else' after 'guard' condition
1 Like
Jon_Shier
(Jon Shier)
2
Swift doesn't support trailing closure syntax in guards or ifs unless you wrap it somehow, either by not using trailing closure syntax at all or wrapping the expression in parens, as you did in the second example.
3 Likes
if does better. (It's even a warning, not an error). Should it?
Trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning
if samples.contains { $0.isInfinite } { } else { return }
Is the message even to be believed? This has always been necessary as far as I remember, but I don't know why it would be problematic.
Jon_Shier
(Jon Shier)
4
As that warning alludes to, it can be tough for a human to read. Similarly, it can be hard for a compiler to parse, since allowing trailing closures would also allow multiple trailing closures, leading to difficulty distinguishing multiple levels of braces.
if multipleClosures { } second: { } third: {}, secondClosure { } { }
It may be possible to support it (I know nothing about the parser), but it would be difficult to justify spending the time.
1 Like
This difference that Jessy raises between guard and if is, IMO, confusing. This difference also was confusing in ViewBuilders.
1 Like
rintaro
(Rintaro Ishizaki)
6
This is a bug. This should be diagnosed as a warning just like if case:
test.swift:9:23: warning: trailing closure in this context is confusable with the body of the statement; pass as a parenthesized argument to silence this warning
if samples.contains { $0.isInfinite } { return }
~^
(where: )
@RonAvitzur Could you file a bug to https://bugs.swift.org ?
RonAvitzur
(Ron Avitzur)
7
Sure. However, it is slightly different. While guard is using the same condition grammar that if uses, which is why the trailing closure is prohibited here, since guard has no then clause the trailing closure is not actually ambiguous, so it either requires a slightly different diagnostic, or a relaxation of the grammar to allow the trailing closure in a guard condition.
2 Likes
As shown above, guards look worse with the extra parentheses. It's visual noise. I think it's the motivation for multiple trailing closures, and the syntax just hasn't made its way back into control flow statements yet.
Multiple trailing closure support makes me think that it's feasible.
You can't really reverse engineer guard, but you can do something like this:
try samples.guardContains { $0.isInfinite } else: { throw NSError() }
extension Sequence {
func guardContains(
_ predicate: (Element) -> Bool,
else throwError: () throws -> Void
) throws {
if !contains(where: predicate) {
try throwError()
}
}
}
I still think if statements would be fine with realistic line usage…
if samples.contains { $0.isInfinite } {
// after-guard/else stuff
} else {
return
}
…but it's not as nice a break as with guard's else. With guard, I especially don't see the rationale for the necessity of parentheses, because of else having to be there.