Save 37% off PRO during our Black Friday Sale! »

Monads are Not Monsters - UIKonf 2015

Monads are Not Monsters - UIKonf 2015

UIKonf 2015 talk Monads are not Monsters.

09cc2d9e2e471b3a598a5c0b96732b6c?s=128

Junior B.

May 18, 2015
Tweet

Transcript

  1. Monads Are Not Monsters 1

  2. What is Functional Programming? (In a nutshell) • It's a

    style not only a paradigm • Programming with first class functions • Modularization of the software • Avoid side effects (where possible) • Immutability (where possible) 2
  3. What is a Monad? 3

  4. Crockford's law In addition to it begin useful, it is

    also cursed and the curse of the monad is that once you get the epiphany, once you understand - "oh that's what it is" - you lose the ability to explain it to anybody. — Doug Crockford 4
  5. It's about Compositionality and Effects In functional programming we can

    compose things, how can we use it? 5
  6. Example (Initial) func refreshData() { var error : NSErrorPointer =

    nil var response: NSURLResponse? let contentRequest = NSURLRequest(URL: NSURL(string: "https://lobste.rs/hottest.json")!) let data = NSURLConnection.sendSynchronousRequest(contentRequest, returningResponse: &response, error: error) if let d = data { self.list = NSJSONSerialization.JSONObjectWithData(d, options: nil, error: error) as! Array<Dictionary<String, AnyObject>>? if let l = self.list { self.title = "Lobsters (Hottest \(l.count))" self.tableView.reloadData() } } } 6
  7. Result 7

  8. Let's improve it! - Version 1 (1/2) func retrieveData(stringURL: String)

    -> NSData? { var error : NSErrorPointer = nil var response: NSURLResponse? let contentRequest = NSURLRequest(URL: NSURL(string: stringURL)!) return NSURLConnection.sendSynchronousRequest(contentRequest, returningResponse: &response, error: error) } func deserializeData(data: NSData) -> Array<Dictionary<String, AnyObject>>? { var error : NSErrorPointer = nil return NSJSONSerialization.JSONObjectWithData(data, options: nil, error: error) as! Array<Dictionary<String, AnyObject>>? } 8
  9. Version 1 (2/2) func updateUI(items: Array<Dictionary<String, AnyObject>>) { //Side Effects

    self.list = items self.title = "Lobsters (Hottest \(items.count))" self.tableView.reloadData() } // Very compact, but order is reversed func refreshData() { let url = "https://lobste.rs/hottest.json" updateUI(deserializeData(retrieveData(url)!)!) } 9
  10. The last version is great, but would crash in some

    cases! 10
  11. Solution Turn Optional into a Monad // Turn the Optional

    type into a Monad extension Optional { // Definition of flatMap func flatMap<U>(f: (a: T) -> Optional<U>) -> Optional<U> { switch (self) { case .None: return nil case .Some(let value): return f(a: value) } } } 11
  12. ...and the operator // Operator definition public func >>= <T,U>(lhs:

    Optional<T>, rhs: T -> Optional<U>) -> Optional<U> { return lhs.flatMap(rhs) } 12
  13. Crash Free - Version 2 func refreshData() { let url

    = "https://lobste.rs/hottest.json" if let result = retrieveData(url) >>= self.deserializeData { updateUI(result) } } 13
  14. In Cat Oriented Programming™ 14

  15. If cat is NULL 15

  16. 16

  17. Maybe/Optional Monad 17

  18. With Optionals we are crash free, but what about errors?

    We need to handle errors... How? 18
  19. With a Result type! public enum Result<V> { case Error(NSError)

    case Value(Box<V>) // A Box is a workaround to avoid // compilation failure public init(_ e: NSError?, _ v: V) { if let ex = e { self = Result.Error(ex) } else { self = Result.Value(Box(v)) } } // [...] } 19
  20. FlatMap for Result // Our FlatMap operator for Result type!

    public func >>= <VA, VB>(a: Result<VA>, f: VA -> Result<VB>) -> Result<VB> { switch a { case let .Error(l): return .Error(l) case let .Value(r): return f(r.value) } } 20
  21. What is the result? (Version 3) func refreshData() { let

    url = "https://lobste.rs/hottest.json" (retrieveData(url) >>= deserializeData) >>- updateUI } 21
  22. Version 3 (1/2) func retrieveData(stringURL: String) -> Result<NSData> { var

    error : NSError?; var response: NSURLResponse? let contentRequest = NSURLRequest(URL: NSURL(string: stringURL)!) if let data = NSURLConnection.sendSynchronousRequest(contentRequest, returningResponse: &response, error: &error) { return Result(data) } return Result(error!) } 22
  23. Version 3 (2/2) func deserializeData(data: NSData) -> Result<Array<Dictionary<String, AnyObject>>> {

    var error : NSError? let items = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as? Array<Dictionary<String, AnyObject>> if let i = items { return Result(i) } return Result(error!) } 23
  24. The download is synchronous! 24

  25. We need to make our code asynchronous: I PROMISE you,

    FUTURES are here to help. 25
  26. What is a Future? A Future is an object holding

    a value which may become available at some point and: • If the runnable block has not yet completed, we say that the Future is not completed. • If the runnable block has completed with a value or an exception, we say that the Future is completed. 26
  27. What is a Promise? A Promise is an "extension" of

    Future, with the following differences: • A Promise "can" return a Future (not the opposite) • A Promise can or cannot be fulfilled • A Promise is read/write 27
  28. How Future Works? A future needs: • An execution context

    to run (Queue) • A task to complete 28
  29. Eventually • An 'on success' block to run • An

    'on error' block to run • An 'on complete' block run 29
  30. Future public class Future<T> { public typealias Runnable = ()

    -> Result<T> typealias OnSuccessCallback = (T) -> () public typealias OnFailureCallback = (NSError) -> () typealias OnCompleCallback = (result: Result<T>) -> () // wrapper around dispatch_queue_t let executor: ExecutionContext var result: Result<T>? = nil { didSet { // perform onSuccess, onComplete, onFailure self.performCallbacks() } } let task: Runnable? //[...] } 30
  31. Chaining public func then<U>(_ executor: ExecutionContext = ExecutionContext.defaultContext , _

    task: (value: T) -> Result<U>) -> Future<U> { let future = Future<U>() self.onComplete(){ result in switch result { case .Error(let e): synchronized(future) { future.result = Result<U>(e) } case .Value(let val): future.execute(executor){ return task(value: result.value!) } } } return future } 31
  32. Promise public class Promise<T> : Future<T> { // Fulfill the

    promise successfully public func success (v: T) { //... } // Process with failure public func failure (e: NSError) { //... } } 32
  33. Final Version func refreshData() { let url = "https://lobste.rs/hottest.json" ///

    this is a convenience global function to create a Future future { return self.retrieveData(url) }.then { data in return self.deserializeData(data) }.onSuccess { items in self.updateUI(items) }.onFailure { e in NSLog(e.description) } } 33
  34. With Cache? func refreshWithCache() { let cached = Promise<Array<Dictionary<String, AnyObject>>>()

    let url = "https://lobste.rs/hottest.json" future { if let cachedData = self.getCached() { cached.success(cachedData) } return retrieveData(url) >>= deserializeData }.onSuccess { items in self.updateUI(items) self.cacheItems(items) } cached.onSuccess { items in self.updateUI(items) } } 34
  35. So what is a Monad? A Monad is a way

    to create complexity, starting with simplicity. 35
  36. 36

  37. Other famous Monads • I/O Monad • State Monad •

    Reader/Writer Monad • Continuation Monad 37
  38. What can be built with Monads? (Examples) • A Reactive

    Framework, for combining streams • A DSL for SQLite, creating type safe queries • A toolset to deal with collections (e.g. LINQ) 38
  39. Next? • Brian Beckman: Don't fear the Monad (video) •

    Functional Programming in Swift (eBook) • Functional Programming (Coursera) • Principles of Reactive Programming (Coursera) 39
  40. Resources • Example Project in Github • DeLorean Functional Reactive

    Kit • BuzzDeCafe (Cat images) • SwiftZ Library • Deviant Art 40
  41. No cats were harmed during this presentation 41

  42. Junior B. www.bonto.ch - @bontojr www.sideeffects.xyz 42