Bryan Luby asks: “What is your take on using the Swift 2 “guard case” syntax vs using the “~=” expression pattern?” And I was all, aren’t guard cases really for pattern matching associated values like this?
enum Test {case a(Int), b(String)} let x = Test.a(2) let y = Test.b("Hello") guard case Test.a(let value) = x else { fatalError("shouldn't fire") } guard case Test.b(let value) = x else { fatalError("will fire") }
But no, Bryan was talking about guard case indices = index else { return nil }
, which works like this:
let foo = "abcdef".characters let bar = "abcdefghij".characters guard case foo.indices = foo.startIndex else {fatalError("won't fail")} guard case foo.indices = bar.endIndex.predecessor() else {fatalError("fails")}
It’s a really weird way of testing the rhs value against the pattern on the lhs. Compare and contrast with
guard indices ~= index else { return nil }
After thinking about this for a bit:
- That is quite cool
- I don’t think I like it at all.
The readability is awful. And, I don’t think a large part of the Swift community is aware of or uses this pattern. Going by the principle of “code is more often read than written” (not to mention the “principle of least astonishment”), I’d stick with the pattern matching operator over guard case.
Agree? Disagree? Comment, tweet, or email. And thank you Bryan because I love discovering stuff like this.
Zachary Waldowski tweets: “if/guard/for/while case is the built-in way, whereas ~= is essentially an implementation detail. :/”. Maybe so but I don’t think that make the code any prettier or easier to read. He replies: “Honestly, I couldn’t tell you; I think “if case” reads better, but my coworkers say I’m nuts.”
3 Comments
“The readability is awful”
I completely agree with you. I think a few too many programmers start thinking of problems in terms of what they know, or from abstractions, rather than from first principles.
Readability is key, for reasons far too obvious to list.
I believe the current approach to return values and closures is a mess. It can take a lot of mental effort to parse many method signatures, let alone chains of closures.
We need mechanisms, even if they are just sugar, to clearly delineate the return(s) of a method, the closures, and any trailing closures.
For example, a common scrap of code:
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
// …
}
Even if you have some “muscle memory” for this stuff, it still takes quite a few brain cells to understand what this method’s purpose is, and that’s without more complex return types in the handler and the method itself.
(Do not get me started on the use of ! for both negation and force-unwrapping… or why, in 2016, we can’t just have a “not” keyword.)
And while we’re there, I have no problem replacing “->” with the keyword “returns”. It makes logical sense, it’s extremely easy to read and write, and it’s one less right-angle-bracket to parse.
And since Swift method signatures can be so long, I’d like to see a style adopted for separating out those returns. e.g.
func someFunction( … interminable list of parameters …)
returns (Bool, Int) {
// … body
}
The above clearly separates the components of the signature, helping to make it more readable and self-documenting. Just a suggestion…
I really, really do not understand what a guard statement gets you? It seems that the vast majority have the same else { return ) statement hanging off of it that the else statement should almost be optional. I do not think I have ever seen a guard statement in the wild where an if/else statement would have sufficed just as sufficiently. Is this a case a new shiny things? Out with the old and in with the new? What? I sure hope I am missing a vital piece, because otherwise the guard statement just adds to additional convolution and perhaps change for the sake of change.
And it can also test and unwrap optionals scoped to the top level. Super useful.