Do switch statements with more than one evaluations short circuit?
It probably doesn't matter, but I am curious.
Here's a simple example:
let one = 1
let two = true
let three = false
switch (one, two, three) {
case let (0, b, c) where b==c:
break
case (0, true, true):
break
default:
break
}
In the first case statement, will the 'where' evaluation even happen?
In the second case statement, will the 'two' == true happen?
@J.beenie s answer treats your first question nicely (and convincingly). b == c
will not be called since your initial one
does not match 0
, a classical AND
short circuit.
Your second question depends on the implementation of ==
for tuples. According to a comment to this question this has been part of the Swift-language since 2.2.1 and the standard implementation would of course short circuit since it is the fastest thing to do. So in your second case the second element would not be compared.
Btw: You do not need to break
in a Swift switch
statement, instead you would fallthrough
if you desire otherwise.
Correction:
My guess proved to be only half correct. Pattern matching in switch
statements seems to do more than I expected. I tried to hijack ==
using my own Bool
enum (roughly following this post (and adjusting for Swift 3)) and got some surprising results:
import Cocoa
let one = 1
let two:MyBool = .myTrue
let three:MyBool = .myFalse
typealias ThreeTuple = (o:Int, tw:MyBool, th:MyBool)
let tuple:ThreeTuple
tuple = (one, two, three)
switch tuple {
case let (1, b, c) where b == c:
print("first case")
case (1, .myTrue, .myFalse):
print("second case")
default:
print("default")
}
enum MyBool : ExpressibleByBooleanLiteral, Equatable {
case myTrue, myFalse
public init() { self = .myFalse }
public init(booleanLiteral value: BooleanLiteralType) {
self=value ? .myTrue : .myFalse
}
}
func ==(lhs: MyBool, rhs: MyBool) -> Bool {
print("evaluate ==")
switch (lhs, rhs) {
case (.myTrue,.myTrue), (.myFalse,.myFalse):
return true
default:
return false
}
}
which yields
evaluate ==
second case
at which point I was majorly surprised. The sole evaluation of ==
for MyBool
values originates from the where b == c
clause in the first case
and all the tuple "comparisons" do not use the MyBool
==
function at all!! I suspected the optimiser was interfering so I turned the switch
into a func
as
func match(_ tuple:ThreeTuple) {
switch tuple {
case let (1, b, c) where b == c:
print("first case")
case (1, .myTrue, .myFalse):
print("second case")
default:
print("default")
}
}
which should preclude excessive optimisation at compile time, but when I now ask for
match((1, .myTrue, .myTrue))
match((1, .myTrue, .myFalse))
match((0, .myTrue, .myFalse))
I get
evaluate ==
first case
evaluate ==
second case
default
where the evaluate ==
still originate only from the first case
. So the only reasonable conclusion seems to be that there is "some other magic" going on during pattern matching in switch
statements. I tried to google if I could figure out what that was, but to no avail so far. In any way, there seems to be way more short circuiting than I would have expected.