Last active
August 29, 2015 14:28
-
-
Save heiko-henrich/953c80ec1f9b96008539 to your computer and use it in GitHub Desktop.
Result Error Handling and Monad Error Handling (Result Type) working together
This file contains 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
/// simple Error Type | |
struct Error: ErrorType {} | |
/// the error handling monad | |
enum Result<T> { | |
case Value(T) | |
case Error(ErrorType) | |
} | |
/// this operator transforms a throwing function result in a resutl type | |
/// I would have preferred a prefix operator, but this forces to use parens which is awkward | |
postfix operator +/-? {} | |
postfix func +/-?<T>(@autoclosure f: () throws -> T ) -> Result<T> { | |
do { | |
return Result.Value(try f()) | |
} | |
catch let e { | |
return Result.Error(e) | |
} | |
} | |
/// example | |
func throwWhenOdd(i: Int) throws -> Int { | |
if i % 2 != 0 { | |
throw Error() | |
} | |
else { | |
return i | |
} | |
} | |
let result1 = try throwWhenOdd(5)+/-? | |
let result2 = try throwWhenOdd(6)+/-? | |
/// extension to Result "unwraps" Result and throws, when an error is found | |
extension Result { | |
func throwing() throws -> T { | |
switch self { | |
case let .Value(value): return value | |
case let .Error(error): throw error | |
} | |
} | |
} | |
/// example | |
try? result1.throwing() | |
try? result2.throwing() | |
/// this operator turns an Optional into a Result | |
infix operator +/-?? { | |
associativity right | |
precedence 131 | |
} | |
func +/-??<T>(opt: T?, error:ErrorType) -> Result<T> { | |
return opt == nil ? Result.Error(error) : Result.Value(opt!) | |
} | |
/// example | |
let opt1: Int? = nil | |
let opt2: Int? = 5 | |
opt1 +/-?? Error() | |
opt2 +/-?? Error() | |
/// this is the opposite of the try? keyword, | |
/// which unwraps an optional and throws an error when failing | |
infix operator !! { | |
associativity right | |
precedence 131 | |
} | |
func !!<T>(opt: T?, error:ErrorType) throws -> T{ | |
if let opt = opt { | |
return opt | |
} | |
else { | |
throw error | |
} | |
} | |
/// example | |
do { | |
print("success", try opt2 !! Error()) | |
// will throw here: | |
print("more success", try opt1 !! Error()) | |
} | |
catch let e { | |
print("error!!!", e) | |
} | |
// success 5 | |
// error!!! Error() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
There is a lot of discussion about the new swift error handling and how error handling ought to be.
A lot of frameworks (like reactiive cocoa, RXSwift among others) use an algebraic Result type to promote errors. swift tries a new way somewhrere between the old fashoned try catch construct and functions which might return errors.
I don't think these are mutually exclusive paths.
Since XCode 7b6 @autoclosure can throw errors, so it's easy to convert a throwing function in an expression which yields a "Result"
On the other hand, the try? keyword transforms a throwing function result in an Optional.
It' easy to do the opposite:
the !! operator here kind of force unwraps the optional and throws an error when failing.