Grzegorz Dyrda
Geeky Devs Studio
Introduction to
Functional-Reactive
Programming
Slide 2
Slide 2 text
Grzegorz Dyrda
Geeky Devs Studio
Introduction to
Functional-Reactive
Programming
Slide 3
Slide 3 text
Grzegorz Dyrda
Geeky Devs Studio
Introduction to
Functional-Reactive
Programming
Slide 4
Slide 4 text
Grzegorz Dyrda
Geeky Devs Studio
Introduction to
Functional-Reactive
Programming
Slide 5
Slide 5 text
Functional-Reactive
Programming
Slide 6
Slide 6 text
Why? Why do I need to
learn another technology?
Slide 7
Slide 7 text
–Bill Gates, 1981
“Nobody will ever need
more than 640k of RAM”
Slide 8
Slide 8 text
What does the Fox say?
Slide 9
Slide 9 text
What does the Market say?
Slide 10
Slide 10 text
What does the Market say?
• Java 9 + Reactive Streams (JEP 266)
• Spring Web Reactive (WebFlux)
• Angular 2+, Cycle.js
• Android Architecture Components,
Room, Retrofit, Realm, …
Slide 11
Slide 11 text
Functional-Reactive
Slide 12
Slide 12 text
Functional + Reactive
Slide 13
Slide 13 text
Functional + Reactive
Slide 14
Slide 14 text
Functional + Reactive
Slide 15
Slide 15 text
Functional + Time
Slide 16
Slide 16 text
Functional + Threads
Slide 17
Slide 17 text
Functional + Asynchronous
Slide 18
Slide 18 text
Functional + Asynchronous
Concurrency
Slide 19
Slide 19 text
Functional + Asynchronous
Slide 20
Slide 20 text
Functional + Asynchronous
Slide 21
Slide 21 text
Functional
Slide 22
Slide 22 text
You cannot understand
Functional-Reactive programming,
if you don’t understand
Functional programming.
Slide 23
Slide 23 text
What is Functional Programming?
Slide 24
Slide 24 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 25
Slide 25 text
Functional Programming
…is about moving from
the imperative
to declarative style.
Slide 26
Slide 26 text
Language Support
Slide 27
Slide 27 text
• Functions as first-class citizens
• Higher-order functions
• Lambda expressions
• Immutable data types
• Functions are closures
Language Support
JAVA 8+
APPROVED
Slide 28
Slide 28 text
Example
Slide 29
Slide 29 text
Client says:
“Make a sum of all our prices
that are higher than $20,
discounted by 10%”
Example
Slide 30
Slide 30 text
val prices = listOf(20, 12, 15, 21, 25, 28, 30)
Example
Slide 31
Slide 31 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 32
Slide 32 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 33
Slide 33 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 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()
var sum = 0.0
for (price in prices) {
if (price > 20) {
sum += price * 0.9
}}
}}
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()
Slide 36
Slide 36 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 37
Slide 37 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 38
Slide 38 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 39
Slide 39 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 40
Slide 40 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 41
Slide 41 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 42
Slide 42 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 43
Slide 43 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 44
Slide 44 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 45
Slide 45 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 46
Slide 46 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()
Higher level, more readable &
closer to the Business Intent
Slide 47
Slide 47 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 48
Slide 48 text
Another
Example
Slide 49
Slide 49 text
You’re a Blog admin:
“Calculate average blogpost length
of all your blog’s Users
that speak Polish”
Example
Slide 50
Slide 50 text
data class Post(val title: String, val body: String)
data class User(val language: String, val posts: List)
Example
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 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 = ???
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
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 { ??? }}
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" }}
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" }
.map { ??? }}
val users = db.getAllUsers()
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" }
.map { user -> user.posts }}
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
.filter { user -> user.language == "PL" }
.map { user -> user.posts }
// Return type is: List>
// But we need: List
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
.filter { user -> user.language == "PL" }
.map { user -> user.posts }
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
.filter { user -> user.language == "PL" }
.flatMap { user -> user.posts }
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
.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 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
.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 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
.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 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
.filter { user -> user.language == "PL" }
.flatMap { user -> user.posts }
val users = db.getAllUsers()
Slide 65
Slide 65 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 66
Slide 66 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 67
Slide 67 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 68
Slide 68 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 69
Slide 69 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 70
Slide 70 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 71
Slide 71 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 72
Slide 72 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 73
Slide 73 text
Benefits
Slide 74
Slide 74 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 75
Slide 75 text
Functional
-vs-
Object-Oriented
Slide 76
Slide 76 text
Functional
-vs-
Object-Oriented
Slide 77
Slide 77 text
Functional
+
Object-Oriented
Slide 78
Slide 78 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 79
Slide 79 text
No content
Slide 80
Slide 80 text
Functional
Slide 81
Slide 81 text
Functional + Reactive
Slide 82
Slide 82 text
Reactive
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
Observable.fromIterable(users))
.subscribe { item ->
print("Item = $item")
}}
// Item = User(language="PL",posts=...)
// Item = User(language="EN",posts=...)
// Item = User(language="PL",posts=...)
// Item = User(language="PL",posts=...)
...
Creating a Stream
Where are my Steroids?
Slide 119
Slide 119 text
API
Slide 120
Slide 120 text
API
abstract class Observable {
}}
interface Observer {
}}
Slide 121
Slide 121 text
API
abstract class Observable {
fun filter(predicate: (T) -> Boolean): Observable
fun map(transform: (T) -> R): Observable
//
// ...13k lines of code...
//
}}
interface Observer {
}}
Slide 122
Slide 122 text
API
abstract class Observable {
fun filter(predicate: (T) -> Boolean): Observable
fun map(transform: (T) -> R): Observable
//
// ...13k lines of code...
//
}}
interface Observer {
fun onNext(@NonNull t: T)
fun onError(@NonNull e: Throwable)
fun onComplete()
}}
// 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 132
Slide 132 text
// Task: perform network request after each click
Combining
// Return type: Observable>
// But we want: Observable
val buttonClicks = RxView.clicks(button)
val userInfoRequest = myService.getUserInfo()
buttonClicks
.map { click -> myService.getUserInfo() }
Slide 133
Slide 133 text
// Returns Observable that emits all items emitted by
// Observables returned by [mapper]
fun flatMap(mapper: (T) -> Observable): Observable
buttonClicks
.flatMap { click -> myService.getUserInfo() }
Combining
val buttonClicks = RxView.clicks(button)
val userInfoRequest = myService.getUserInfo()
// Task: perform network request after each click
Slide 134
Slide 134 text
Combining
.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
Slide 135
Slide 135 text
// Task: do the same thing on btn1 & btn2 click
Combining
val btn1Clicks = RxView.clicks(button1)
val btn2Clicks = RxView.clicks(button2)
btn1Clicks
.map { click -> "Click!" }
.subscribe { msg ->
print("msg = $msg")
}}
Slide 136
Slide 136 text
// Task: do the same thing on btn1 & btn2 click
Combining
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
Slide 137
Slide 137 text
fun fetchUser(): User { /* fetch from network */ }
Threading
Observable.fromCallable(this::fetchUser)
.subscribeOn(Schedulers.io())
.subscribe { user ->
print("User = $user")
}}
Slide 138
Slide 138 text
fun fetchUser(): User { /* fetch from network */ }
Threading
Observable.fromCallable(this::fetchUser)
.subscribeOn(Schedulers.io())
.subscribe { user ->
statusLabel.text = user.firstName
}}
Slide 139
Slide 139 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 140
Slide 140 text
Netflix
Slide 141
Slide 141 text
Tips
Slide 142
Slide 142 text
Tips
• Start small - get used to FP first
(be declarative, avoid side-effects etc.)
• RX is not an architecture. It's a Tool.
So use it wisely
• Subtask = flatMap
• Remember that error = end of Stream
Slide 143
Slide 143 text
Use RX when you need...
• Composing & orchestrating multiple
async events
• Manipulating time (delay, timeout etc.)
• Cancellation of async events
Slide 144
Slide 144 text
Conclusions
Slide 145
Slide 145 text
• Great for composing/orchestrating multiple
Event streams
• 100+ operators for creating, transforming &
combining streams
• Declarative threading
• Cancellation of async tasks
• Do not overuse for simple tasks
Conclusions on RX
Slide 146
Slide 146 text
How to Start?
Slide 147
Slide 147 text
How to Start?
• ReactiveX.io
• github.com/ReactiveX
• rxmarbles.com
• geekydevs.com/rx