Kotlin и ФП: то, что вы всегда хотели в Java, но боялись...

56
Kotlin и ФП: то, что вы всегда хотели в Java, но боялись попробовать (даже после Java 8) Марат Ахин 15 февраля 2018 г. Санкт-Петербургский политехнический университет 1

Transcript of Kotlin и ФП: то, что вы всегда хотели в Java, но боялись...

Page 1: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Kotlin и ФП: то, что вы всегда хотели в Java, но боялисьпопробовать (даже после Java 8)

Марат Ахин

15 февраля 2018 г.

Санкт-Петербургский политехнический университет

1

Page 2: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Recap

То, о чем забыл рассеянный преподаватель

2

Page 3: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Функциональное программирование

3

Page 4: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Функциональное программирование

Что это такое?

• Функциональная программа является вычисляемым выражением,которое отвечает на интересующий нас вопрос

• Сам процесс вычисления нас особо не интересует• Вычисления являются повторяемыми

4

Page 5: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Функциональное программирование

• Функциональная программа отвечает на вопрос “что?”• Императивная программа отвечает на вопрос “как?”

Черное и белое

5

Page 6: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Функциональное программирование в реальности

Посередине между черным и белым

• Кто-то попытался добавить ФП в свой императивный мир• Java• C++• C#

• Кто-то попытался добавить ИП в свой функциональный мир• OCaml• Clojure

• Кто-то попытался объединить две стороны• Scala• Kotlin

• Кто-то просто странный• JavaScript

6

Page 7: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

ФП в Котлине

• Все — это функция объект• Функция, получается, тоже объект, просто не совсем обычного типа

fun <T> stringifyList(list: List<T>, f: (T) -> String): List<String> {return TODO()

}

Wat? Generics?

7

Page 8: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Generics 101

8

Page 9: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Generics 101

• В Котлине есть дженерики• АКА параметрический полиморфизм• Основывается на дженериках из Java

• Привет, type erasure, вот это вот все• Но кое-что работает по-другому (ждите следующей серии!)

fun <T> anyToString(value: T): String = value.toString()

class ValueWrapper<T>(val value: T) {val valueAsString = value.toString()

}

9

Page 10: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Generics 101

10

Page 11: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Как использовать функциональный тип

fun <T> stringifyList(list: List<T>, f: (T) -> String): List<String> {val res = mutableListOf<String>()for (e in list) {

res.add(f.invoke(e))}return res

}

Wat? Mutable lists???

11

Page 12: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Kotlin collections 101

12

Page 13: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Kotlin collections 101

• Коллекции (списки, множества, отображения) в Котлине разделенына неизменяемые и изменяемые на уровне системы типов

• Неизменяемые коллекции не позволяют изменять свое содержимое(ммм…)

• Изменяемые коллекции расширяют неизменяемые и добавляютвозможность модификации

• Для создания коллекций принято использовать функции изстандартной библиотеки

• listOf()/mutableListOf()• setOf()/mutableSetOf()• ...

Более подробно посмотрим в следующих сериях

13

Page 14: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Kotlin collections 101

14

Page 15: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Как использовать функциональный тип

fun <T> stringifyList(list: List<T>, f: (T) -> String): List<String> {val res = mutableListOf<String>()for (e in list) {

res.add(f.invoke(e))}return res

}

15

Page 16: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Как использовать функциональный тип

fun <T> stringifyList(list: List<T>, f: (T) -> String): List<String> {val res = mutableListOf<String>()for (e in list) {

res.add(f(e))}return res

}

16

Page 17: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Как создать функциональный тип (1)

class ErrorProcessor {fun buildErrorDescList(): List<String> {

val errorList = getErrorList()return stringifyList(errorList, ::anyToString)

}}

17

Page 18: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Как создать функциональный тип (2)

class ErrorProcessor {fun buildErrorDesc(e: Error): String = e.toString()

fun buildErrorDescList(): List<String> {val errorList = getErrorList()return stringifyList(errorList, this::buildErrorDesc)

}}

18

Page 19: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Как создать функциональный тип (3)

class ErrorProcessor {fun buildErrorDescList(): List<String> {

val errorList = getErrorList()return stringifyList(errorList,

fun(e: Error): String {return e.toString()

})

}}

19

Page 20: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Как создать функциональный тип (3)

class ErrorProcessor {fun buildErrorDescList(): List<String> {

val errorList = getErrorList()return stringifyList(errorList,

fun(e): String {return e.toString()

})

}}

20

Page 21: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Как создать функциональный тип (3)

