Slide 1

Slide 1 text

Back to the Futures — @Javi "Back to the Futures" - Javier Soto. March 2015 1

Slide 2

Slide 2 text

Agenda 4 Traditional Asynchronous Code 4 Problems with Traditional Error Handling 4 Future 4 Future.map 4 Future.andThen "Back to the Futures" - Javier Soto. March 2015 2

Slide 3

Slide 3 text

Traditional asynchronous code "Back to the Futures" - Javier Soto. March 2015 3

Slide 4

Slide 4 text

Traditional asynchronous code struct User { let avatarURL: NSURL } func requestUserInfo(userID: String, completion: (User?, NSError?) -> ()) func downloadImage(URL: NSURL, completion: (UIImage?, NSError?) -> ()) func loadAvatar(userID: String, completion: (UIImage?, NSError?) -> ()) { requestUserInfo(userID) { user, error in if let user = user { downloadImage(user.avatarURL) { avatar, error in if let avatar = avatar { completion(avatar, nil) } else { completion(nil, error!) } } } else { completion(nil, error!) } } } "Back to the Futures" - Javier Soto. March 2015 4

Slide 5

Slide 5 text

Traditional asynchronous code "Back to the Futures" - Javier Soto. March 2015 5

Slide 6

Slide 6 text

Traditional asynchronous code func downloadImage(URL: NSURL, completion: (UIImage?, NSError?) -> ()) 4 (.Some, .None) 4 (.None, .Some) 4 (.Some, .Some) 4 (.None, .None) "Back to the Futures" - Javier Soto. March 2015 6

Slide 7

Slide 7 text

4 (.Some, .Some) 4 (.None, .None) "Back to the Futures" - Javier Soto. March 2015 7

Slide 8

Slide 8 text

