Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Practical FP in Kotlin

Practical FP in Kotlin

[Droid Knights 2018](https://droidknights.github.io/2018/) 에서 발표한 [Practical FP in Kotlin] 발표 자료 입니다.

Sunghyun Hwang

April 22, 2018
Tweet

More Decks by Sunghyun Hwang

Other Decks in Programming

Transcript

  1. •Language Features (that aid) • Immutable Data • First Class

    Function • Tail Call Optimization •Programming Techniques (to write) • Map • Reduce • Recursion • Currying •Advantages (of Functional Program) • Parallelization • Lazy Evaluation • Determinism •Language Features (that aid) • Immutable Data • First Class Function • Tail Call Optimization •Programming Techniques (to write) • Map • Reduce • Recursion • Currying •Advantages (of Functional Program) • Parallelization • Lazy Evaluation • Determinism When “FP” is told
  2. When “FP” is told fun someFancyFunc(f: (Int) -> Int): Int

    { val immutable = random() tailrec fun fancy(n: Int, acc: Int): Int = if (n > 1) { fancy(n - 1, acc + f(n)) } else { acc } return fancy(immutable, 0) }
  3. fun someFancyFunc(f: (Int) -> Int): Int { val immutable =

    random() tailrec fun fancy(n: Int, acc: Int): Int = if (n > 1) { fancy(n - 1, acc + f(n)) } else { acc } return fancy(immutable, 0) } fun someFancyFunc(f: (Int) -> Int): Int { val immutable = random() tailrec fun fancy(n: Int, acc: Int): Int = if (n > 1) { fancy(n - 1, acc + f(n)) } else { acc } return fancy(immutable, 0) } When “FP” is told
  4. fun someFancyFunc(f: (Int) -> Int): Int { val immutable =

    random() tailrec fun fancy(n: Int, acc: Int): Int = if (n > 1) { fancy(n - 1, acc + f(n)) } else { acc } return fancy(immutable, 0) } fun someFancyFunc(f: (Int) -> Int): Int { val immutable = random() tailrec fun fancy(n: Int, acc: Int): Int = if (n > 1) { fancy(n - 1, acc + f(n)) } else { acc } return fancy(immutable, 0) } When “FP” is told
  5. fun someFancyFunc(f: (Int) -> Int): Int { val immutable =

    random() tailrec fun fancy(n: Int, acc: Int): Int = if (n > 1) { fancy(n - 1, acc + f(n)) } else { acc } return fancy(immutable, 0) } fun someFancyFunc(f: (Int) -> Int): Int { val immutable = random() tailrec fun fancy(n: Int, acc: Int): Int = if (n > 1) { fancy(n - 1, acc + f(n)) } else { acc } return fancy(immutable, 0) } When “FP” is told
  6. fun someFancyFunc(f: (Int) -> Int): Int { val immutable =

    random() tailrec fun fancy(n: Int, acc: Int): Int = if (n > 1) { fancy(n - 1, acc + f(n)) } else { acc } return fancy(immutable, 0) } fun someFancyFunc(f: (Int) -> Int): Int { val immutable = random() tailrec fun fancy(n: Int, acc: Int): Int = if (n > 1) { fancy(n - 1, acc + f(n)) } else { acc } return fancy(immutable, 0) } When “FP” is told
  7. fun main(args : Array<String>) { val f: (Int) -> Int

    = { i -> i * 2 } someFancyFunc(f) // 130 someFancyFunc(f) // 340 someFancyFunc(f) // 88 } fun main(args : Array<String>) { val f: (Int) -> Int = { i -> i * 2 } someFancyFunc(f) // 130 someFancyFunc(f) // 340 someFancyFunc(f) // 88 } When “FP” is told
  8. fun main(args : Array<String>) { val f: (Int) -> Int

    = { i -> i * 2 } someFancyFunc(f) // 130 someFancyFunc(f) // 340 someFancyFunc(f) // 88 } fun main(args : Array<String>) { val f: (Int) -> Int = { i -> i * 2 } someFancyFunc(f) // 130 someFancyFunc(f) // 340 someFancyFunc(f) // 88 } When “FP” is told
  9. fun main(args : Array<String>) { val f: (Int) -> Int

    = { i -> i * 2 } someFancyFunc(f) // 130 someFancyFunc(f) // 340 someFancyFunc(f) // 88 } fun main(args : Array<String>) { val f: (Int) -> Int = { i -> i * 2 } someFancyFunc(f) // 130 someFancyFunc(f) // 340 someFancyFunc(f) // 88 } When “FP” is told
  10. fun main(args : Array<String>) { val f: (Int) -> Int

    = { i -> i * 2 } someFancyFunc(f) // 130 someFancyFunc(f) // 340 someFancyFunc(f) // 88 } When “FP” is told
  11. Functional Code Functional code is characterized by one thing: the

    absence of side effects. It (a pure function) doesn’t rely on data outside the current function, and it doesn’t change data that exists outside the current function. [1]
  12. fun fibonacci(n: Int): Int { if (n <= 2) {

    return 1 } var a = 1 var b = 1 for (i in 2 until n) { val c = a + b a = b b = c } return b } Imperative Programming
  13. fun fibonacci(n: Int): Int { if (n <= 2) {

    return 1 } var a = 1 var b = 1 for (i in 2 until n) { val c = a + b a = b b = c } return b } fun fibonacci(n: Int): Int { if (n <= 2) { return 1 } var a = 1 var b = 1 for (i in 2 until n) { val c = a + b a = b b = c } return b } Imperative Programming
  14. fun fibonacci(n: Int): Int { if (n < 2) {

    return 1 } var a = 1 var b = 1 for (i in 2 until n) { val c = a + b a = b b = c } return b } fun fibonacci(n: Int): Int { if (n <= 2) { return 1 } var a = 1 var b = 1 for (i in 2 until n) { val c = a + b a = b b = c } return b } Imperative Programming
  15. Imperative programming is a programming paradigm that uses statements that

    change a program’s state. It consists of a series of commands for the computer to perform. It focuses on describing the details of how a program operates. [2]
  16. fun fibonacci(n: Int): Int { return if (n <= 2)

    { 1 } else { fibonacci(n-1) + fibonacci(n - 2) } } Declarative Programming
  17. Declarative programming is a programming paradigm that expresses the logic

    of a computation without describing its control flow. [3]
  18. Functional programming is a programming paradigm that treats computation as

    the evaluation of mathematical functions and avoids changing-state and mutable data. [4]
  19. •Language Features (that aid) • Immutable Data • First Class

    Function • Tail Call Optimization •Programming Techniques (to write) • Map • Reduce • Recursion • Currying •Advantages (of Functional Program) • Parallelization • Lazy Evaluation • Determinism •Language Features (that aid) • Immutable Data • First Class Function • Tail Call Optimization •Programming Techniques (to write) • Map • Reduce • Recursion • Currying •Advantages (of Functional Program) • Parallelization • Lazy Evaluation • Determinism When “FP” is told
  20. •Language Features (that aid) • Immutable Data • First Class

    Function • Tail Call Optimization •Programming Techniques (to write) • Map • Reduce • Recursion • Currying •Advantages (of Functional Program) • Parallelization • Lazy Evaluation • Determinism •Language Features (that aid) • Immutable Data • First Class Function • Tail Call Optimization •Programming Techniques (to write) • Map • Reduce • Recursion • Currying •Advantages (of Functional Program) • Parallelization • Lazy Evaluation • Determinism When “FP” is told
  21. 1. Writing pure functions is easy, but combining them into

    a complete application is where things get hard. (…) 4. Because you can’t mutate existing data, you instead use a pattern that I call, “Update as you copy.” 5. Pure functions and I/O don’t really mix. (…) [5] 1. Writing pure functions is easy, but combining them into a complete application is where things get hard. (…) 4. Because you can’t mutate existing data, you instead use a pattern that I call, “Update as you copy.” 5. Pure functions and I/O don’t really mix. (…) [5]
  22. interface PizzaView : PizzaRepository { fun getPizza(): Pizza = pizzas()[0]

    } interface PizzaView { val r: PizzaRepository fun getPizza(): Pizza = r.pizzas()[0] }
  23. interface PizzaView : PizzaRepository { fun getPizza(): Pizza = pizzas()[0]

    } interface PizzaView { val r: PizzaRepository fun getPizza(): Pizza = r.pizzas()[0] }
  24. val obj = object : PizzaView, PizzaRepositoryImpl {} val obj

    = object : PizzaView, PizzaRepositoryImpl { override val repository: PizzaRepository get() = this }
  25. val obj = object : PizzaView, PizzaRepositoryImpl {} val obj

    = object : PizzaView, PizzaRepositoryImpl { override val repository: PizzaRepository get() = this }
  26. 1. Writing pure functions is easy, but combining them into

    a complete application is where things get hard. (…) 4. Because you can’t mutate existing data, you instead use a pattern that I call, “Update as you copy.” 5. Pure functions and I/O don’t really mix. (…) [5] 1. Writing pure functions is easy, but combining them into a complete application is where things get hard. (…) 4. Because you can’t mutate existing data, you instead use a pattern that I call, “Update as you copy.” 5. Pure functions and I/O don’t really mix. (…) [5]
  27. 1. Writing pure functions is easy, but combining them into

    a complete application is where things get hard. (…) 4. Because you can’t mutate existing data, you instead use a pattern that I call, “Update as you copy.” 5. Pure functions and I/O don’t really mix. (…) [5] 1. Writing pure functions is easy, but combining them into a complete application is where things get hard. (…) 4. Because you can’t mutate existing data, you instead use a pattern that I call, “Update as you copy.” 5. Pure functions and I/O don’t really mix. (…) [5]
  28. "The I/O monad does not make a function pure. It

    just makes it obvious that it’s impure.” — Martin Odersky
  29. data class PizzaViewModel( val displayName: String, val color: Color )

    interface PizzaActivityLike : PizzaView { fun onGetPizzasButtonClicked( sth: Boolean ): IO<PizzaViewModel> = getPizzas().filter { /* sth */ }.map { p -> toPizzaViewModel(p) } }
  30. interface PizzaRepositoryImpl : PizzaRepository { override fun pizza(): Pizza {

    // What if no pizzas are available? return db.getPizzaFromDb() } }
  31. interface PizzaRepositoryImpl : PizzaRepository { override fun pizza(): Pizza? {

    return try { db.getPizzaFromDb() } catch (oop: OutOfPizzaException) { null } } }
  32. interface PizzaRepositoryImpl : PizzaRepository { override fun pizza(): Either<Pizza, Exception>

    { return try { Left(db.getPizzaFromDb()) } catch (oop: OutOfPizzaException) { Right(oop) } } }
  33. sealed class ShadowOfPizza data class RealPizza(val p: Pizza) : ShadowOfPizza()

    object OutOfPizza : ShadowOfPizza() interface PizzaRepositoryImpl : PizzaRepository { override fun pizza(): ShadowOfPizza { return try { RealPizza(db.getPizzaFromDb()) } catch (oop: OutOfPizzaException) { OutOfPizza } } }
  34. 1. Use interface always 2. Use only data class 3.

    Let impure jobs happen only in impure layers 4. Let exception happen only in impure layers 1. Use interface always 2. Use only data class 3. Let impure jobs happen only in impure layers 4. Let exception happen only in impure layers .map( )
  35. 1. Use interface always 2. Use only data class 3.

    Let impure jobs happen only in impure layers 4. Let exception happen only in impure layers 1. Use interface always 2. Use only data class 3. Let impure jobs happen only in impure layers 4. Let exception happen only in impure layers .map( )
  36. 1. Use interface always 2. Use only data class 3.

    Let impure jobs happen only in impure layers 4. Let exception happen only in impure layers 1. Use interface always 2. Use only data class 3. Let impure jobs happen only in impure layers 4. Let exception happen only in impure layers .map( )
  37. 1. Use interface always 2. Use only data class 3.

    Let impure jobs happen only in impure layers 4. Let exception happen only in impure layers 1. Use interface always 2. Use only data class 3. Let impure jobs happen only in impure layers 4. Let exception happen only in impure layers .map( )
  38. We are hiring! https://rainist.com/recruit/engineer • Back-end Engineer (Scala) • Data

    Engineer • Data Scientist • Android Engineer (Kotlin) • iOS Engineer • QA Engineer • Security Engineer • … X Engineer