Last active
January 18, 2017 00:15
-
-
Save nkpart/1c4b70eeb33c3a1a66adcf7927311e6a to your computer and use it in GitHub Desktop.
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
//: Playground - noun: a place where people can play | |
import Cocoa | |
import Foundation | |
// 0. BASE CASE | |
// Our Error enum | |
enum FailsUserI { | |
case duplicateI | |
} | |
// Our logic | |
func checkUserB(_ u : String) -> FailsUserI? { | |
if u == "nick" { | |
return .duplicateI | |
} else { | |
return nil | |
} | |
} | |
let _ : FailsUserI? = checkUserB("nick") | |
// 1. Abstract the Construction of the Error | |
// We define the FailsUserI case constructors as static functions on a protocol | |
protocol FailsUser { | |
static func duplicate() -> Self | |
} | |
// The change to our function is to call the protocol functions to construct errors | |
func checkUser<E: FailsUser>(_ u : String) -> E? { | |
if u == "nick" { | |
return E.duplicate() | |
} else { | |
return nil | |
} | |
} | |
// To recover the original behaviour, we implement the protocol: | |
extension FailsUserI : FailsUser { | |
static func duplicate() -> FailsUserI { | |
return .duplicateI | |
} | |
} | |
let x: FailsUserI? = checkUser("nick") | |
// 2. Use our function with a different error type | |
// A product of errors, could be more cases here, one of the cases is our user error | |
enum AppFails { | |
case userFails(FailsUserI) | |
} | |
// The construction here defers to the underlying FailsUserI interface | |
extension AppFails : FailsUser { | |
static func duplicate() -> AppFails { | |
return .userFails(FailsUserI.duplicate()) | |
} | |
} | |
// Note that the code here is the same for the 'FailsUserI' case. | |
let y: AppFails? = checkUser("nick") | |
// 3. Compose Multiple Components together | |
// A second class of errors | |
protocol ApiErr { | |
static func serverDown() -> Self | |
} | |
// A second piece of app logic | |
func store<E: ApiErr> (_ u: String) -> E? { | |
if u == "ryan" { | |
return E.serverDown() | |
} else { | |
return nil | |
} | |
} | |
// A function that calls `store` and `checkUser`, note that the error protocols are unioned | |
func both<E: ApiErr & FailsUser>() -> E? { | |
let f: E? = store("ryan") | |
return f.flatMap { _ in checkUser("nick") } | |
} | |
// A concrete union of our 2 classes of errors | |
enum AppFails2 { | |
case duplicateUser | |
case noServer | |
} | |
extension AppFails2 : FailsUser { | |
static func duplicate() -> AppFails2 { | |
return .duplicateUser | |
} | |
} | |
extension AppFails2 : ApiErr { | |
static func serverDown() -> AppFails2 { | |
return .noServer | |
} | |
} | |
// The final call that does it all and produces the union | |
let z: AppFails2? = both() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment