# Switch expression function

I made a bunch of functions and an enumeration, that together allow me to write something like a switch expression. It's not as powerful as Swift switch statements, since it's lacking pattern matching and I don't intend to use this in real code, it was just an experiment. Here is what I got:

enum Caze<T: ForwardIndexType where T: Equatable> {
case RangeCaze(Range<T>)
case ArrayCaze([T])

func contains(elem: T) -> Bool {
switch self {
case let .RangeCaze(r):
return r.contains(elem)
case let .ArrayCaze(arr):
return arr.contains(elem)
}
}
}

func caze<T: ForwardIndexType, R where T: Equatable>(vals: T..., @autoclosure(escaping) ret f: () -> R) -> (Caze<T>, () -> R) {
return (.ArrayCaze(vals), f)
}

func caze<T: ForwardIndexType, R where T: Equatable>(vals: Range<T>, @autoclosure(escaping) ret f: () -> R) -> (Caze<T>, () -> R) {
return (.RangeCaze(vals), f)
}

func caze<T: Equatable, R>(vals: T..., @autoclosure(escaping) ret f: () throws -> R) -> ([T], () throws -> R) {
return (vals, f)
}

func schwitch<T: ForwardIndexType, R where T: Equatable>(value: T, _ cases: (Caze<T>, () -> R)..., @autoclosure def: () throws -> R) rethrows -> R {
for (vals, f) in cases {
if vals.contains(value) {
return f()
}
}
return try def()
}

func schwitch<T: Equatable, R>(value: T, _ cases: ([T], () -> R)..., @autoclosure def: () throws -> R) rethrows -> R {
for (vals, f) in cases {
if vals.contains(value) {
return f()
}
}
return try def()
}


This allows me to write code like:

schwitch(5, // results in "lol"
caze(0, ret: "hello"),
caze(1, 2, ret: "test"),
caze(3..<7, ret: "lol"),
def: "nop")

schwitch("helloo", //results in "nop"
caze("hello", "Hello", ret: "test"),
def: "nop")


I think there are a few problems with this though.

1. There is quite a bit of code duplication due to a few nearly identical functions being needed.
2. The closure sent to the caze functions are escaping, though they are intended to never actually escape beyond a very defined scope.
3. If code in a caze function throws, it doesn't get rethrown.
4. I also considered an alternative implementation for the schwitch functions but am worried that it takes brevity a bit too far:

(cases.filter{\$0.contains(value)}.first ?? def)()


Are there any ways to improve on these 4 points and are there any other things that I missed or could have done better?

• I have started an effort to improve my own code but have hit a road block since Xcode hangs up on my code. I have posted a question on SO about it. – overactor Jun 7 '16 at 9:11

1. There is quite a bit of code duplication due to a few nearly identical functions being needed.

This is probably the biggest issue and the one I'll be addressing here.

The two schwitch functions have identical bodies, this is a major code smell. The only difference between the two is the generic constraints on T and the type T is wrapped up in (Array and Caze repectively). You don't actually care that Tconforms to ForwardIndexType in the schwitch function and the only way I was using both Caze and Array was by calling contains on them (with identical signatures). This clearly calls for a protocol. And I didn't even need to declare one myself. the contains function I was using is declared in SequenceType, a protocol both Array and Range conform to. So here's what the new (and only) schwitch function will look like:

func schwitch<S: SequenceType, R where S.Generator.Element: Equatable>(value: S.Generator.Element, _ cases: (S, () -> R)..., @autoclosure def: () -> R) -> R {
for (vals, f) in cases {
if vals.contains(value) {
return f()
}
}
return def()
}


You will notice that function is now missing a rethrows, the swift compiler hangs up on that due to a bug.

This also allowed me to get rid of the Caze enum and only have two caze functions, one that takes any amount of values and one that takes a range:

func caze<T: Equatable, R>(vals: T..., @autoclosure(escaping) ret f: () -> R) -> (AnySequence<T>, () -> R) {
return (AnySequence(vals), f)
}

func caze<T, R where T: Equatable, T: ForwardIndexType>(range: Range<T>, @autoclosure(escaping) ret f: () -> R) -> (AnySequence<T>, () -> R) {
return (AnySequence(range), f)
}


I've made both functions return an AnySequence<T> instead of more concrete type, I think that makes sense, considering how the results are used, but it's really up to you if you maybe want to return Array<T> and Range<T> instead.

I can still use my function exactly as I did before.