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/

Eac0bf787b5279aca5e699ece096956e?s=128

Yasuhiro Inami

June 04, 2015
Tweet

Transcript

  1. State, Promises, and Reactive Programming 2015/Jun/04 Swift Language User Group

    @Realm Yasuhiro Inami / @inamiy
  2. None
  3. WWDC

  4. 2015

  5. 2014

  6. Swift

  7. 1 Year Anniversary!

  8. !

  9. None
  10. What has Swift brought us?

  11. Functional Programming

  12. Functions as first-class citizen Functions can be passed around just

    like any other data types
  13. Functions let y = f(x) • x (input), y (output)

    • data in, data out • func f(x: Int) -> String
  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<T, U>(x: T) -> U
  15. Callback-style functions let y = f(x) • Callback style (Continuation-passing

    style) • func doSomething<A, R>(arg: A, callback: R -> Void) • T = (A, R -> Void) • U = Void
  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)") } }
  17. Callback... // 3 callbacks doSomething(arg) { result1 in doSomethingElse(result1) {

    result2 in doYetAnotherSomething(result2) { result3 in println("done with result = \(result3)") } } }
  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 ... } } } } } }
  19. !"

  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) }
  21. How to do?

  22. Monad

  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
  24. In short...

  25. Monad is a container

  26. Monad is a container for pipelining

  27. Pseudocode struct Monad<T> { // func map<U>(f: T -> U)

    -> Monad<U> // not required func flatMap<U>(f: T -> Monad<U>) -> Monad<U> } func toMonad<T>(value: T) -> Monad<T> { return Monad(value) }
  28. Pseudocode (+comment) // pipelineable container struct Monad<T> { // unwrap

    -> transform -> rewrap (automatically) // func map<U>(f: T -> U) -> Monad<U> // not required // unwrap -> transform (rewrapping manually) func flatMap<U>(f: T -> Monad<U>) -> Monad<U> } // wrap (Haskell's `return` or `pure`) func toMonad<T>(value: T) -> Monad<T> { return Monad(value) }
  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<U>. If U = Monad<V>, then returning type will have a nested Monad<Monad<V>>, so this is where flatMap shines, by map (unwrap & rewrap) + flatten (re-unwrap) to remove one nest to create Monad<V> *2 Functors, Applicatives, And Monads In Pictures - adit.io
  30. Monad Laws (Haskell) • return 㱻 toMonad • >>= 㱻

    bind 㱻 flatMap
  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...
  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 ... } } }
  33. Nested Hell ↓ Pipelining

  34. Example of Monads • Optional • Array • (3rd Party)

    Result, Either, etc • Promise / Future
  35. Promises Container for single deferred computation

  36. Promises in other languages • JavaScript: Promise • Scala: Future

    (not scala.concurrent.Promise) • Java: CompletableFuture • C#: Task
  37. Promise example (Swift) class Promise<T> { init(_ executor: (resolve: Promise<T>

    -> Void) -> Void) func map<U>(f: T -> U) -> Promise<U> func flatMap<U>(f: T -> Promise<U>) -> Promise<U> } https://github.com/koher/PromiseK
  38. koher/PromiseK // helper: wrap call of `f(arg, callback)` with Promise

    func promisify<A, R>(f: (A, R -> Void) -> Void)(_ arg: A) -> Promise<R> { return Promise<R> { resolve in f(arg) { result in resolve(Promise<R>(result)) } } }
  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) }
  40. Comparison with then // ideally doSomething(arg) .then { doSomethingElse($0) }

    .then { println($0) } // PromiseK promisify(doSomething)(arg) .flatMap { promisify(doSomethingElse)($0) } .map { println($0) }
  41. Comparison with then • doSomething → promisify(doSomething) • then →

    flatMap • then → map (overloaded) then is a syntax sugar of map as well as flatMap!
  42. Monad Programming rules!

  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)
  44. Promises are cool. But...

  45. Too Simple

  46. Promises lack in core interfaces... • Progress • Pause /

    Resume • Cancel We need something like NSOperation but more power using Promise pipelining...
  47. Extending Promises

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

  49. None
  50. None
  51. SwiftTask • Promise + progress + pause + cancel +

    retry for Swift • Tiny state machine inside • Pure Swift (no import Foundation) • Works both synchronously & asynchronously • Thread safe
  52. Interface (SwiftTask v3.3.0) class Task<Progress, Value, Error> typealias ErrorInfo =

    (error: Error?, isCancelled: Bool) func then<V2>(f: (V?, ErrorInfo?) -> V2) -> Task<P, V2, E> func then<P2, V2, E2>(f: (V?, ErrorInfo?) -> Task<P2, V2, E2>) -> Task<P2, V2, E2> func success<V2>(f: V -> V2) -> Task<P, V2, E> func success<P2, V2, E2>(f: V -> Task<P2, V2, E2>) -> Task<P2, V2, E2> func failure(f: ErrorInfo -> V) -> Task func failure<P2, E2>(f: ErrorInfo -> Task<P2, V, E2>) -> Task<P2, V, E2>
  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
  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 }
  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()
  56. Demo (SwiftTask)

  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
  58. Looks similar to something...

  59. None
  60. onNext onCompleted onError

  61. Reactive Programming

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

  63. ReactiveCocoa (ObjC/Swift)

  64. and even similar to...

  65. None
  66. Node.js Stream

  67. None
  68. None
  69. Stream Container for "multiple" deferred computations

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

  71. ReactKit • class Stream<T>: Task<T, Void, NSError> (subclassed) • Stream

    Pipelining = Collaboration of multiple running Tasks • Streams are hot and cold (lazy) • Simple backpressure mechanism (pausing upstream)
  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, ...
  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]
  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
  75. Example (Incremental Search) self.searchResultsStream = searchBar.textChangedStream() |> throttle(0.3) |> distinctUntilChanged

    |> map { text -> Stream<[Result]> in return API.getSearchResultsStream(text) } |> switchLatestInner
  76. Demo https://github.com/ReactKit/ReactKitCatalog

  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!
  78. How it works

  79. None
  80. None
  81. None
  82. None
  83. None
  84. None
  85. None
  86. None
  87. None
  88. None
  89. None
  90. Streams are ‗ and ❄

  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)
  92. Comparison

  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 ❄)
  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) |> ...
  95. None
  96. func map(transform: T -> U) -> Stream<T> -> Stream<U> V.S.

    class TransformStream<T, U>: Stream<T>
  97. Function > Subclassing

  98. Recap

  99. ReactKit is Stateful

  100. Unites Hot and Cold Observables

  101. Connects Promises and Reactive Programming

  102. New approach to Reactive Programming

  103. Thanks!

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