Less Imperative with More Kotlin

Less Imperative with More Kotlin

Huyen's talk given at a Night Discussing Kotlin @ Foursquare HQ discussing basic definitions and terms related to Functional Programming and relating them to Kotlin.

5160e0e4d1e208d420c13805ea985e7f?s=128

Huyen Tue Dao

December 13, 2017
Tweet

Transcript

  1. LESS IMPERATIVE WITH MORE HUYEN TUE DAO @QUEENCODEMONKEY

  2. Alright. Let’s talk terms.

  3. IMPERATIVE PROGRAMMING a sequence of statements that change state.

  4. focuses on actions. explicit control flow. IMPERATIVE PROGRAMMING

  5. paradigm by which you describe how to do something. IMPERATIVE

    PROGRAMMING
  6. 100 REM FIND THE SUM OF THE TWO NUMBERS 200

    LET X=9 300 LET Y=6 400 LET Z=X+Y 500 PRINT X,Y,Z 600 END
  7. fun average(numbers: List<Int>): Float { var total = 0f for

    (i in 0 until numbers.size) { total += numbers[i] } return total / numbers.size }
  8. DECLARATIVE PROGRAMMING describe what you want to do.

  9. CAVEAT you or someone you encounter on StackOverflow may find

    this too fluffy, simplistic, imprecise, ambiguous. that’s okay. let’s keep this chill and practical.
  10. DECLARATIVE PROGRAMMING describe what you want to do.

  11. DECLARATIVE STYLE describe what you want to do.

  12. <html> <body> <h1>Heyo, Foursquare!</h1> <p>What's up?</p> </body> </html>

  13. SELECT * FROM [Builds] WHERE [Efficacy] = 'OP'

  14. fun average(numbers: List<Int>): Float {A var total = 0 forA(i

    in 0Auntil numbers.size) { total += numbers[i] } return total.toFloat() / numbers.size }A
  15. fun average(numbers: List<Int>): Float {A val total = numbers.sum() return

    total.toFloat() / numbers.size }A
  16. DECLARATIVE STYLE “…coding in the imperative style is like talking

    to a toddler.” Venkat Subramaniam
  17. DECLARATIVE STYLE “The declarative version is like speaking to a

    responsible adult.” Venkat Subramaniam
  18. Take your first left. Continue straight 2 miles. Slight right

    onto Raynor St. Left onto Kerrigan Ave. Continue straight .2 miles. DECLARATIVE IMPERATIVE My address is 8 Kerrigan Ave Reference, CO 88888 Tyler McGinnis “How do I get to your house from here?”
  19. fun average(numbers: List<Int>): Float {A var total = 0 forA(i

    in 0Auntil numbers.size) { total += numbers[i] } return total.toFloat() / numbers.size }A
  20. fun average(numbers: List<Int>): Float {A val total = numbers.sum() return

    total.toFloat() / numbers.size }A
  21. FUNCTIONAL PROGRAMMING

  22. FUNCTIONAL STYLE

  23. FUNCTIONAL PROGRAMMING some disagreement on exact definition.

  24. FUNCTIONAL PROGRAMMING style of programming based on evaluating functions

  25. FUNCTIONAL PROGRAMMING style of programming based on evaluating f: x

    → f(x)
  26. FUNCTIONAL PROGRAMMING style of programming based on evaluating expressions

  27. STATEMENTS EXPRESSIONS computations. actions.

  28. fun average(numbers: List<Int>): Float {A var total = 0 forA(i

    in 0Auntil numbers.size) { total += numbers[i] } return total.toFloat() / numbers.size }A
  29. fun average(numbers: List<Int>): Float {A val total = numbers.sum() return

    total.toFloat() / numbers.size }A
  30. fun average(numbers: List<Int>) = numbers.sum().toFloat() / numbers.size

  31. FUNCTIONAL PROGRAMMING mathematical, theoretical foundations. Category Theory. λ-calculus. many high-level

    and abstract concepts.
  32. Let’s start with basic and practical, exploring a different way

    of thinking
  33. var count = 1 fun calculate(x: Int, y: Int): Int

    {A count++ return x + y }A
  34. what are side effects? SIDE EFFECTS when a function makes

    modifications of outside state
  35. what are side effects? SIDE EFFECTS name sets them apart

    from main result of function.
  36. fun calculateAndCache(x: Int, y: Int): Int { val value =

    x + y val sharedPrefs = getPreferences(Context.MODE_PRIVATE) sharedPrefs .edit() .putInt("lastValue", value) .apply() return value }
  37. var count = 1 fun calculate(x: Int, y: Int): Int

    {A return x + y - count }A
  38. fun calculate(x: Int, y: Int): Int { val value =

    x + y + resources.getInteger(R.integer.some_important_integer) return value }
  39. when a function utilizes outside state or I/O SIDE CAUSES

    Kris Jenkins
  40. when a function makes modifications of outside state SIDE EFFECTS

  41. when a function interacts with the outside world SIDE EFFECTS

  42. SIDE EFFECTS “Wait. Isn’t that just programming?”

  43. SIDE EFFECTS apps need to interact with the outside world

  44. Here comes the “but”…

  45. make code harder to trace and understand. SIDE EFFECTS

  46. make code harder to test and debug. have to re-create

    the environment. SIDE EFFECTS
  47. therefore, can be a source of bugs. SIDE EFFECTS

  48. but we need them. so we want to limit and

    control them. SIDE EFFECTS
  49. FUNCTIONAL PROGRAMMING style of programming based on evaluating expressions

  50. FUNCTIONAL PROGRAMMING “Functional code is characterised by one thing: the

    absence of side effects.” Mary Rose Cook
  51. FUNCTIONAL PROGRAMMING “Every other ‘functional’ thing can be derived from

    this property.” Mary Rose Cook
  52. FUNCTIONAL PROGRAMMING No side effects. Immutability. Higher-order functions. Kotlin in

    Action
  53. FUNCTIONAL PROGRAMMING No side effects. Immutability. Higher-order functions. Kotlin in

    Action
  54. result depends only on the inputs. there are no side

    effects. PURE FUNCTIONS
  55. idempotent function that has the property that: f(f(x)) → f(x)

    PURE FUNCTIONS
  56. idempotent multiple calls with same inputs has same effect as

    one call. PURE FUNCTIONS
  57. var count = 0 fun increment() { count++ // count

    = 1 count++ // count = 2 count++ // count = 3 count++ // count = 4 }
  58. nullipotent calling function zero times has same effect as calling

    it once. PURE FUNCTIONS
  59. referential transparency an expression or function may be safely replaced

    by its value. PURE FUNCTIONS
  60. var count = 0 fun calculate(x: Int, y: Int): Int

    {A return x + y }A val result = calculate(1, 2) // result = 3, count = 0
  61. var count = 0 fun calculate(x: Int, y: Int): Int

    { return x + y }A val result = 3 // result = 3, count = 0
  62. var count = 0 fun calculate(x: Int, y: Int): Int

    {A count++ return x + y }A val result = calculate(1, 2) // result = 3, count = 1
  63. var count = 0 fun calculate(x: Int, y: Int): Int

    {A count++ return x + y }A val result = 3 // result = 3, count = 0
  64. referential transparency an expression or function may be safely replaced

    by its value. PURE FUNCTIONS
  65. Absence of side effects. PURE FUNCTIONS

  66. Encourages separation of concerns. PURE FUNCTIONS

  67. Easier to understand and test. PURE FUNCTIONS

  68. FUNCTIONAL PROGRAMMING No side effects. Immutability. Higher-order functions. Kotlin in

    Action
  69. FUNCTIONAL PROGRAMMING No side effects. Immutability. Higher-order functions. Kotlin in

    Action
  70. FUNCTIONAL PROGRAMMING No side effects. Immutability. Higher-order functions. Kotlin in

    Action
  71. So what are the benefits?

  72. Easier to understand.

  73. More concise.

  74. More maintainable.

  75. Easier testing.

  76. Safer multithreading.

  77. Less error-prone.

  78. FUNCTIONAL BITS OF

  79. FUNCTIONAL BITS OF function types + lambdas.

  80. FUNCTIONAL BITS OF data classes + immutable collections

  81. FUNCTIONAL BITS OF functional style APIs in the stdlib

  82. FUNCTIONAL BITS OF functional is not required. can live next

    to imperative and OOP code.
  83. Let’s start thinking functionally with .

  84. fun average(numbers: List<Int>): Float {A val total = numbers.sum() return

    total.toFloat() / numbers.size }A
  85. fun average(numbers: List<Int>) = numbers.sum().toFloat() / numbers.size

  86. fun average(numbers: List<Int>) = numbers.sum().toFloat() / numbers.size immutability

  87. fun average(numbers: List<Int>) = numbers.sum().toFloat() / numbers.size pure function

  88. fun average(numbers: List<Int>) = numbers.sum().toFloat() / numbers.size pure function depends

    only on input same inputs yield same output
  89. fun average(numbers: List<Int>) = numbers.sum().toFloat() / numbers.size

  90. fun average(numbers: List<Int>) = numbers.average()

  91. stdlib is full of functional operators.

  92. functional operators are awesome.

  93. let’s talk about mapping.

  94. val films = listOf( Movie("Beauty and the Beast", 2017, 504.1f),

    Movie("Guardians of the Galaxy Vol. 2", 2017, 389.8f), Movie("Dunkirk", 2017, 188.5f), Movie("Rogue One", 2016, 532.18f), Movie("Finding Dory", 2016, 486.30f), Movie("Deadpool", 2016, 363.07f), Movie("Fantastic Beasts and Where to Find Them", 2016, 234.04f), Movie("Hidden Figures", 2016, 169.39f), Movie("Star Wars: The Force Awakens", 2015, 936.66f), Movie("Jurassic World", 2015, 652.27f), Movie("Avengers: Age of Ultron", 2015, 459.01f), Movie("Inside Out", 2015, 356.46f) )
  95. fun getFilmTitles(films: List<Movie>): List<String> {A val names = mutableListOf<String>() for

    (film in films) { names.add(film.name) } return names }A
  96. fun getFilmTitles(films: List<Movie>): List<String> {A return films.map { film ->

    film.name } }A
  97. fun getFilmTitles(films: List<Movie>): List<String> {A return films.map { film ->

    film.name } }A
  98. Iterable<T>.map(transform: (T) -> R): List<R>

  99. extension function Iterable<T>.map(transform: (T) -> R): List<R>

  100. Iterable<T>.map(transform: (T) -> R): List<R> immutability

  101. Iterable<T>.map(transform: (T) -> R): List<R> higher-order function

  102. fun getFilmTitles(films: List<Movie>): List<String> {A val names = mutableListOf<String>() for

    (film in films) { names.add(film.name) } return names }A
  103. fun getFilmTitles(films: List<Movie>): List<String> {A return films.map { film ->

    film.name } }A let’s sprinkle on some sugar.
  104. fun getFilmTitles(films: List<Movie>) = films.map { it.name }

  105. fun getFilmTitles(films: List<Movie>) = films.map { it.name } expression body

    implicit single parameter name
  106. fun getFilmTitles(films: List<Movie>) = films.map { it.name }

  107. fun getFilmTitles(films: List<Movie>) = films.map(Movie::name) property reference

  108. let’s talk about folding.

  109. fun totalGross(films: List<Movie>): Float { var gross:Float = 0f for

    (film in films) { gross += film.gross } return gross }
  110. fun totalGross(films: List<Movie>): Float { return films.fold(0f) { gross, film

    -> gross + film.gross } }
  111. fun totalGross(films: List<Movie>): Float { return films.fold(0f) { gross, film

    -> gross + film.gross } }
  112. fun Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R

  113. fun Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R

    fun Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R pure function
  114. fun Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R

    extension function
  115. fun Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R

    immutability
  116. fun Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R

    higher-order function
  117. fun totalGross(films: List<Movie>): Float { return films.fold(0f) { gross, film

    -> gross + film.gross } }
  118. fun totalGross(films: List<Movie>): Float { return films.fold(0f) { gross, film

    -> gross + film.gross } }
  119. fun totalGross(films: List<Movie>): Float { return films.fold(0f) { gross, film

    -> gross + film.gross } }
  120. let’s sprinkle on some sugar. fun totalGross(films: List<Movie>): Float {

    return films.fold(0f) { gross, film -> gross + film.gross } }
  121. fun totalGross(films: List<Movie>) = films.sumByDouble { movie -> movie.gross.toDouble() }

  122. let’s talk about filtering.

  123. fun moviesFrom2017(films: List<Movie>): List<Movie> { val results = mutableListOf<Movie>() for

    (film in films) { if (film.year == 2017) { results.add(film) } } return results }
  124. fun moviesFrom2017(films: List<Movie>) = films.filter { it.year == 2017 }

  125. fun moviesFrom2017(films: List<Movie>) = films.filter { it.year == 2017 }

  126. fun Iterable<T>.filter(predicate: (T) -> Boolean): List<T>

  127. fun Iterable<T>.filter(predicate: (T) -> Boolean): List<T> pure function

  128. fun Iterable<T>.filter(predicate: (T) -> Boolean): List<T> immutability

  129. fun Iterable<T>.filter(predicate: (T) -> Boolean): List<T> higher-order function

  130. fun moviesFrom2017(films: List<Movie>) = films.filter { it.year == 2017 }

  131. println("2016 Films = ${films.filter { it.year == year }}")

  132. val greaterThan100M = { movie: Movie -> movie.gross > 100.0f

    } println("2016 Films = ${films.filter(greaterThan100M)}") starting to look like a strategy pattern
  133. val greaterThan100M = { movie: Movie -> movie.gross > 100.0f

    } println("2016 Films = ${films.filter(greaterThan100M)}") starting to look like a strategy pattern
  134. now for some composition.

  135. val names = films .filter { it.year == 2016 &&

    it.gross > 100f } .map { it.name } println("Name of 2016 Films Grossing More Than $100M = $names")
  136. experiment with creating declarative structures.

  137. interface Map<K, V> V computeIfAbsent(K key, Function<? super K,? extends

    V> mappingFunction) V computeIfPresent(K key, Function<? super K,? extends V> mappingFunction) let’s make our own and sprinkle on function types
  138. inline fun <K, V> MutableMap<K, V>.computeIf(key: K, predicate: (K, V?)

    -> Boolean, mappingFunction: (K, V?) -> V?): V? { val oldValue = get(key) if (!predicate(key, oldValue)) return oldValue val newValue = mappingFunction(key, oldValue) if (newValue == oldValue) return newValue if (newValue != null) put(key, newValue) else if (oldValue != null) remove(key) return newValue }
  139. inline fun <K, V> MutableMap<K, V>.computeIf(key: K, predicate: (K, V?)

    -> Boolean, mappingFunction: (K, V?) -> V?): V? { val oldValue = get(key) if (!predicate(key, oldValue)) return oldValue val newValue = mappingFunction(key, oldValue) if (newValue == oldValue) return newValue if (newValue != null) put(key, newValue) else if (oldValue != null) remove(key) return newValue } extension function
  140. inline fun <K, V> MutableMap<K, V>.computeIf(key: K, predicate: (K, V?)

    -> Boolean, mappingFunction: (K, V?) -> V?): V? { val oldValue = get(key) if (!predicate(key, oldValue)) return oldValue val newValue = mappingFunction(key, oldValue) if (newValue == oldValue) return newValue if (newValue != null) put(key, newValue) else if (oldValue != null) remove(key) return newValue } functions as inputs
  141. inline fun <K, V> MutableMap<K, V>.computeIf(key: K, predicate: (K, V?)

    -> Boolean, mappingFunction: (K, V?) -> V?): V? { val oldValue = get(key) if (!predicate(key, oldValue)) return oldValue val newValue = mappingFunction(key, oldValue) if (newValue == oldValue) return newValue if (newValue != null) put(key, newValue) else if (oldValue != null) remove(key) return newValue } all mutability hidden inside
  142. inline fun <K, V> MutableMap<K, V>.computeIf(key: K, predicate: (K, V?)

    -> Boolean, mappingFunction: (K, V?) -> V?): V? { val oldValue = get(key) if (!predicate(key, oldValue)) return oldValue val newValue = mappingFunction(key, oldValue) if (newValue == oldValue) return newValue if (newValue != null) put(key, newValue) else if (oldValue != null) remove(key) return newValue } blending programming paradigms
  143. cachedConfigs.computeIf( "KittiesOnDemandFeature", { key, previous -> previous?.json != kittyJson },

    { key, previous -> FeatureConfig.from(kittyJson) } )
  144. THANK YOU! SPEAKERDECK.COM/QUEENCODEMONKEY YOUTUBE.COM/ANDROIDDIALOGS RANDOMLYTYPING.COM HUYEN TUE DAO @QUEENCODEMONKEY

  145. val result = x + y computation

  146. val result = x + y action

  147. REFERENCES 147 Wikipedia: Functional Programming | wikipedia.org/wiki/Functional_programming Functional Programming |

    https://wiki.haskell.org/Functional_programming What Is Functional Programming? | blog.jenkster.com/2015/12/what-is-functional-programming.html | @krisajenkins An introduction to functional programming | codewords.recurse.com/issues/one/an-introduction-to-functional-programming | @maryrosecook Imperative vs. Declarative Programming | https://tylermcginnis.com/imperative-vs-declarative-programming/ | @tylermcginnis Imperative Style | http://blog.agiledeveloper.com/2015/07/the-imperative-style.html | @venkat_s Declarative Style | http://blog.agiledeveloper.com/2015/07/the-declarative-style.html | @venkat_s Functional Style | http://blog.agiledeveloper.com/2015/08/the-functional-style.html | @venkat_s Benefits of Pure Functions: Offer Referential Transparency | blog.agiledeveloper.com/2016/01/benefits-of-pure-functions-offer.html | @venkat_s Functional Programming Favors Expressions over Statements | http://blog.agiledeveloper.com/2015/08/functional-programming- favors.html | @venkat_s What is function programming? | https://www.quora.com/What-is-functional-programming
  148. REFERENCES 148 Kotlin in Action | manning.com/books/kotlin-in-action WikiWikiWeb | wiki.c2.com/

    Why Functional Programming Matters | https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf | John Hughes, The University, Glasgow Kotlin purity and function memoization | https://medium.com/@JorgeCastilloPr/kotlin-purity-and-function-memoization- b12ab35d70a5 Kotlin Functional Programming: Does it make sense? | medium.com/@JorgeCastilloPr/kotlin-functional-programming-does-it-make- sense-36ad07e6bacf | @JorgeCastilloPr Functional, Declarative, Imperative Programming [closed] | https://stackoverflow.com/questions/602444/functional-declarative-and- imperative-programming What are monads in functional programming and why are they useful? | https://www.quora.com/What-are-monads-in-functional- programming-and-why-are-they-useful-Are-they-a-generic-solution-to-the-problem-of-state-in-FP-or-Haskell-specific-Are-they- specific-to-Haskell-or-are-they-encountered-in-other-FP-languages