Skip to content

Instantly share code, notes, and snippets.

@marc0der
Last active February 18, 2022 14:17
Show Gist options
  • Select an option

  • Save marc0der/6012b751849661fd71927887ec6b1570 to your computer and use it in GitHub Desktop.

Select an option

Save marc0der/6012b751849661fd71927887ec6b1570 to your computer and use it in GitHub Desktop.

Revisions

  1. marc0der revised this gist Dec 22, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions monad.kt
    Original file line number Diff line number Diff line change
    @@ -27,13 +27,13 @@ fun <A, B> Option<A>.flatMap(f: (A) -> Option<B>): Option<B> =
    fun <A> Option.Companion.unit(a: A): Option<A> = Some(a)
    // extra
    fun <A> Option<A>.getOrElse(f: () -> A): A =
    when(this) {
    is Some -> this.get
    is None -> f()
    }
    */

    fun <A> remote(f: () -> A): A = if (Math.random() > 0.2) f() else throw IOException("boom!")
  2. marc0der created this gist Dec 22, 2021.
    48 changes: 48 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,48 @@
    # Functional error handling in Kotlin

    ## Agenda:

    * About the (simple) exercise
    * Higher Order Functions
    * Throwing exceptions: here be dragons!!
    * Sentinel values: better but not great
    * The `Option`: error as an ADT
    * Functional combinators: add some sugar
    * Trapping exceptions
    * Defer exception handling
    * Monad comprehensions with Arrow.kt

    ## Functional concepts in this session:

    * HOFs (Higher Order Functions)
    * Monad
    * Functor
    * Comprehension

    ## The problem domain

    Provide a function that performs a simple arithmetic calculation:

    ```kotlin
    fun calculate(a: Int, b: Int): Result<Int, Int>
    ```

    1. Multiply the two numbers (product)
    2. Add the two numbers (sum)
    3. Divide the product (dividend) by the sum (divisor)
    4. Calculate the quotient and remainder as `Result<Int, Int>`

    **All calculation steps to be performed over a dodgy network!!!**

    ### Example

    ```
    a = 11
    b = 5
    product = 55
    sum = 16
    quotient = 3
    remainder = 7
    ```
    81 changes: 81 additions & 0 deletions monad.kt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,81 @@
    package fpinkotlin

    import arrow.core.None
    import arrow.core.Option
    import arrow.core.Some
    import arrow.core.computations.option
    import arrow.core.getOrElse
    import java.io.IOException

    /*
    sealed class Option<out A> { companion object }
    data class Some<out A>(val get: A): Option<A>()
    object None : Option<Nothing>()
    // functor
    fun <A, B> Option<A>.map(f: (A) -> B): Option<B> =
    when(this) {
    is Some -> Option.unit(f(this.get))
    is None -> None
    }
    // monad
    fun <A, B> Option<A>.flatMap(f: (A) -> Option<B>): Option<B> =
    this.map { a -> f(a) }.getOrElse { None }
    fun <A> Option.Companion.unit(a: A): Option<A> = Some(a)
    fun <A> Option<A>.getOrElse(f: () -> A): A =
    when(this) {
    is Some -> this.get
    is None -> f()
    }
    */

    fun <A> remote(f: () -> A): A = if (Math.random() > 0.2) f() else throw IOException("boom!")

    fun <A> trap(f: () -> A): Option<A> =
    try {
    Some(f())
    } catch (e: Throwable) {
    None
    }

    typealias Result = Pair<Int, Int>

    fun calculate1(a: Int, b: Int): Option<Result> =
    multiply(a, b).flatMap { product ->
    add(a, b).flatMap { sum ->
    divide(product, sum).flatMap { quotient ->
    modulo(product, sum).map { rem ->
    quotient to rem
    }
    }
    }
    }

    suspend fun calculate2(a: Int, b: Int): Option<Result> =
    option {
    val product = multiply(a, b).bind()
    val sum = add(a, b).bind()
    val quotient = divide(product, sum).bind()
    val rem = modulo(product, sum).bind()
    quotient to rem
    }

    private fun multiply(a: Int, b: Int) = trap { remote { a * b } }

    private fun add(a: Int, b: Int) = trap { remote { a + b } }

    private fun divide(product: Int, sum: Int) = trap { remote { product / sum } }

    private fun modulo(product: Int, sum: Int) = trap { remote { product % sum } }

    suspend fun main() {
    println(calculate1(11, 5).getOrElse { "computer says no!" })
    println(calculate2(11, 5).getOrElse { "computer says no!" })
    }