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

State, Promises, and Reactive Programming

State, Promises, and Reactive Programming

Presented in Swift Language User Group Meetup on 2015/Jun/04.
http://www.meetup.com/swift-language/events/222212719/

Yasuhiro Inami

June 04, 2015
Tweet

More Decks by Yasuhiro Inami

Other Decks in Programming

Transcript

  1. State, Promises, and
    Reactive Programming
    2015/Jun/04 Swift Language User Group @Realm
    Yasuhiro Inami / @inamiy

    View Slide

  2. View Slide

  3. WWDC

    View Slide

  4. 2015

    View Slide

  5. 2014

    View Slide

  6. Swift

    View Slide

  7. 1 Year
    Anniversary!

    View Slide

  8. !

    View Slide

  9. View Slide

  10. What has
    Swift
    brought us?

    View Slide

  11. Functional
    Programming

    View Slide

  12. Functions as
    first-class citizen
    Functions can be passed around just like any other data types

    View Slide

  13. Functions
    let y = f(x)
    • x (input), y (output)
    • data in, data out
    • func f(x: Int) -> String

    View Slide

  14. Higher-order functions
    let y = f(x)
    • func in, func out
    • func f(x: Int -> String) -> (Bool -> Void)
    • generic in, generic out
    • func f(x: T) -> U

    View Slide

  15. Callback-style functions
    let y = f(x)
    • Callback style (Continuation-passing style)
    • func doSomething(arg: A, callback: R ->
    Void)
    • T = (A, R -> Void)
    • U = Void

    View Slide

  16. Callback
    // 1 callback
    doSomething(arg) { result in
    println("done with result = \(result)")
    }
    // 2 callbacks
    doSomething(arg) { result1 in
    doSomethingElse(result1) { result2 in
    println("done with result = \(result2)")
    }
    }

    View Slide

  17. Callback...
    // 3 callbacks
    doSomething(arg) { result1 in
    doSomethingElse(result1) { result2 in
    doYetAnotherSomething(result2) { result3 in
    println("done with result = \(result3)")
    }
    }
    }

    View Slide

  18. Callback Hell
    // 4, 5, 6...
    doSomething(arg) { result1 in
    doSomethingElse(result1) { result2 in
    doYetAnotherSomething(result2) { result3 in
    doSuperSomething(result3) { result4 in
    doSuperDuperSomething(result4) { result5 in
    doYetAnotherSuperDuperSomething(result5) { result6 in
    ...
    }
    }
    }
    }
    }
    }

    View Slide

  19. !"

    View Slide

  20. If we could...
    // callback style
    doSomething(arg) { result1 in
    doSomethingElse(result1) { result2 in
    println("\(result3)")
    }
    }
    ↓↓↓
    // ideally, method chaining style
    doSomething(arg)
    .then { result1 in doSomethingElse(result1) }
    .then { result2 in println(result2) }

    View Slide

  21. How to do?

    View Slide

  22. Monad

    View Slide

  23. Monad*1
    In functional programming, a monad is a structure that represents
    computations defined as sequences of steps: a type with a
    monad structure defines what it means to chain operations, or
    nest functions of that type together. This allows the programmer
    to build pipelines that process data in steps, in which each action
    is decorated with additional processing rules provided by the
    monad.
    *1 Monad (functional programming) - Wikipedia, the free encyclopedia

    View Slide

  24. In short...

    View Slide

  25. Monad is a
    container

    View Slide

  26. Monad is a
    container for
    pipelining

    View Slide

  27. Pseudocode
    struct Monad {
    // func map(f: T -> U) -> Monad // not required
    func flatMap(f: T -> Monad) -> Monad
    }
    func toMonad(value: T) -> Monad {
    return Monad(value)
    }

    View Slide

  28. Pseudocode (+comment)
    // pipelineable container
    struct Monad {
    // unwrap -> transform -> rewrap (automatically)
    // func map(f: T -> U) -> Monad // not required
    // unwrap -> transform (rewrapping manually)
    func flatMap(f: T -> Monad) -> Monad
    }
    // wrap (Haskell's `return` or `pure`)
    func toMonad(value: T) -> Monad {
    return Monad(value)
    }

    View Slide

  29. flatMap
    monad.flatMap(f)*2 will...
    1. unwrap the value from monad
    (container)
    2. return f(unwrappedValue) directly
    without rewrapping*3 it
    *3 monad.map(f) will rewrap value of type U to Monad. If U = Monad,
    then returning type will have a nested Monad>, so this is where
    flatMap shines, by map (unwrap & rewrap) + flatten (re-unwrap) to remove
    one nest to create Monad
    *2 Functors, Applicatives, And Monads In Pictures - adit.io

    View Slide

  30. Monad Laws (Haskell)
    • return 㱻 toMonad
    • >>= 㱻 bind 㱻 flatMap

    View Slide

  31. Monad Laws (Swift)
    1. toMonad(a).flatMap(f) 㱻 f(a)
    2. monad.flatMap(toMonad) 㱻 monad
    3. monad.flatMap(f).flatMap(g) 㱻 monad.flatMap { x in
    f(x).flatMap(g) }
    #3 is especially important because...

    View Slide

  32. Monad Laws (Swift)
    monad.flatMap(f).flatMap(g).flatMap(h)...

    monad.flatMap { x in
    return f(x).flatMap { y in
    return g(y).flatMap { z in
    return ...
    }
    }
    }

    View Slide

  33. Nested Hell

    Pipelining

    View Slide

  34. Example of Monads
    • Optional
    • Array
    • (3rd Party) Result, Either, etc
    • Promise / Future

    View Slide

  35. Promises
    Container for single deferred computation

    View Slide

  36. Promises in other languages
    • JavaScript: Promise
    • Scala: Future (not scala.concurrent.Promise)
    • Java: CompletableFuture
    • C#: Task

    View Slide

  37. Promise example (Swift)
    class Promise {
    init(_ executor: (resolve: Promise -> Void) -> Void)
    func map(f: T -> U) -> Promise
    func flatMap(f: T -> Promise) -> Promise
    }
    https://github.com/koher/PromiseK

    View Slide

  38. koher/PromiseK
    // helper: wrap call of `f(arg, callback)` with Promise
    func promisify(f: (A, R -> Void) -> Void)(_ arg: A)
    -> Promise {
    return Promise { resolve in
    f(arg) { result in
    resolve(Promise(result))
    }
    }
    }

    View Slide

  39. Now we can...
    // callback style
    doSomething(arg) { result1 in
    doSomethingElse(result1) { result2 in
    println("\(result3)")
    }
    }
    !!!
    // Promise pipelining
    promisify(doSomething)(arg)
    .flatMap { result1 in promisify(doSomethingElse)(result1) }
    .map { result2 in println(result2) }

    View Slide

  40. Comparison with then
    // ideally
    doSomething(arg)
    .then { doSomethingElse($0) }
    .then { println($0) }
    // PromiseK
    promisify(doSomething)(arg)
    .flatMap { promisify(doSomethingElse)($0) }
    .map { println($0) }

    View Slide

  41. Comparison with then
    • doSomething → promisify(doSomething)
    • then → flatMap
    • then → map (overloaded)
    then is a syntax sugar of map as well as flatMap!

    View Slide

  42. Monad
    Programming
    rules!

    View Slide

  43. Good part of then
    (JavaScript)
    • More readable, more intuitive
    • map & flatMap 㱺 then
    • Defines internal states and allows
    short circuit (conditional pipelining)
    • then(onFulfilled,
    onRejected)
    • then(onFulfilled)
    • catch(onRejected)

    View Slide

  44. Promises are cool.
    But...

    View Slide

  45. Too Simple

    View Slide

  46. Promises lack in core interfaces...
    • Progress
    • Pause / Resume
    • Cancel
    We need something like NSOperation but more power using
    Promise pipelining...

    View Slide

  47. Extending
    Promises

    View Slide

  48. SwiftTask
    https://github.com/ReactKit/SwiftTask

    View Slide

  49. View Slide

  50. View Slide

  51. SwiftTask
    • Promise + progress + pause + cancel + retry for Swift
    • Tiny state machine inside
    • Pure Swift (no import Foundation)
    • Works both synchronously & asynchronously
    • Thread safe

    View Slide

  52. Interface (SwiftTask v3.3.0)
    class Task
    typealias ErrorInfo = (error: Error?, isCancelled: Bool)
    func then(f: (V?, ErrorInfo?) -> V2) -> Task
    func then(f: (V?, ErrorInfo?) -> Task)
    -> Task
    func success(f: V -> V2) -> Task
    func success(f: V -> Task) -> Task
    func failure(f: ErrorInfo -> V) -> Task
    func failure(f: ErrorInfo -> Task) -> Task

    View Slide

  53. Interface (SwiftTask v3.3.0)
    func progress(f: (oldProgress: P?, newProgress: P) -> Void)
    -> Task
    func pause() -> Bool
    func resume() -> Bool
    func cancel(error: E?) -> Bool
    func try(maxTryCount: Int) -> Task

    View Slide

  54. Networking Example
    let task = AlamofireTask { progress, fulfill, reject, configure in
    // define task
    Alamofire.download(.GET, "http://httpbin.org/stream/100", destination: somewhere)
    .progress { newProgress in
    progress(newProgress)
    }
    .response { request, response, data, error in
    if let error = error {
    reject(error)
    return
    }
    fulfill(response)
    }
    return
    }

    View Slide

  55. Networking Example
    task.progress { oldProgress, newProgress in
    // update progress UI
    }.success { response -> Void in
    // handle fulfilled
    }.failure { error, isCancelled -> Void in
    // handle rejected or cancelled
    }.then { _ in
    // finally
    }
    // running task is interruptable if configured
    task.pause()
    task.resume()
    task.cancel()

    View Slide

  56. Demo
    (SwiftTask)

    View Slide

  57. Recap
    SwiftTask can...
    • Progress ... send progress values multiple times
    • Fulfill ... send fulfilled value once at end, or
    • Reject/Cancel ... send error once at end

    View Slide

  58. Looks similar to
    something...

    View Slide

  59. View Slide

  60. onNext
    onCompleted
    onError

    View Slide

  61. Reactive
    Programming

    View Slide

  62. Reactive Extensions
    (C#, Java, JavaScript, etc)

    View Slide

  63. ReactiveCocoa
    (ObjC/Swift)

    View Slide

  64. and even
    similar to...

    View Slide

  65. View Slide

  66. Node.js Stream

    View Slide

  67. View Slide

  68. View Slide

  69. Stream
    Container for "multiple" deferred computations

    View Slide

  70. ReactKit
    https://github.com/ReactKit/ReactKit

    View Slide

  71. ReactKit
    • class Stream: Task (subclassed)
    • Stream Pipelining = Collaboration of multiple running Tasks
    • Streams are hot and cold (lazy)
    • Simple backpressure mechanism (pausing upstream)

    View Slide

  72. Example (FizzBuzz)
    Stream.sequence(1...100)
    |> map { x -> String in
    switch x {
    case _ where x % 15 == 0: return "FizzBuzz"
    case _ where x % 3 == 0: return "Fizz"
    case _ where x % 5 == 0: return "Buzz"
    default: return "\(x)"
    }
    }
    ~>! println
    // prints each: 1, 2, Fizz, 4, Buzz, ...

    View Slide

  73. Example (Fibonacci)
    let fibonacciValues =
    Stream.infiniteSequence((0, 1)) { a in (a.1, a.0 + a.1) }
    |> map { a in a.0 }
    |> skip(3)
    |> take(10)
    |> buffer()
    ~>! () // terminal operation
    println(fibonacciValues)
    // [2, 3, 5, 8, 13, 21, 34, 55, 89, 144]

    View Slide

  74. Example (KVO)
    // create stream via KVO
    self.cell1Stream = KVO.stream(cell1, "value")
    // bind stream via KVC (`<~` as binding operator)
    (cell2, "value") <~ self.cell1Stream
    // cell2.value == "initial" at this point
    cell1.value = "REACT"
    // cell2.value == "REACT" // updates automatically

    View Slide

  75. Example (Incremental Search)
    self.searchResultsStream =
    searchBar.textChangedStream()
    |> throttle(0.3)
    |> distinctUntilChanged
    |> map { text -> Stream<[Result]> in
    return API.getSearchResultsStream(text)
    }
    |> switchLatestInner

    View Slide

  76. Demo
    https://github.com/ReactKit/ReactKitCatalog

    View Slide

  77. Stream Operations
    • Transforming: map, flatMap, mapAccumulate (scan),
    buffer, bufferBy, groupBy
    • Filtering: filter, take, takeUntil, skip, skipUntil,
    sample, distinct, distinctUntilChanged
    • Combining: merge, concat, startWith, combineLatest,
    zip, catch
    • Timing: delay, interval, throttle, debounce ... and a lot!

    View Slide

  78. How it works

    View Slide

  79. View Slide

  80. View Slide

  81. View Slide

  82. View Slide

  83. View Slide

  84. View Slide

  85. View Slide

  86. View Slide

  87. View Slide

  88. View Slide

  89. View Slide

  90. Streams are
    ‗ and ❄

    View Slide

  91. Rx's Hot & Cold Observables
    • Hot Observable ‗
    • always active (may lose data if not subscribed)
    • uses same underlying source (broadcasting)
    • Cold Observable ❄
    • paused until subscription
    • clones underlying source (not broadcasting)

    View Slide

  92. Comparison

    View Slide

  93. Comparison (Rx/RAC)
    • Rx.NET, RxJava, RxJS, etc...
    • Observable (either ‗ or ❄)
    • ReactiveCocoa (ver 3.0.0)
    • Signal (‗) / SignalProducer (❄)
    • ReactKit
    • Stream (always ‗ and lazy like ❄)

    View Slide

  94. Comparison (Node.js)
    • Node.js Stream
    • Stateful: pause, resume, close (destroy)
    • Pipe-based: upstream.pipe(transformStream)...
    • ReactKit
    • Stateful: pause, resume, cancel
    • Functional: upstream |> map(transform) |> ...

    View Slide

  95. View Slide

  96. func map(transform: T -> U) ->
    Stream -> Stream
    V.S.
    class TransformStream:
    Stream

    View Slide

  97. Function
    >
    Subclassing

    View Slide

  98. Recap

    View Slide

  99. ReactKit is
    Stateful

    View Slide

  100. Unites
    Hot and Cold
    Observables

    View Slide

  101. Connects
    Promises and
    Reactive
    Programming

    View Slide

  102. New approach to
    Reactive
    Programming

    View Slide

  103. Thanks!

    View Slide

  104. Questions?
    @inamiy
    https://github.com/ReactKit

    View Slide