class ErrorProcessor {fun buildErrorDescList(): List<String> {

val errorList = getErrorList()return stringifyList(errorList,

fun(e) = e.toString())}

}

21

Page 22: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Как создать функциональный тип (4)

class ErrorProcessor {fun buildErrorDescList(): List<String> {

val errorList = getErrorList()return stringifyList(errorList,

{ e: Error -> e.toString() })}

}

22

Page 23: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Как создать функциональный тип (4)

class ErrorProcessor {fun buildErrorDescList(): List<String> {

val errorList = getErrorList()return stringifyList(errorList,

{ e -> e.toString() })}

}

23

Page 24: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Как создать функциональный тип (4)

class ErrorProcessor {fun buildErrorDescList(): List<String> {

val errorList = getErrorList()return stringifyList(errorList) {

e -> e.toString()}

}}

24

Page 25: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Как создать функциональный тип (4)

class ErrorProcessor {fun buildErrorDescList(): List<String> {

val errorList = getErrorList()return stringifyList(errorList) { it.toString() }

}}

25

Page 26: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Неужели все это придется писать самому?

26

Page 27: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Функции высших порядков

• fold/foldRight• map/flatMap• filter• groupBy• all/any/none• …

Под капот заглянем чуть позже

27

Page 28: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Захват переменных из окружения

class ErrorProcessor {private val freshErrorId

get() = UUID.randomUUID()

private val ignoredErrorTypes = mutableSetOf<ErrorType>()

fun buildErrorDescList(): List<String> =getErrorList()

.filter { it.type !in ignoredErrorTypes }

.map { freshErrorId to it }

.map { (id, error) -> ”[$id] ${error.msg}” }}

28

Page 29: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Функциональные типы с неявным this

• Обозначаются как T.[function type]• Являются очередным способом сделать код более читаемым

fun <T, R> with(receiver: T, block: T.() -> R): R =receiver.block()

public class UserBuilder {public void setName(final String name) { ... }public void setAddress(final Address address) { ...}public void setEmail(final String email) { ... }public User build() { ... }

}

29

Page 30: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Функциональные типы с неявным this

fun buildTestUser(): User =with(UserBuilder()) {

setName(”Test User”)setAddress(null)setEmail(”[email protected]”)build()

}

30

Page 31: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Функциональные типы с неявным this

fun buildTestUser_(ub: UserBuilder): User {ub.setName(”Test User”)ub.setAddress(null)ub.setEmail(”[email protected]”)return ub.build()

}

fun buildTestUser2(): User =with(UserBuilder(), ::buildTestUser_)

31

Page 32: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Функции-расширения

• Воплощение функциональных типов с неявным this надпроизвольными типами

• Являются еще одним очень классным способом сделать код болеечитаемым

• К этому моменту вы, должно быть, начали догадываться, чтоявляется одной из основных идей Котлина…

• Кроме того, позволяют расширять внешний код

fun UserBuilder.buildTestUser(): User =with(this, ::buildTestUser_)

32

Page 33: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Функции-расширения

public inline fun <T> T.apply(block: T.() -> Unit): T {block()return this

}public inline fun <T> T.also(block: (T) -> Unit): T {

block(this)return this

}public inline fun <T, R> T.run(block: T.() -> R): R {

return block()}public inline fun <T, R> T.let(block: (T) -> R): R {

return block(this)}

33

Page 34: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Intermission

34

Page 35: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Inline functions

• В Котлине можно пометить функцию как inline

• Компилятор будет пробовать встраивать тело такой функции прямов место вызова

• Более того, он будет пробовать встраивать и переданные вinline-функцию лямбда-аргументы

• Это позволяет улучшить производительность за счет некоторыхдополнительных оптимизаций

• С другой стороны, это усложняет жизнь компилятору

• inline очень интересно дружит с дженериками, но об этом потом

35

Page 36: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Inline functions

public inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> {return flatMapTo(ArrayList<R>(), transform)

}

36

Page 37: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Inline functions

• Лямбда-аргументы у inline-функции встраиваются по умолчанию• Если мы хотим это запретить, используем noinline на аргументе

• Например, если хотим передать эту лямбду дальше или сохранитьее где-нибудь

class FunctionStorage(val functionResults: MutableMap<() -> Any, Any>)

inline fun FunctionStorage.run(noinline body: () -> Any) =functionResults[body] ?: kotlin.run {

val res = body()functionResults[body] = resres

}37

Page 38: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Inline functions

public inline fun <T, R> Iterable<T>.flatMap(/* noinline */ transform: (T) -> Iterable<R>): List<R> {return flatMapTo(ArrayList<R>(), transform) // wat?

}

