Ding dong – the wicked switch is gone!?
Here's my experiment with an if statement that can also
behave like a switch.
Put these things at the top level:
var refStr: String?
infix operator •
prefix operator -->
extension Any? {
static func •(obj: Any?, refObj: Any?) -> Bool {
if let refObj {
refStr = String(describing: refObj)
} else {
refStr = nil
}
if let obj {
return refStr == String(describing: obj)
} else {
return refStr == nil
}
}
static prefix func -->(obj: Any?) -> Bool {
if let obj {
return refStr == String(describing: obj)
} else {
return refStr == nil
}
}
}
Compare the new super if with a regular switch:
let someInt: Int = 3
if 1 • someInt { print("1")
} else if -->2 { print("2")
} else if -->3 { print("3")
}
switch someInt {
case 1: print("1")
case 2: print("2")
case 3: print("3")
default: break
}
Let's define some more variables to "switch":
enum Go { case up, down }
let someChar: Character = "c"
let someStr: String = "goose"
let someGo: Go = Go.up
let someOptGo: Go? = nil
if 1 • someInt { print("1")
} else if -->2 { print("2")
} else if "a" • someChar { print("a")
} else if -->"b" { print("b")
} else if "duck" • someStr { print("duck")
} else if -->"swan" { print("swan")
} else if Go.down • someGo { print("down")
} else if "up" • someOptGo { print("up")
} else if -->nil { print("nil")
} else if someChar == "c" { print("true")
} else { print("pass")
}
Benefits:
-
not having to choose between switch and if
-
consistently uses curly braces (unlike switches, that
behave more like Python)
-
no mandatory defaults (checking exhaustiveness becomes
the responsibility of the programmer)
-
do switch-like comparisons without the need to repeat a
possibly very long variable name
-
freely mix conditions with switch-like comparisons
-
switch more than one variable in the same if statement
Things to consider:
How does it affect performance?
Will String(describing: ...), or alternatively \(...),
operate the same way in the future?
Disclaimer: The above strategy should not be used in any
critical application where safety is a concern, since it
relies on String(describing: ...)
It would be possible to avoid using String(describing: ...)
by making individual operators for a lot of types, but that
doesn't seem very practical.
The method introduced so far is not really a pitch, since
it can be done already. The actual pitch would be to make
it possible to write .up instead of Go.up, and to be able
to deal with intervals. Then, of course, the whole thing
should not have to rely on String(describing: ...)
If it would be desirable, a double dot could be used when
the super if must be exhaustive:
if .up •• someGo {
print("up")
} else if -->.down {
print("down")
}
Okay, but how should the following example be tackled?
let instanceOfSomeSubClass: SomeSubClass = SomeSubClass()
let instanceOfSomeSuperClass: SomeSuperClass = instanceOfSomeSubClass
switch instanceOfSomeSuperClass {
case let var1 as SomeSubClass: print("var1")
case let var2 as OtherSubClass: print("var2")
default: break
}
Well, it's always possible to use a common trick to avoid
having to repeat a verbose variable name:
let ref = instanceOfSomeSuperClass
if let var1 = ref as? SomeSubClass { print("var1")
} else if let var2 = ref as? OtherSubClass { print("var2")
}