Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

WWDC

Slide 4

Slide 4 text

2015

Slide 5

Slide 5 text

2014

Slide 6

Slide 6 text

Swift

Slide 7

Slide 7 text

1 Year Anniversary!

Slide 8

Slide 8 text

!

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

What has Swift brought us?

Slide 11

Slide 11 text

Functional Programming

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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)") } }

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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 ... } } } } } }

Slide 19

Slide 19 text

!"

Slide 20

Slide 20 text

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) }

Slide 21

Slide 21 text

How to do?

Slide 22

Slide 22 text

Monad

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

In short...

Slide 25

Slide 25 text

Monad is a container

Slide 26

Slide 26 text

Monad is a container for pipelining

Slide 27

Slide 27 text

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) }

Slide 28

Slide 28 text

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) }

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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...

Slide 32

Slide 32 text

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 ... } } }

Slide 33

Slide 33 text

Nested Hell ↓ Pipelining

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Promises Container for single deferred computation

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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)) } } }

Slide 39

Slide 39 text

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) }

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

Monad Programming rules!

Slide 43

Slide 43 text

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)

Slide 44

Slide 44 text

Promises are cool. But...

Slide 45

Slide 45 text

Too Simple

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

Extending Promises

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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 }

Slide 55

Slide 55 text

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()

Slide 56

Slide 56 text

Demo (SwiftTask)

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

Looks similar to something...

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

onNext onCompleted onError

Slide 61

Slide 61 text

Reactive Programming

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

ReactiveCocoa (ObjC/Swift)

Slide 64

Slide 64 text

and even similar to...

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

Node.js Stream

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

Stream Container for "multiple" deferred computations

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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, ...

Slide 73

Slide 73 text

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]

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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!

Slide 78

Slide 78 text

How it works

Slide 79

Slide 79 text

No content

Slide 80

Slide 80 text

No content

Slide 81

Slide 81 text

No content

Slide 82

Slide 82 text

No content

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

No content

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

No content

Slide 87

Slide 87 text

No content

Slide 88

Slide 88 text

No content

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

Streams are ‗ and ❄

Slide 91

Slide 91 text

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)

Slide 92

Slide 92 text

Comparison

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

No content

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

Function > Subclassing

Slide 98

Slide 98 text

Recap

Slide 99

Slide 99 text

ReactKit is Stateful

Slide 100

Slide 100 text

Unites Hot and Cold Observables

Slide 101

Slide 101 text

Connects Promises and Reactive Programming

Slide 102

Slide 102 text

New approach to Reactive Programming

Slide 103

Slide 103 text

Thanks!

Slide 104

Slide 104 text

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