• Из inline- в inline-функцию передача работает нормально

• Потому что мы просто встраиваем один вызов в другой

38

Page 39: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

How to return?

fun foo(angle: Double): Double {if (angle == 0.0) return Double.NaNreturn Math.sin(angle) + Math.cos(angle)

}

39

Page 40: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

How to return?

fun bar(angle: Double, f: (Double) -> Double): Double {return f(angle)

}

fun main(args: Array<String>) {bar(Math.PI) { angle ->

if (angle == 0.0) return Double.NaNreturn Math.sin(angle) + Math.cos(angle)// return is not allowed here// wat?

}}

40

Page 41: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

How to return?

fun bar(angle: Double, f: (Double) -> Double): Double {return f(angle)

}

fun main(args: Array<String>) {bar(Math.PI) { angle ->

if (angle == 0.0) return@bar Double.NaNreturn@bar Math.sin(angle) + Math.cos(angle)

}}

41

Page 42: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

How to return?

fun bar(angle: Double, f: (Double) -> Double): Double {return f(angle)

}

fun main(args: Array<String>) {bar(Math.PI, fun(angle): Double {

if (angle == 0.0) return Double.NaNreturn Math.sin(angle) + Math.cos(angle)

})}

А что для inline-функций?

42

Page 43: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

How to return?

inline fun bar(angle: Double, f: (Double) -> Double): Double {return f(angle)

}

fun main(args: Array<String>) {bar(Math.PI) { angle ->

if (angle == 0.0) return Double.NaNreturn Math.sin(angle) + Math.cos(angle)// everything is OK// wat?

}}

43

Page 44: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

How to return?

fun youShallNotPrintlnAfterEmptyStr(list: List<String>) {list.forEach { str ->

if (str.isEmpty()) return // Non-local returnprintln(str)

}}

fun youShallPrintlnAfterEmptyStr(list: List<String>) {list.forEach { str ->

if (str.isEmpty()) return@forEachprintln(str)

}}

44

Page 45: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Crossinline

public inline fun <T> Iterable<T>.forEachAsync(action: (T) -> Unit): Unit {for (element in this)

GlobalThreadPool.submit(Callable { action(element) })// Possible non-local return

}

45

Page 46: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Crossinline

public inline fun <T> Iterable<T>.forEachAsync(crossinline action: (T) -> Unit): Unit {for (element in this)

GlobalThreadPool.submit(Callable { action(element) })}

46

Page 47: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Sequences

• В Java 8 появились стримы — лениво вычисляемые потоки данных• В Котлине аналогичная штука называется Sequence

• Ленивый аналог Iterable• Можно делать все то же самое, но лениво!

47

Page 48: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Sequences

public interface Sequence<out T> {public operator fun iterator(): Iterator<T>

}

public fun <T : Any> generateSequence(nextFunction: () -> T?): Sequence<T> = ...

public fun <T : Any> generateSequence(seed: T?, nextFunction: (T) -> T?): Sequence<T> = ...

48

Page 49: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Basic FP built-ins

49

Page 50: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Juicy insides

public inline fun <T, R> Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R {var accumulator = initialfor (element in this)

accumulator = operation(accumulator, element)return accumulator

}

50

Page 51: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Juicy insides

public operator fun <T> Iterable<T>.plus(elements: Array<out T>): List<T> {if (this is Collection) return this.plus(elements)val result = ArrayList<T>()result.addAll(this)result.addAll(elements)return result

}

51

Page 52: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Juicy insides

public operator fun <T> Collection<T>.plus(elements: Array<out T>): List<T> {val result = ArrayList<T>(this.size + elements.size)result.addAll(this)result.addAll(elements)return result

}

52

Page 53: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

Juicy insides

public inline fun <T> List<T>.getOrElse(index: Int, defaultValue: (Int) -> T): T {return if (index >= 0 && index <= lastIndex) get(index)

else defaultValue(index)}

public fun <T> List<T>.getOrNull(index: Int): T? {return if (index >= 0 && index <= lastIndex) get(index)

else null}

53

Page 54: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

What I learned today?

54

Page 55: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

What I learned today?

• В Котлине есть функции высших порядков• Более того, есть функции с неявным this• А еще есть функции-расширения

Inline-функция-расширение, являющаяся оператором?Без проблем! ,

55

Page 56: Kotlin и ФП: то, что вы всегда хотели в Java, но боялись ......Kotlin и ФП: то, что вы всегда хотели в Java, но боялись

What’s next?

56