1. Arrow 101 - Kotlin
funcional com Arrow
Conceitos sobre teoria da categoria e programação
funcional em Kotlin.
Leandro Borges Ferreira
2.
3. FP é sobre compor funções
fun greaterThanTenAndEven: (List<Int>) -> List<Int> =
{ list -> list.filter { num -> num > 3 && num % 2 == 0} }
4. Podemos melhorar
val greaterThanTen = { num : Int -> num > 3}
val even = { num : Int -> num % 2 == 0 }
val greaterThanTenAndEven : (Int) -> Boolean =
{ num -> greaterThanTen(num) && even(num) }
listOf<Int>().filter(greaterThanTenAndEven)
5. Funções parcialmente aplicadas
val filter: ((Int) -> Boolean, List<Int>) -> List<Int> =
{ f, list -> list.filter { num -> f(num) } }
val takeEvens = filter.partially1 { num -> num % 2 == 0 }
val takeGreaterThanTen = filter.partially1 { num -> num > 10 }
val takeEvensGreaterThanTen = takeEvens compose takeGreaterThanTen
//[1, 2, 5, 12] -> takeEvens -> [2, 12] -> takeGreaterThanTen -> [12]
takeEvensGreaterThanTen(listOf(1, 5, 12)) // Result: 12
6. Currying
val curriedFilter = filter.curried()
val takeEvens = curriedFilter({ num -> num % 2 == 0 })
val greaterThanTen = curriedFilter { num -> num > 10 }
val takeEvensGreaterThanTen = takeEvens compose greaterThanTen
7. DataTypes
val numOp : Option<Int> = Some(3)
numOp.map { num -> num + 3 }
numOp.getOrElse { throw Exception("Ops!") }
val none : Option<Int> = None
val num : Int? = 3
num?.plus(3)
num ?: throw Exception("Ops!")
Option
9. Either
val right: Either<String, Int> = Either.Right(5)
val left: Either<String, Int> =
Either.Left(“Putz, deu errado”)
val right: Either<String, Int> = 5.right()
either.fold({
//If left
}, {
//If right
})
10. Teoria da Categoria
Bolinhas conectadas por setinhas
Teoria da Categoria é a ciência da abstração e composição
Dr. Eugenia Cheng
15. Monoids - Arrow
@typeclass
interface Monoid<A> : TC {
fun empty(): A
fun combine(a: A, b: A): A
}
data class Color(val red: Int = 0,
val green: Int = 0,
val blue: Int = 0) {
companion object
}
16. Monoid
@instance(Color::class)
interface ColorMonoid: Monoid<Color> {
override fun empty(): Color = Color()
override fun combine(a: Color, b: Color): Color = Color(
red = min(a.red + b.red, 255),
green = min(a.green + b.green, 255),
blue = min(a.blue + b.blue, 255))
}
Color.monoid().run { Color() + Color() }
17. Option + Monoids
val numOp1 = Some(3)
val numOp2 = Some(5)
val numOp3 = Option.monoid(Int.monoid())
.run { numOp1 + numOp2 }
// numOp3 = Some(5)
18. Tipos de ordem maior
val stringList : List<String>
val list : List<A>
val stringA = A<String>
val aOfB = A<B>
Kind<A, B> = A<B>
20. Você já usa Functors
val stringList : List<String> = listOf()
val intList = stringList.map { word -> word.toInt() }
val floatList = intList.map { int -> int.toFloat() }
fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R>
fun <A, B> Kind<F, A>.map(f: (A) -> B): Kind<F, B>
Iterables em Kotlin se comportam como Functors
Compare as assinaturas
F<A> -> F<B>
21. Tudo pode ser mapeável
val eitherString: Either<Exception, String> = "either".right()
val eitherInt: Either<Exception, Int> =
eitherString.map { word -> word.toInt() }
val tryString : Try<String> = Try { "try!" }
val tryInt : Try<Int> = tryString.map { word -> word.toInt() }
val optionInt : Option<Int> =
Option.just("option").map { word -> word.toInt() }
22. Applicative
Aplica funções nos contêineres
Just:
fun <A> just(a: A): Kind<F, A>
fun <A> just(a: A): Option<A> = Option.just(a)
Exemplo
23. Applicative
Aplica funções nos contêineres
Ap:
fun <A, B> Kind<F, A>
.ap(ff: Kind<F, (A) -> B>): Kind<F, B>
Exemplo
val opFn : Option<(String)-> Int> = Option.just { word -> word.toInt() }
val opFnInt : Option<Int> = Option.just("String").ap(opFn)
24. Applicative Builder
val tuple = Option.applicative()
.tupled(
Option.just("First"),
Option.just("Second")
)
val map : Map<String, String> = mapOf()
val opWord1 = map["id1"].toOption()
val opWord2 = map["id2"].toOption()
Option.applicative()
.tupled(opWord1, opWord2)
.fix()
.map { (word1, word2) ->
//Só entro aqui se os dois possuem valor
}
26. Você já usa Monads
val intList : List<Int> = listOf<String>().flatMap {
listOf<Int>()
}
val intList2 : List<List<Int>> = listOf<String>().map {
listOf<Int>()
}
val intList2Flat : List<Int> = intList2.flatten()
27. Programar pensando nos
Wrappers
val sum10 : (Int) -> Int = { num : Int -> num + 10 }
val multBy5 : (Int) -> Int = { num : Int -> num * 5 }
val sum10MultBy5 : (Int) -> Int = multBy5 compose sum10
val numList : List<Int> = listOf()
numList.map { num -> sum10MultBy5(num) }
28. Programar pensando nos
Wrappers
val sum10 : (Int) -> Int = { num : Int -> num + 10 }
val multBy5 : (Int) -> Int = { num : Int -> num * 5 }
val sum10MultBy5 : (Int) -> Int = multBy5 compose sum10
fun <A> fnWithWrapper(fu: Functor<A>)
: (Kind<A, Int>) -> Kind<A, Int> = fu.lift { sum10MultBy5(it) }
val opFn = fnWithWrapper(Option.functor())
val result1 : Option<Int> = opFn(Option.just(5)).fix()
//Some(75)
val eiFn = fnWithWrapper(Either.functor<Exception>())
val result2 : Either<Exception, Int> = eiFn(Either.right(5)).fix()
//Right(75)
val listFn = fnWithWrapper(ListK.functor())
val result3 : ListK<Int> = listFn(listOf(5).k()).fix()
//ListK(75)
val obs = Observable.fromArray(1,2,3,4,5).k()
val obsFn = fnWithWrapper(ObservableK.functor())
val result4 : ObservableK<Int> = obsFn(obs).fix()
29. Monad Comprehensions
As vezes flatMaps ficam bem difíceis de se ler…
fun command() : Either<Nothing, List<String>>
val either: Either<Nothing, List<String>> =
command1().flatMap { result1 ->
command2(result1).flatMap { result2 ->
command3(result2).flatMap {
Either.right(listOf(“ok!"))
}
}
}
30. Monad Comprehensions
Comprehensions deixam o código mais “Flat"
fun command() : Either<Nothing, List<String>>
val eitherC: Either<Nothing, List<String>> =
Either.monad<Nothing>().binding {
val result1 = command1().bind()
val result2 = command2().bind()
val result3 = command3().bind()
result3
}.fix()
31. Monad Comprehensions
Comprehensions deixam o código mais “Flat"
fun command() : Either<Nothing, List<String>>
val eitherC: Either<Nothing, List<String>> =
Either.monad<Nothing>().binding {
val result1 = command1().bind()
val result2 = command2().bind()
val result3 = command3().bind()
result3
}.fix()