Error Handling func downloadImage(URL: NSURL, completion: (UIImage?, NSError?) -> ()) downloadImage(url) { imageOrNil, errorOrNil in if error = errorOrNil { // What if image is also not nil?? } else if image = imageOrNil { } } "Back to the Futures" - Javier Soto. March 2015 8

Slide 9

Slide 9 text

Error Handling var error: NSError? let string = NSString(contentsOfFile:path encoding:NSUTF8Encoding error:&error) if string == nil { // Oops: if error.code == NSFileReadNoSuchFileError { // ... } else if ... } "Back to the Futures" - Javier Soto. March 2015 9

Slide 10

Slide 10 text

"Back to the Futures" - Javier Soto. March 2015 10

Slide 11

Slide 11 text

"Back to the Futures" - Javier Soto. March 2015 11

Slide 12

Slide 12 text

"Back to the Futures" - Javier Soto. March 2015 12

Slide 13

Slide 13 text

NSError Alternative protocol ErrorType { } enum UserInfoErrorDomain: ErrorType { case UserDoesNotExist case UserRequestFailure(reason: String) case NetworkRequestFailure(reason: String) } extension NSError: ErrorType { } "Back to the Futures" - Javier Soto. March 2015 13

Slide 14

Slide 14 text

NSError Alternative enum NoError: ErrorType { } let error = NoError(?) "Back to the Futures" - Javier Soto. March 2015 14

Slide 15

Slide 15 text

Result enum Result { case Success(T) case Error(E) } "Back to the Futures" - Javier Soto. March 2015 15

Slide 16

Slide 16 text

Say hello to Future "Back to the Futures" - Javier Soto. March 2015 16

Slide 17

Slide 17 text

Futures 4 Encapsulate a deferred computation. 4 Treat values that incur a delay to be retrieved as if they were regular values. 4 Allow us to treat errors as first class citizens. 4 Easily composable. "Back to the Futures" - Javier Soto. March 2015 17

Slide 18

Slide 18 text

Future struct Future { typealias ResultType = Result typealias Completion = ResultType -> () typealias AsyncOperation = Completion -> () private let operation: AsyncOperation } "Back to the Futures" - Javier Soto. March 2015 18

Slide 19

Slide 19 text

Future struct Future { init(operation: AsyncOperation) { self.operation = operation } func start(completion: Completion) { self.operation() { result in completion(result) } } } "Back to the Futures" - Javier Soto. March 2015 19

Slide 20

Slide 20 text

Future.map(): transforming the computed value "Back to the Futures" - Javier Soto. March 2015 20

Slide 21

Slide 21 text

Future.map(): transforming the computed value struct User { let avatarURL: NSURL } func requestUserInfo(userID: String) -> Future func requestUserAvatarURL(userID: String) -> Future { return requestUserInfo(userID) .map { $0.avatarURL } } "Back to the Futures" - Javier Soto. March 2015 21

Slide 22

Slide 22 text

Future.map(): transforming the computed value struct Future { func map(f: T -> U) -> Future } "Back to the Futures" - Javier Soto. March 2015 22

Slide 23

Slide 23 text

map() in other types struct Array { func map(f: T -> U) -> [U] } enum Optional { func map(f: T -> U) -> U? } struct Future { func map(f: T -> U) -> Future } "Back to the Futures" - Javier Soto. March 2015 23

Slide 24

Slide 24 text

Future.map(): transforming the computed value func map(f: T -> U) -> Future { // Return a new Future w/ a new operation... return Future(operation: { completion in } } "Back to the Futures" - Javier Soto. March 2015 24

Slide 25

Slide 25 text

Future.map(): transforming the computed value func map(f: T -> U) -> Future { return Future(operation: { completion in // Retrieve the value from self... self.start { result in } } } "Back to the Futures" - Javier Soto. March 2015 25

Slide 26

Slide 26 text

Future.map(): transforming the computed value func map(f: T -> U) -> Future { return Future(operation: { completion in self.start { result in // Consider .Success and .Error... switch result { } } }) } "Back to the Futures" - Javier Soto. March 2015 26

Slide 27

Slide 27 text

Future.map(): transforming the computed value case .Success(let value): // Call completion with the transformed value completion(Result.Success(f(value))) "Back to the Futures" - Javier Soto. March 2015 27

Slide 28

Slide 28 text

Future.map(): transforming the computed value case .Error(let error): // We didn't get a value: no transformation completion(Result.Error(error)) "Back to the Futures" - Javier Soto. March 2015 28

Slide 29

Slide 29 text

Future.map(): transforming the computed value func map(f: T -> U) -> Future { return Future(operation: { completion in self.start { result in switch result { case .Success(let value): completion(Result.Success(f(value))) case .Error(let error): completion(Result.Error(error)) } } }) } "Back to the Futures" - Javier Soto. March 2015 29

Slide 30

Slide 30 text

Future.andThen(): concatenating async work "Back to the Futures" - Javier Soto. March 2015 30

Slide 31

Slide 31 text

Future.andThen(): concatenating async work func requestUserAvatarURL(userID: String) -> Future func downloadImage(URL: NSURL) -> Future func downloadUserAvatar(userID: String) -> Future { return requestUserAvatarURL(userID) .andThen(downloadImage) } "Back to the Futures" - Javier Soto. March 2015 31

Slide 32

Slide 32 text

Future.andThen(): concatenating async work struct Future { func andThen(f: T -> Future) -> Future } "Back to the Futures" - Javier Soto. March 2015 32

Slide 33

Slide 33 text

Future.andThen(): concatenating async work func andThen(f: T -> Future) -> Future { return Future(operation: { completion in } } "Back to the Futures" - Javier Soto. March 2015 33

Slide 34

Slide 34 text

Future.andThen(): concatenating async work func andThen(f: T -> Future) -> Future { return Future(operation: { completion in self.start { firstFutureResult in } } } "Back to the Futures" - Javier Soto. March 2015 34

Slide 35

Slide 35 text

Future.andThen(): concatenating async work func andThen(f: T -> Future) -> Future { return Future(operation: { completion in self.start { firstFutureResult in switch firstFutureResult { case .Success(let value): // ... case .Error(let error): // ... } } } } "Back to the Futures" - Javier Soto. March 2015 35

Slide 36

Slide 36 text

Future.andThen(): concatenating async work case .Success(let value): let nextFuture = f(value) "Back to the Futures" - Javier Soto. March 2015 36

Slide 37

Slide 37 text

Future.andThen(): concatenating async work case .Success(let value): let nextFuture = f(value) nextFuture.start { finalResult in completion(finalResult) } "Back to the Futures" - Javier Soto. March 2015 37

Slide 38

Slide 38 text

Future.andThen(): concatenating async work case .Error(let error): completion(Result.Error(error)) "Back to the Futures" - Javier Soto. March 2015 38

Slide 39

Slide 39 text

Future.andThen(): concatenating async work func andThen(f: T -> Future) -> Future { return Future(operation: { completion in self.start { firstFutureResult in switch firstFutureResult { case .Success(let value): f(value).start(completion) case .Error(let error): completion(Result.Error(error)) } } }) } "Back to the Futures" - Javier Soto. March 2015 39

Slide 40

Slide 40 text

Recap "Back to the Futures" - Javier Soto. March 2015 40

Slide 41

Slide 41 text

We went from this... func loadAvatar(userID: String, completion: (UIImage?, NSError?) -> ()) { requestUserInfo(userID) { user, error in if let user = user { downloadImage(user.avatarURL) { avatar, error in if let avatar = avatar { completion(avatar, nil) } else { completion(nil, error) } } } else { completion(nil, error) } } } "Back to the Futures" - Javier Soto. March 2015 41

Slide 42

Slide 42 text

... To this func requestUserInfo(userID: String) -> Future func downloadImage(URL: NSURL) -> Future func loadAvatar(userID: String) -> Future { return requestUserInfo(userID) .map { $0.avatarURL } .andThen(downloadImage) } "Back to the Futures" - Javier Soto. March 2015 42

Slide 43

Slide 43 text

Limitations of Futures 4 Only represent computation to retrieve one value. 4 Can't encapsulate streams of values. 4 Only consumer (pull) driven, not producer (push) driven. "Back to the Futures" - Javier Soto. March 2015 43

Slide 44

Slide 44 text

ReactiveCocoa Signals > Futures "Back to the Futures" - Javier Soto. March 2015 44

Slide 45

Slide 45 text

Follow-up Resources 4 Playground: https://github.com/JaviSoto/Talks 4 ReactiveCocoa: https://github.com/ReactiveCocoa/ ReactiveCocoa 4 Railway Oriented Programming: http:// fsharpforfunandprofit.com/posts/recipe-part2/ "Back to the Futures" - Javier Soto. March 2015 45

Slide 46

Slide 46 text

Thanks! Questions? "Back to the Futures" - Javier Soto. March 2015 46