You cannot understand
Functional-Reactive programming,
if you don’t understand
Functional programming.
Slide 9
Slide 9 text
What is Functional Programming?
Slide 10
Slide 10 text
Functional Programming
Programming style in which we avoid
sharing state and data mutation,
in favor of composing functions and
passing data from one to another.
Slide 11
Slide 11 text
Functional Programming
…is about moving from
the imperative
to declarative style.
Slide 12
Slide 12 text
The world’s most popular
functional language is…
Excel
• Functions as first-class citizens
• Higher-order functions
• Lambda expressions
• Immutable data types
• Functions are closures
Language Support
JAVA 8+
APPROVED
Slide 16
Slide 16 text
Example
Slide 17
Slide 17 text
Client says:
“Make a sum of all our prices
that are higher than $20,
discounted by 10%”
Example
Slide 18
Slide 18 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
Slide 19
Slide 19 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
var sum = 0.0
for (var i = 0; i <= prices.size; i++) {
if (price[i] > 20) {
sum += price[i] * 0.9
}}
}}
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Slide 20
Slide 20 text
var sum = 0.0
for (var i = 0; i < prices.size; i++) {
if (price[i] > 20) {
sum += price[i] * 0.9
}}
}}
Example
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Slide 21
Slide 21 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
var sum = 0.0
for (price in prices) {
if (price > 20) {
sum += price * 0.9
}}
}}
Slide 22
Slide 22 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
var sum = 0.0
for (price in prices) {
if (price > 20) {
sum += price * 0.9
}}
}}
Slide 23
Slide 23 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
Slide 24
Slide 24 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
Slide 25
Slide 25 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
Slide 26
Slide 26 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
Slide 27
Slide 27 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
Slide 28
Slide 28 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
Important:
1. No import = stdlib
2. Cross-platform
Slide 29
Slide 29 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
Slide 30
Slide 30 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
// Returns list containing only elements matching [predicate]
fun filter(predicate: (T) -> Boolean): List
Slide 31
Slide 31 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
// Returns list containing only elements matching [predicate]
fun filter(predicate: (T) -> Boolean): List
Slide 32
Slide 32 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
// Returns list containing elements transformed by [transform]
fun map(transform: (T) -> R): List
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
Slide 33
Slide 33 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
// Returns list containing elements transformed by [transform]
fun map(transform: (T) -> R): List
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
Slide 34
Slide 34 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
Single, consistent, higher-level API.
More readable & closer to the Business Intent
Slide 35
Slide 35 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
val sum = prices
.filter { price -> price > 20 }
.map { price -> price * 0.9 }
.sum()
var sum = 0.0
for (price in prices) {
if (price > 20) {
sum += price * 0.9
}}
}}
Slide 36
Slide 36 text
Another
Example
Slide 37
Slide 37 text
You’re a Blog admin:
“Calculate average blogpost length
of all your blog’s Users
that speak Polish”
Example
Slide 38
Slide 38 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
Slide 39
Slide 39 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val users = db.getAllUsers()
Slide 40
Slide 40 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = ???
val users = db.getAllUsers()
Slide 41
Slide 41 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
val users = db.getAllUsers()
Slide 42
Slide 42 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { ??? }}
val users = db.getAllUsers()
Slide 43
Slide 43 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }}
val users = db.getAllUsers()
Slide 44
Slide 44 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.map { ??? }}
val users = db.getAllUsers()
Slide 45
Slide 45 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.map { user -> user.posts }}
val users = db.getAllUsers()
Slide 46
Slide 46 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.map { user -> user.posts }
// Return type is: List>
// But we need: List
val users = db.getAllUsers()
Slide 47
Slide 47 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.map { user -> user.posts }
val users = db.getAllUsers()
Slide 48
Slide 48 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.flatMap { user -> user.posts }
val users = db.getAllUsers()
Slide 49
Slide 49 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.flatMap { user -> user.posts }
// Returns a single list with all elements yielded from
// [transform] invoked on on each element of original collection.
fun flatMap(transform: (T) -> Iterable): List
val users = db.getAllUsers()
Slide 50
Slide 50 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.flatMap { user -> user.posts }
// Returns a single list with all elements yielded from
// [transform] invoked on on each element of original collection.
fun flatMap(transform: (T) -> Iterable): List
val users = db.getAllUsers()
Slide 51
Slide 51 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.flatMap { user -> user.posts }
// Returns a single list with all elements yielded from
// [transform] invoked on on each element of original collection.
fun flatMap(transform: (T) -> Iterable): List
val users = db.getAllUsers()
Slide 52
Slide 52 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.flatMap { user -> user.posts }
val users = db.getAllUsers()
Slide 53
Slide 53 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.flatMap { user -> user.posts }
.map { ??? }}
val users = db.getAllUsers()
Slide 54
Slide 54 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.flatMap { user -> user.posts }
.map { post -> post.body.length }}
val users = db.getAllUsers()
Slide 55
Slide 55 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.flatMap { user -> user.posts }
.map { post -> post.body.length }
.average()
val users = db.getAllUsers()
Slide 56
Slide 56 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.flatMap { user -> user.posts }
.map { post -> post.body.length }
.average()
val users = db.getAllUsers()
// Easy to maintain and modify
Slide 57
Slide 57 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.filter { user -> user.language == "PL" }
.flatMap { user -> user.posts }
.map { post -> post.body.length }
.average()
val users = db.getAllUsers()
Slide 58
Slide 58 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.take(100)
.flatMap { user -> user.posts }
.map { post -> post.body.length }
.average()
val users = db.getAllUsers()
Slide 59
Slide 59 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.take(100)
.flatMap { user -> user.posts }
.map { post -> post.body.length }
.average()
val users = db.getAllUsers()
Slide 60
Slide 60 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.take(100)
.flatMap { user -> user.posts }
.map { post -> post.body.length }
.count()
val users = db.getAllUsers()
Slide 61
Slide 61 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.take(100)
.flatMap { user -> user.posts }
.map { post -> post.body.length }
.reduce { acc, length -> acc + length }
val users = db.getAllUsers()
Slide 62
Slide 62 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.take(100)
.flatMap { user -> user.posts }
.map { post -> post.body.length }
.reduce { acc, length -> acc + length }}
val users = db.getAllUsers()
// Returns a single value computed by accumulating values
// returned from applying [operation] to each element.
fun reduce(operation: (acc: S, T) -> S): S
Slide 63
Slide 63 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.take(100)
.flatMap { user -> user.posts }
.map { post -> post.body.length }
.reduce { acc, length -> acc + length }}
val users = db.getAllUsers()
// Returns a single value computed by accumulating values
// returned from applying [operation] to each element.
fun reduce(operation: (acc: S, T) -> S): S
Slide 64
Slide 64 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
val avgPolishPostLength = users
.take(100)
.flatMap { user -> user.posts }
.map { post -> post.body.length }
.fold(0) { acc, _ -> acc + 1 }}
val users = db.getAllUsers()
// Accumulates value starting with [initial] and applying
// [operation] to current accumulator value and each element.
fun fold(initial: R, operation: (acc: R, T) -> R): R
Slide 65
Slide 65 text
Benefits
Slide 66
Slide 66 text
• No explicit mutation, reassignment,
iteration etc. = less error prone
• Low-level details controlled by the library
= may be optimized under the hood
• More expressive, more readable
• Easy to enhance & modify the logic
• Closer to the business intent
Benefits
Slide 67
Slide 67 text
Functional
-vs-
Object-Oriented
Slide 68
Slide 68 text
Functional
-vs-
Object-Oriented
Slide 69
Slide 69 text
Functional
+
Object-Oriented
Slide 70
Slide 70 text
// Returns list containing only elements matching [predicate]
fun filter(predicate: (T) -> Boolean): List
// Returns list containing elements transformed by [transform]
fun map(transform: (T) -> R): List
// Flattens nested lists into single list
fun flatMap(transform: (T) -> Iterable): List
// Accumulates value by applying [operation]
fun reduce(operation: (acc: S, T) -> S): S
// Accumulates value, starting with [initial]
fun fold(initial: R, operation: (acc: R, T) -> R): R
Common Specialized Functions
Slide 71
Slide 71 text
No content
Slide 72
Slide 72 text
No content
Slide 73
Slide 73 text
Functional…
Slide 74
Slide 74 text
Functional + Reactive
Slide 75
Slide 75 text
Reactive
Part 2:
Slide 76
Slide 76 text
What does is mean
to be “Reactive”?
Slide 77
Slide 77 text
var a = 1
var b = 2
var c = a + b
print("c = $c")
What does is mean
to be “Reactive”?
// ???
Slide 78
Slide 78 text
var a = 1
var b = 2
var c = a + b
print("c = $c")
What does is mean
to be “Reactive”?
// c = 3
Slide 79
Slide 79 text
var a = 1
var b = 2
var c = a + b
a = 2
print("c = $c")
What does is mean
to be “Reactive”?
// ???
Slide 80
Slide 80 text
var a = 1
var b = 2
var c = a + b
a = 2
print("c = $c")
What does is mean
to be “Reactive”?
// c = 3
Slide 81
Slide 81 text
The world’s most popular
reactive system is…
Excel
Slide 82
Slide 82 text
Excel
Slide 83
Slide 83 text
What is Reactive Programming?
Slide 84
Slide 84 text
Reactive Programming
Programming with
Asynchronous Event Streams
Slide 85
Slide 85 text
Reactive Programming
Programming with
Asynchronous Event Streams
Slide 86
Slide 86 text
Observer Pattern
on Steroids
Reactive Programming
Slide 87
Slide 87 text
Observer Pattern
on Functional Steroids
Reactive Programming
Slide 88
Slide 88 text
1 5 12 3 7 4
This can be anything: Number,
Object, Blog Post etc.
Observing a Stream
// Subscribes the given Observer to this Observable instance.
fun subscribe(observer: Observer): Unit
interface Observer {
fun onNext(t: T)
fun onError(e: Throwable)
fun onComplete()
...
}}
// Subscribes to an ObservableSource and provides callbacks.
fun subscribe(onNext: (T) -> Unit,
onError: (Throwable) -> Unit,
onComplete: () -> Unit): Unit
// RxBinding
RxView.clicks(button) // Observable
.map { click -> 1 }
.scan { acc, next -> acc + next }
UI Events
// Similar to reduce(), but accumulates values over time,
// each time passing the result of [operation] downstream.
fun scan(operation: (acc: T, T) -> T): Observable
// Retrofit
myService.getUsers() // Observable
.map { user -> user.posts.size }
.reduce { acc, next -> acc + next }
Network Requests
.subscribe { count ->
print("Post count = $count")
}
// Post count = 120
Slide 139
Slide 139 text
Threading
Slide 140
Slide 140 text
fun fetchUser(): User { /* fetch from network */ }
Threading
Observable.fromCallable(this::fetchUser)
.subscribe { user ->
print("User = $user")
}}
// Error: Network on the Main Thread!
Slide 141
Slide 141 text
fun fetchUser(): User { /* fetch from network */ }
Threading
Observable.fromCallable(this::fetchUser)
.subscribe { user ->
print("User = $user")
}}
.subscribeOn(Schedulers.io())
Slide 142
Slide 142 text
fun fetchUser(): User { /* fetch from network */ }
Threading
Observable.fromCallable(this::fetchUser)
.subscribe { user ->
print("User = $user")
}}
.subscribeOn(Schedulers.io())
Slide 143
Slide 143 text
fun fetchUser(): User { /* fetch from network */ }
Threading
Observable.fromCallable(this::fetchUser)
.subscribe { user ->
print("User = $user")
}}
.subscribeOn(Schedulers.io())
Slide 144
Slide 144 text
fun fetchUser(): User { /* fetch from network */ }
Threading
Observable.fromCallable(this::fetchUser)
.subscribeOn(Schedulers.io())
.subscribe { user ->
statusLabel.text = user.firstName
}}
// Error: Accessing UI from the background thread!
Slide 145
Slide 145 text
fun fetchUser(): User { /* fetch from network */ }
Threading
.subscribe { user ->
statusLabel.text = user.firstName
}}
Observable.fromCallable(this::fetchUser)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
Slide 146
Slide 146 text
fun fetchUser(): User { /* fetch from network */ }
Threading
.subscribe { user ->
statusLabel.text = user.firstName
}}
Observable.fromCallable(this::fetchUser)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
Slide 147
Slide 147 text
fun fetchUser(): User { /* fetch from network */ }
Threading
.subscribe { user ->
statusLabel.text = user.firstName
}}
Observable.fromCallable(this::fetchUser)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
// OK
Slide 148
Slide 148 text
Combining Streams
Slide 149
Slide 149 text
// Task: perform network request after each click
Combining Streams
// Return type: Observable>
// But we want: Observable
val buttonClicks = RxView.clicks(button)
val userInfoRequest = myService.getUserInfo()
buttonClicks
.map { click -> myService.getUserInfo() }
Slide 150
Slide 150 text
// Returns Observable that emits all items emitted by
// Observables returned by [mapper].
fun flatMap(mapper: (T) -> Observable): Observable
buttonClicks
.flatMap { click -> myService.getUserInfo() }
val buttonClicks = RxView.clicks(button)
val userInfoRequest = myService.getUserInfo()
// Task: perform network request after each click
Combining Streams
Slide 151
Slide 151 text
.map { user -> user.firstName }
.subscribe { firstName ->
print("firstName = $firstName")
}
// firstName = John
val buttonClicks = RxView.clicks(button)
val userInfoRequest = myService.getUserInfo()
buttonClicks
.flatMap { click -> myService.getUserInfo() }
// Task: perform network request after each click
Combining Streams
Slide 152
Slide 152 text
// Task: do the same thing on btn1 & btn2 click
val btn1Clicks = RxView.clicks(button1)
val btn2Clicks = RxView.clicks(button2)
btn1Clicks
.map { click -> "Click!" }
.subscribe { msg ->
print("msg = $msg")
}}
Combining Streams
Slide 153
Slide 153 text
// Task: do the same thing on btn1 & btn2 click
val btn1Clicks = RxView.clicks(button1)
val btn2Clicks = RxView.clicks(button2)
Observable.merge(btn1Clicks, btn2Clicks)
.map { click -> "Click!" }
.subscribe { msg ->
print("msg = $msg")
}}
// Merges two (or more) Observables into a single one.
fun merge(s1: Observable, s2: Observable): Observable
Combining Streams
Slide 154
Slide 154 text
Combining Streams
Slide 155
Slide 155 text
Combining Streams
Slide 156
Slide 156 text
Netflix
Slide 157
Slide 157 text
Netflix
Slide 158
Slide 158 text
Netflix
Slide 159
Slide 159 text
Netflix
Slide 160
Slide 160 text
Netflix
Slide 161
Slide 161 text
Netflix
Slide 162
Slide 162 text
Netflix
Slide 163
Slide 163 text
Netflix
Slide 164
Slide 164 text
Conclusions
Slide 165
Slide 165 text
• FRP is great for composing/
orchestrating multiple Event streams
• “There’s an Operator for that”
• Declarative Threading, Error handling,
Cancellation of async tasks etc.
• Do not overuse for simple tasks
Conclusions
Slide 166
Slide 166 text
How to Start?
Slide 167
Slide 167 text
How to Start?
• ReactiveX.io
• github.com/ReactiveX
• rxmarbles.com
• geekydevs.com/rx