Last active
August 29, 2015 14:13
-
-
Save loufranco/f3edb6fb9acc4883aea8 to your computer and use it in GitHub Desktop.
Wrapping Swift Pattern Matching in a Closure
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import Darwin | |
// Given Box and Result definitions from "Functional Programming in Swift" | |
// Buy it here: http://objc.io/books | |
// You need Box because Swift can't handle generics in Enums directly | |
public class Box<T> { | |
let unbox:T | |
init(_ value:T) { self.unbox = value } | |
} | |
public enum Result<T> { | |
case Success(Box<T>) // case Success(T) does not compile | |
case Error(String, NSError?) | |
} | |
// I can declare a function that returns a result or an error | |
// This is better than an Optional, because I get the error message and info | |
func funcWithResult() -> Result<Int> { | |
// definition elided | |
return Result.Error("Not implemented", nil) | |
} | |
// I _could_ do something like this: | |
var result:Int | |
switch funcWithResult() { | |
case let Result.Success(box): | |
result = box.unbox | |
case let Result.Error(msg, error): | |
// deal with error case, but what do you do about result? | |
println("Error: \(msg)") | |
} | |
// which is fine when you don't need to define anything for later | |
// But, we can't be sure result is defined, and we can't use let (for immutability) | |
// We could use Int?, but really, if we don't have a result we just want to bail here and go forward with Int | |
// So, do this instead | |
let immutableResult:Int = { switch funcWithResult() { | |
case let Result.Success(box): | |
return box.unbox | |
case let Result.Error(msg, error): | |
// deal with error case | |
// in my case, I am scripting, so I call an @noreturn function and bail the script | |
// Swift knows that this branch never returns and can analyze code paths correctly | |
exit(-1) | |
} | |
}() | |
// If your error case can't return something sensible, then | |
let immutableOptionalResult:Int? = { switch funcWithResult() { | |
case let Result.Success(box): | |
return box.unbox | |
case let Result.Error(msg, error): | |
// deal with error case | |
return nil | |
} | |
}() | |
// or ... | |
if let immutableOptionalResultChecked:Int = { switch funcWithResult() { | |
case let Result.Success(box): | |
return box.unbox | |
case let Result.Error(msg, error): | |
// deal with error case | |
return nil | |
} | |
}() { | |
// immutableOptionalResultChecked can safely be used here | |
} else { | |
// you already handled the error, so bail or leave this out or whatever | |
} | |
// There's a trade-off here where you have to think about whether to continue with Result, an Int? or deal with | |
// the error and bail. In my case, I am at the top level of a script, I just want to exit() and go forward | |
// with immutable, simple, non-optional types. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment