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