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

KotlinConf - Arrow's 2.0 Trajectory

KotlinConf - Arrow's 2.0 Trajectory

Since the first commit 10 years ago Arrow has travelled a long path to reach 2.0, this talk will take us through the journey, decisions, and challenges that shaped Arrow.

Arrow aims to bring functional programming patterns to Kotlin in an idiomatic way, by leveraging Kotlin's DSL features to represent complex programs in elegant ways.

Simon Vergauwen

April 16, 2023
Tweet

More Decks by Simon Vergauwen

Other Decks in Programming

Transcript

  1. @vergauwen_simon
    KotlinConf’23
    Amsterdam
    Arrow’s 2.0
    Trajectory
    Simon
    Vergauwen
    Xebia Functional

    View Slide

  2. ΛRROW
    Who am I?
    @vergauwen_simon

    View Slide

  3. Arrow’s 2.0 Trajectory
    Arrow’s
    history
    Complexity
    of FP
    Evolving
    (OSS) APIs
    Embracing
    Language
    Eco-system
    ΛRROW

    View Slide

  4. Arrow’s History
    Oct 2017
    March 2017
    FineCinnamon/Katz
    Dec 2017
    arrow-kt
    June 2017
    Kategory
    March 2013
    MarioAriasC/funKTionale
    Sept 2017
    nomisRev/optikal
    ΛRROW

    View Slide

  5. ΛRROW
    Arrow’s History
    Typeclasses &
    Higher Kinded
    Types
    < 0.12.x

    View Slide

  6. Complexity of Higher-Kinded Types
    interface GistsApi {


    fun publicGistsForUser(username: String): Kind>
    >

    }


    interface Logger {


    fun log(msg: String): Kind


    }
    ΛRROW

    View Slide

  7. Complexity of Higher-Kinded Types
    fun main() {


    GistsApi(MonoK.async(), MonoKLogger, HttpClient.newHttpClient())


    .publicGistsForUser("nomisRev")


    .fix()


    .mono


    .block()


    .let(
    ::
    println)


    }
    ΛRROW

    View Slide

  8. Complexity of Higher-Kinded Types
    fun main() {


    GistsApi(IO.async(),
    ...
    )
    //
    Arrow Fx IO


    GistsApi(DeferredK.async(),
    ...
    )
    //
    KotlinX Coroutines Deferred


    GistsApi(SingleK.async(),
    ...
    )
    //
    RxJava Single


    }
    ΛRROW

    View Slide

  9. Arrow’s History
    Typeclasses &
    Higher Kinded
    Types
    Rehaul of
    Arrow
    < 0.12.x
    0.12.x
    ΛRROW

    View Slide

  10. Complexity of FP
    Steep learning curve Non-idiomatic Foreign concepts,
    no language support
    ΛRROW

    View Slide

  11. Suspend to the rescue
    interface GistsApi {


    suspend fun publicGistsForUser(username: String): List


    }


    interface Logger {


    suspend fun log(msg: String): Unit


    }
    ΛRROW

    View Slide

  12. Suspend to the rescue
    fun main() {


    val api = GistApi(Logger.Default, HttpClient.newHttpClient())


    runBlocking { api.publicGistsForUser("nomisRev") }


    mono { api.publicGistsForUser("raulraja") }


    scope.future { api.publicGistsForUser("serras") }

    // ...

    }
    ΛRROW

    View Slide

  13. Arrow’s History
    Typeclasses &
    Higher Kinded
    Types
    Rehaul of
    Arrow
    < 0.12.x
    0.12.x
    Suspend &
    Kotlin
    Idiomatic
    1.x.x
    ΛRROW

    View Slide

  14. Wrapper specific code
    fun GithubUser.process(): Either =


    if (login.isNotEmpty()) ProcessedUser(this).right()


    else ProcessingFailed.left()
    ΛRROW

    View Slide

  15. Embracing DSLs
    fun GithubUser.process(): Either =


    either.eager {


    ensure(login.isNotEmpty()) { ProcessingFailed }


    ProcessedUser(this@process)


    }
    ΛRROW

    View Slide

  16. Traverse & co
    fun List.processAll(): Either>>
    =


    traverse(GithubUser
    ::
    process)
    ΛRROW

    View Slide

  17. Traverse = map + bind
    fun List.processAll(): Either>>
    =


    either.eager {


    map { it.process().bind() }


    }
    ΛRROW

    View Slide

  18. Arrow’s History
    Typeclasses &
    Higher Kinded
    Types
    Rehaul of
    Arrow
    < 0.12.x
    0.12.x
    Suspend &
    Kotlin
    Idiomatic
    Small Uniform
    APIs & learning
    curve
    1.x.x
    2.x.x
    ΛRROW

    View Slide

  19. DSLs and smart-contracts
    val res: Either = either {


    val x: Int? = null


    ensureNotNull(x) { "X was zero" }


    x + 1


    }
    ΛRROW

    View Slide

  20. Idiomatic API names
    inline fun Either.getOrElse(default: (A)
    ->
    B): B


    fun Either.getOrNull(): B?


    inline fun Either.onRight(action: (right: B)
    -
    >
    Unit): Either


    fun Either.leftOrNull(): A?


    inline fun Either.onLeft(action: (left: A)
    ->
    Unit): Either
    ΛRROW

    View Slide

  21. Context receivers
    context(Raise)


    fun GithubUser.process(): ProcessedUser {


    ensure(login.isNotEmpty()) { ProcessingFailed }


    return ProcessedUser(this)


    }



    context(Raise)


    fun List.processAll(): List =


    map { it.process() }
    ΛRROW

    View Slide

  22. DSLs everywhere
    suspend fun ResourceScope.dataSource(): HikariDataSource =


    closeable { HikariDataSource(HikariConfig()) }


    suspend fun SagaScope.example(): String =


    saga({ "Update user" }) { println("Rollback user") }
    ΛRROW

    View Slide

  23. Binary Size
    Arrow


    0.11.0
    Arrow


    1.0.0
    Arrow


    2.0.0
    5000
    4000
    3000
    2000
    1000
    0
    ΛRROW
    4,1 MB
    3,1 MB
    429 KB

    View Slide

  24. Evolving APIs
    Community
    feedback
    Iteration makes
    perfect
    Breaking


    Changes
    ΛRROW

    View Slide

  25. IntelliJ
    @Deprecated(


    RedundantAPI + "Prefer the new recover API",


    ReplaceWith("recover { a
    ->
    f(a).bind() }", "arrow.core.recover")


    )


    inline fun Either.handleErrorWith(f: (A)
    ->
    Either): Either
    ΛRROW

    View Slide

  26. IntelliJ ΛRROW

    View Slide

  27. OpenRewrite ΛRROW

    View Slide

  28. OpenRewrite
    plugins {


    kotlin("jvm")


    id("org.openrewrite.rewrite") version "5.39.3"


    }


    dependencies {


    rewrite(“io.arrow-kt:rewrite-arrow:1.0.0-RC3“)


    }


    rewrite {


    activeRecipe("arrow.RaiseRefactor")


    }
    ΛRROW

    View Slide

  29. OpenRewrite
    ./gradlew :sample:rewriteRun


    > Task :sample:rewriteRun


    Validating active recipes


    Parsing sources from project sample


    All sources parsed, running active recipes: arrow.RaiseRefactor


    Changes have been made to sample/src/main/kotlin/org/example/example.kt by:


    arrow.RaiseRefactor


    org.openrewrite.java.ChangeMethodName:
    ..
    .

    org.openrewrite.java.ChangeType:
    .
    ..


    Please review and commit the results.
    ΛRROW

    View Slide

  30. ΛRROW
    Large-scale automated refactoring


    Experimental Kotlin Support


    Support JVM & commonMain


    Friendly & helpful community


    First Arrow recipe
    OpenRewrite

    View Slide

  31. Documentation ΛRROW

    View Slide

  32. Migration Guide ΛRROW

    View Slide

  33. Eco System ΛRROW

    View Slide

  34. Community ΛRROW

    View Slide

  35. Community
    https://slack-chats.kotlinlang.org/c/arrow https://github.com/arrow-kt
    ΛRROW

    View Slide

  36. Visit us at the booth to talk
    to Arrow contributors &
    Xebia Functional and get
    discount on courses.

    __

    View Slide

  37. Thank you,

    and don’t forget

    to vote
    @vergauwen_simon
    KotlinConf’23
    Amsterdam

    View Slide