$30 off During Our Annual Pro Sale. View Details »

Monads are Not Monsters - UIKonf 2015

Monads are Not Monsters - UIKonf 2015

UIKonf 2015 talk Monads are not Monsters.

Junior B.

May 18, 2015
Tweet

More Decks by Junior B.

Other Decks in Programming

Transcript

  1. Monads Are Not
    Monsters
    1

    View Slide

  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

    View Slide

  3. What is a Monad?
    3

    View Slide

  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

    View Slide

  5. It's about
    Compositionality and
    Effects
    In functional programming we can
    compose things, how can we use it?
    5

    View Slide

  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>?
    if let l = self.list {
    self.title = "Lobsters (Hottest \(l.count))"
    self.tableView.reloadData()
    }
    }
    }
    6

    View Slide

  7. Result
    7

    View Slide

  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>? {
    var error : NSErrorPointer = nil
    return NSJSONSerialization.JSONObjectWithData(data, options: nil, error: error)
    as! Array>?
    }
    8

    View Slide

  9. Version 1 (2/2)
    func updateUI(items: Array>) {
    //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

    View Slide

  10. The last version is great,
    but would crash in some
    cases!
    10

    View Slide

  11. Solution
    Turn Optional into a Monad
    // Turn the Optional type into a Monad
    extension Optional {
    // Definition of flatMap
    func flatMap(f: (a: T) -> Optional) -> Optional {
    switch (self) {
    case .None: return nil
    case .Some(let value): return f(a: value)
    }
    }
    }
    11

    View Slide

  12. ...and the operator
    // Operator definition
    public func >>= (lhs: Optional, rhs: T -> Optional) -> Optional {
    return lhs.flatMap(rhs)
    }
    12

    View Slide

  13. Crash Free - Version 2
    func refreshData() {
    let url = "https://lobste.rs/hottest.json"
    if let result = retrieveData(url) >>= self.deserializeData {
    updateUI(result)
    }
    }
    13

    View Slide

  14. In Cat Oriented Programming™
    14

    View Slide

  15. If cat is NULL
    15

    View Slide

  16. 16

    View Slide

  17. Maybe/Optional Monad
    17

    View Slide

  18. With Optionals we are crash free, but what
    about errors?
    We need to handle errors...
    How?
    18

    View Slide

  19. With a Result type!
    public enum Result {
    case Error(NSError)
    case Value(Box)
    // 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

    View Slide

  20. FlatMap for Result
    // Our FlatMap operator for Result type!
    public func >>= (a: Result, f: VA -> Result) -> Result {
    switch a {
    case let .Error(l):
    return .Error(l)
    case let .Value(r):
    return f(r.value)
    }
    }
    20

    View Slide

  21. What is the result?
    (Version 3)
    func refreshData() {
    let url = "https://lobste.rs/hottest.json"
    (retrieveData(url) >>= deserializeData) >>- updateUI
    }
    21

    View Slide

  22. Version 3 (1/2)
    func retrieveData(stringURL: String) -> Result {
    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

    View Slide

  23. Version 3 (2/2)
    func deserializeData(data: NSData)
    -> Result>>
    {
    var error : NSError?
    let items = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error)
    as? Array>
    if let i = items {
    return Result(i)
    }
    return Result(error!)
    }
    23

    View Slide

  24. The download is synchronous!
    24

    View Slide

  25. We need to make our code asynchronous:
    I PROMISE you, FUTURES
    are here to help.
    25

    View Slide

  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

    View Slide

  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

    View Slide

  28. How Future Works?
    A future needs:
    • An execution context to run (Queue)
    • A task to complete
    28

    View Slide

  29. Eventually
    • An 'on success' block to run
    • An 'on error' block to run
    • An 'on complete' block run
    29

    View Slide

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

    View Slide

  31. Chaining
    public func then(_ executor: ExecutionContext = ExecutionContext.defaultContext
    , _ task: (value: T) -> Result) -> Future {
    let future = Future()
    self.onComplete(){ result in
    switch result {
    case .Error(let e):
    synchronized(future) {
    future.result = Result(e)
    }
    case .Value(let val):
    future.execute(executor){
    return task(value: result.value!)
    }
    }
    }
    return future
    }
    31

    View Slide

  32. Promise
    public class Promise : Future {
    // Fulfill the promise successfully
    public func success (v: T) {
    //...
    }
    // Process with failure
    public func failure (e: NSError) {
    //...
    }
    }
    32

    View Slide

  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

    View Slide

  34. With Cache?
    func refreshWithCache() {
    let cached = Promise>>()
    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

    View Slide

  35. So what is a Monad?
    A Monad is a way to
    create complexity,
    starting with simplicity.
    35

    View Slide

  36. 36

    View Slide

  37. Other famous
    Monads
    • I/O Monad
    • State Monad
    • Reader/Writer Monad
    • Continuation Monad
    37

    View Slide

  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

    View Slide

  39. Next?
    • Brian Beckman: Don't fear the Monad (video)
    • Functional Programming in Swift (eBook)
    • Functional Programming (Coursera)
    • Principles of Reactive Programming
    (Coursera)
    39

    View Slide

  40. Resources
    • Example Project in Github
    • DeLorean Functional Reactive Kit
    • BuzzDeCafe (Cat images)
    • SwiftZ Library
    • Deviant Art
    40

    View Slide

  41. No cats were harmed
    during this presentation
    41

    View Slide

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

    View Slide