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

FP for OOP Devs

Saúl Díaz
December 02, 2023

FP for OOP Devs

As OOP developers, we look with suspicions to Functional Programming; however we have been using it all along more than we think of.

We will learn the basics of FP and how we can leverage our OOP to improve over the solutions we've been providing for our problems; and finally we will learn of a certain concept of functional programming we've known from the beginning.

Saúl Díaz

December 02, 2023
Tweet

More Decks by Saúl Díaz

Other Decks in Technology

Transcript

  1. 3 fun sumAll(numbers: List<Int>) : Int { var result =

    0 for(number in numbers) { result += number } return result }
  2. 5 fun sumAll(numbers: List<Int>) : Int { return when(numbers.size) {

    1 -> numbers[0] 2 -> numbers[0] + numbers[1] 3 -> numbers[0] + numbers[1] + numbers[2] /* ... */ } }
  3. 6 tailrec fun sumAll(numbers: List<Int>, sum: Int = 0) :

    Int { return if (numbers.isEmpty()) { sum } else { sumAll( numbers.subList(0, numbers.lastIndex), sum + numbers.first() ) } }
  4. 8 Iterable<A>.map(transform: (A) -> B) : Iterable<B> Iterable<A>.zip(other: Collection<B>) :

    Iterable<Pair<A,B>> Iterable<A>.reduce(operation: (A, B) -> B): B Iterable<A>.fold(initial: B, operation: (A, B) -> B): B Iterable<A>.flatMap(operation: (A) -> Iterable<B>): List<B> DOUBLE STANDARDS
  5. 10 // With FP only fun sumAll(numbers: List<Int>) = numbers.fold(0)

    { number, sum -> number + sum } val result = sumAll(listOf(1, 2, 3)) // Adding OOP fun List<Int>.sum() = fold(0) { number, sum -> number + sum } val result = listOf(1, 2, 3).sum()
  6. fun printAllAttendees (conference: Conference): List<String> { val attendees = HashSet<Attendee>()

    for (talk in conference.talks) { for (attendee in talk.attendees) { attendees += attendee } } val names = ArrayList<String>() for (attendee in attendees) { names.add("${attendee.id} - ${attendee.name}") } return names } OOP is more readable
  7. fun printAllAttendees (conference: Conference): List<String> { val attendees = HashSet<Attendee>()

    for (talk in conference.talks) { for (attendee in talk.attendees) { attendees += attendee } } val names = ArrayList<String>() for (attendee in attendees) { names.add("${attendee.id} - ${attendee.name}") } return names } OOP is more readable familiar
  8. This can also be familiar, too fun printParticipants(conference: Conference): List<String>

    { return conference.talks .map { talk -> talk.attendees } .flatten() .toSet() .map { attendee -> "${attendee.id} - ${attendee.name}" } }
  9. 100% of the bugs are caused by source code Reducing

    LoCs also reduces review times and points of failure
  10. 17 As developers, we can only give solutions as good

    as the tools we have at our disposal
  11. 01 . All you wanted to know about FP* *

    And obviously you were too afraid to ask
  12. 19 History of Programming Paradigms 1957 FORTRAN Procedural programming COBOL

    would follow in 1959 and ALGOL in 1961 START 1947 A.R.C.2 Machine Assembly Kathleen and Andrew Donald Booth releases “Coding in A.R.C.”, which looks more like math; but it is the first mention to an “assembler” to program a machine.
  13. 20 1960 LISP Functional programming Based in the works of

    Lambda Calculus from 1930. 1967 SIMULA-1 & SMALLTALK Object-Oriented Programming Remember there’s still 16 years until C++ is released 28 years until Java is TBC
  14. 22 A function is a computation that has an input

    and an output “ f: (A) -> B
  15. Impure functions fun <E> pureListAdd(list: List<E>, element: E): List<E> =

    list + element fun <E> impureListAdd(list: MutableList<E>, element: E): List<E> { list.add(element) return list }
  16. 27 Higher order functions refers to the concept that a

    function can accept and/or return another function “ f: (A) -> B
  17. 28 Higher order functions refers to the concept that a

    function can accept and/or return another function “ f: ((C) -> D) -> (E)-> F
  18. Simple example fun List<Int>.incr(): List<Int> { val result = mutableListOf<Int>()

    this.forEach {number -> result.add(number + 1) } return result } fun List<Int>.sqr(): List<Int> { val result = mutableListOf<Int>() this.forEach {number -> result.add(number * number) } return result }
  19. Simple example interface Operation { fun execute(number: Int): Int }

    fun List<Int>.applyOp(op: Operation) : List<Int> { val result = mutableListOf<Int>() this.forEach {number -> result.add(op.execute(number)) } return result }
  20. Simple example class Incr : Operation { override fun execute(number:

    Int): Int = number + 1 } class Sqr: Operation { override fun execute(number: Int): Int = number * number }
  21. Simple example val incr = { number: Int -> number

    + 1 } val sqr = { number: Int -> number * number } fun List<Int>.applyOp(op: (Int) -> Int) : List<Int> { val result = mutableListOf<Int>() this.forEach {number -> result.add(op(number)) } return result } listOf(1,2,3).incr().sqr() // [4,9,16] listOf(1,2,3).applyOp(incr).applyOp(sqr) // [4,9,16]
  22. Simple example val incr = { number: Int -> number

    + 1 } val sqr = { number: Int -> number * number } val incrAndSqr = { number: Int -> sqr(incr(number))} listOf(1,2,3).incr().sqr() // [4,9, 16] listOf(1,2,3).applyOp(incr).applyOp(sqr) // [4,9, 16] listOf(1,2,3).applyOp(incrAndSqr) // [4,9, 16]
  23. map fun <A, B> List<A>.map(transform: (A) -> B) : List<B>

    { val result = mutableListOf<B>() forEach { element -> result.add(transform(element)) } return result } listOf(1,2,3).map { number -> number * 2 } // [2, 4, 6]
  24. map fun <A> id(a: A) = a val incr =

    { number: Int -> number + 1 } val string = { number: Int -> number.toString() } listOf(1,2,3).map(incr).map(string) // map(f) o map(g) - [“2”,”3”,”4”] listOf(1,2,3).map { number -> string(incr(number)) } // map(f o g) - [“2”,”3”,”4”] listOf(1,2,3).map(::id) == id(listOf(1,2,3))// map(id) == id - true listOf(1,2,3).map(incr).map(::id) // f o id == f - [2,3,4] listOf(1,2,3).map(::id).map(incr) // id o f == f - [2,3,4]
  25. zip fun <A,B> List<A>.zip(with: List<B>): List<Pair<A,B>> { val result =

    mutableListOf<Pair<A,B>>() for (i in 0.. min(this.lastIndex, with.lastIndex)) { result.add(this[i] to with[i]) } return result; }
  26. zip val nums = listOf(1, 2, 3) val letters =

    listOf("a", "b", "c") val lessLetters = listOf("a", "b") nums.zip(letters) // [(1, a), (2, b), (3, c)] nums.zip(lessLetters) // [(1, a), (2, b)]
  27. zip val greenRoomSessions: Effect<NetworkError, Sessions> = effect { api.requestTalksFrom(greenRoomId) }

    val purpleRoomSessions: Effect<NetworkError, Sessions> = effect { api.requestTalksFrom(purpleRoomId) } greenRoomSessions.zip(purpleRoomSessions) // Effect<NetworkError,<Sessions, Sessions> .map { (purpleSessions, greenSessions) -> /*....*/ }
  28. flatmap // map: ((A) -> B ) -> (([A]) ->

    [B]) // flatMap: ((A) -> [B]) -> (([A]) -> [B])
  29. flatmap fun <A, B> List<A>.flatMap(transform: (A) -> List<B>): List<B> {

    val result = mutableListOf<B>() this.forEach { element -> result.addAll(transform(element)) } return result }
  30. flatmap fun <A, B> combo(a: List<A>, b: List<B>) = a.map

    { elementA -> b.map { elementB -> elementA to elementB } } listOf(1,2,3).zip(listOf("a", "b", "c")) // [(1, a), (2, b), (3, c)] combo(listOf(1,2,3), listOf("a", "b", "c")) // [[(1, a), (1, b), (1, c)],...
  31. flatmap combo( listOf(1, 2, 3), listOf("a", "b", "c") ).flatMap {

    element: List<Pair<Int, String>> -> element.map { (num, letter) -> "$num$letter" } } // [1a, 1b, 1c, 2a...
  32. flatmap Effect { api.getSessionInfo(fpSessionId) } .flatmap { session -> Effect

    { api.getSpeakerInfo(session.speakerId) }} .map { speaker -> println(speaker.name)} // Saul Diaz
  33. A monad is a monoid in the category of endofunctors

    “ CONTAINER > same HAS MAP > >
  34. The monad // A monad is a Set (the mathematical

    one) such as // - Has an operation SxS -> S // - Has an element e: 1 -> S // That satisfies that // - a x (b x c) == (a x b) x c [associativity] // - e x a == a x e == a [left and right identity]
  35. Simple example // Left identity val number = 5 val

    monad = listOf(5) val double = { value: Int -> listOf(value * 2)} val result = monad.flatMap(double) val result2 = double(number) println("$result == $result2") // [10] == [10]
  36. Simple example // Right identity val wrapInList = { value:

    Int -> listOf(value) } val flatMapped = monad.flatMap(wrapInList) println("$monad == $flatMapped") // [5] == [5]
  37. Simple example // Associativity val multiplyBy5 = { value: Int

    -> listOf(value * 5)} val multiplyBy10 = { value: Int -> listOf(value * 10) } val chained = monad.flatMap(multiplyBy5).flatMap(multiplyBy10) val nested = monad.flatMap{ multiplyBy5(it).flatMap(multiplyBy10) } println("$nested == $chained") // [250] == [250]
  38. A monad is a container that wraps a type and

    provides a property the content does not have
  39. Examples of popular monads List<A>, Array<A> // The capability to

    store several values Set<A> // Stores multiple values that have no duplicates Optional<A>, Maybe<A> // The ability to handle nullability Either<E, R>, Result<E, R> // Short-circuits a computation if an error happens Validated<E, R> // Accumulates errors in a computation Eval<A> // Defers a computation Effect<E, R> // Handles a side effect with a suspend function
  40. All monads are created equal fun <A, B> List<A>.map(transform: (A)

    -> B): List<B> fun <A, B> Optional<A>.map(transform: (A) -> B): Optional<B> fun <A, B> Eval<A>.map(transform: (A) -> B): Eval<B> fun <E, R, C> Either<E, R>.map(transform: (R) -> C): Either<E, C> fun <E, R, C> Validated<E, R>.map(transform: (R) -> C): Validated<E, C>
  41. ▸ As developers, knowing more tools will improve the number

    and quality of solutions we can apply to problems ▸ Leveraging OOP with FP can result in concise, understandable code with less boilerplate ▸ As OOP developers, we’ve been using more FP that we realized
  42. 67