Проблема 1: слабая читаемость
static int getFirstActualTimeIndex(List times) {
int start = -1;
for (int position = 0; position < times.size(); position++) {
if (times.get(position).compareTo(new Date()) >= 0) {
start = position;
break;
}
}
return start;
}
5
Slide 6
Slide 6 text
Проблема 2: неясен момент завершения
static int getFirstActualTimeIndex(List times) {
int start = -1;
for (int position = 0; position < times.size(); position++) {
if (times.get(position).compareTo(new Date()) >= 0) {
start = position;
break;
}
}
return start;
}
6
Slide 7
Slide 7 text
Проблемы
● слабая читаемость
● недетерминированный флоу
● небезопасное изменение стейта
7
Slide 8
Slide 8 text
Imperative vs Declarative
● переход от императивной парадигмы к
декларативной позволяет писать более
безопасный и предсказуемый код
8
● алгоритм состоит из последовательных шагов:
не то, как ты это делаешь, а что именно
● такой код легче читается и более краток
Slide 9
Slide 9 text
Решение 1: в лоб, используя Rx
static int getFirstActualTimeIndex(List times) {
int index = 0;
return Observable.fromIterable(times)
.map(time -> Pair.create(index++, time))
.filter(data -> data.second.compareTo(new Date()) >= 0)
.map(data -> data.first)
.firstElement()
.toSingle(-1)
.blockingGet();
}
9
Slide 10
Slide 10 text
Решение 2: Kotlin-коллекции
fun getFirstActualTimeIndex(times: List) =
times.mapIndexed { idx, date -> Pair(idx, date) }
.filter { it.second >= Date() }
.map { it.first }
.firstOrNull() ?: -1
10
Slide 11
Slide 11 text
Kotlin-коллекции — это мощь!
fun getFirstActualTimeIndex(times: List) =
times.indexOfFirst { it >= Date() }
11
Slide 12
Slide 12 text
Причем здесь Rx?
12
Slide 13
Slide 13 text
У всего этого есть что-то общее...
● это паттерн?
● похоже на “стримы” из Java 8
● что это дает?
13
Slide 14
Slide 14 text
14
Slide 15
Slide 15 text
Rx и Kotlin - быстрое погружение
● те, кто владеет Rx, уже использует монады
15
● Observable - монада с определенным
частным поведением
● коллекции в Kotlin - тоже
Slide 16
Slide 16 text
Монада?
● в общем случае, контейнер
16
● можно класть значение
● безопасно выполнять
некоторые операции
Slide 17
Slide 17 text
Реализация
● поместить значение?
// create Kotlin-list
val ls = listOf(1, 2, 3)
17
// put the value
Observable.just(1);
// using various from-methods
List ls = new ArrayList<>();
Observable.fromIterable(ls);
Slide 18
Slide 18 text
Реализация
● применить операцию?
18
val ls = listOf(1, 2, 3)
println(ls)
// [1, 2, 3]
Реализация — резюмируя
● поместить значение
● применить операцию
● связывание
● кон-р, фабр. методы
● map
● flatMap
24
Slide 25
Slide 25 text
Простейший пример
Монада Option (Scala), она же Maybe (Haskell)
25
Slide 26
Slide 26 text
Null Safety в Kotlin
data class Person(
val firstName: String?,
val lastName: String?)
26
Slide 27
Slide 27 text
Null Safety в Kotlin
data class Person(
val firstName: String?,
val lastName: String?)
data class Employee(
val person: Person?,
val department: String)
27
Slide 28
Slide 28 text
Null Safety в Kotlin
data class Person(
val firstName: String?,
val lastName: String?)
val ivanov: Employee? = Employee(
Person("Ivan", "Ivanov"), "it")
data class Employee(
val person: Person?,
val department: String)
28
Slide 29
Slide 29 text
Null Safety в Kotlin
data class Person(
val firstName: String?,
val lastName: String?)
val ivanov: Employee? = Employee(
Person("Ivan", "Ivanov"), "it")
val firstCharInFirstName: Char? =
data class Employee(
val person: Person?,
val department: String)
29
ivanov?. person?.firstName?.first()
Slide 30
Slide 30 text
Решение с помощью Option
val firstCharInFirstName: Char? = ivanIvanov
.flatMap { it.person }
.flatMap { it.firstName }
.map(String::firstOrNull)
.unwrap()
30
ivanIvanov
?.person
?.firstName
?.first()
Slide 31
Slide 31 text
Разворачивание Option-значений
31
Slide 32
Slide 32 text
Rx-chains
// api
fun getNews(date: Date): Observable
32
Slide 33
Slide 33 text
Rx-chains
// api
fun getNews(date: Date): Observable
// mapper
fun parseEntities(response: NewsResponse): List
33
Slide 34
Slide 34 text
Rx-chains
// api
fun getNews(date: Date): Observable
// mapper
fun parseEntities(response: NewsResponse): List
// db
fun saveNews(List): Observable
34
Slide 35
Slide 35 text
Rx-chains
fun fetchNews(date: Date): Observable
fun parseEntities(response: NewsResponse): List
fun saveNews(List): Observable
35
fun getNews(date: Date)
.map(mapper::parseEntities)
.flatMap(db::saveNews)
.toCompletable()
= api.fetchNews(date)
Slide 36
Slide 36 text
Больше монад!
● Future в Scala
кейс тот же, что с Rx-chains
36
● Try
связать операции, бросающие Exception’ы
● Either
получить одно или другое значение
Slide 37
Slide 37 text
Монада — теоретический подход
37
Slide 38
Slide 38 text
Как появилась монада?
38
Slide 39
Slide 39 text
Как появилась монада?
39
Slide 40
Slide 40 text
Поведение и роли
● Type classes
Monad, Functor, Applicative, etc
● Data types
Option, Either, Try, etc
40
Slide 41
Slide 41 text
В контексте Kotlin
● не писать Java-style код
● использовать все возможности Kotlin
● решать задачи на другом уровне
● интересные решения на ФЯП
41
Slide 42
Slide 42 text
Так ли все безоблачно?
● увеличивает порог входа
● углубление в теорию
● “оверинжиниринг” простых задач
42
Slide 43
Slide 43 text
Решение проблем!
● код читается лучше
● последовательность шагов
● стейт внутри монады
43