Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

ΛRROW Who am I? @vergauwen_simon

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Complexity of Higher-Kinded Types interface GistsApi { fun publicGistsForUser(username: String): Kind > } interface Logger { fun log(msg: String): Kind } ΛRROW

Slide 7

Slide 7 text

Complexity of Higher-Kinded Types fun main() { GistsApi(MonoK.async(), MonoKLogger, HttpClient.newHttpClient()) .publicGistsForUser("nomisRev") .fix() .mono .block() .let( :: println) } ΛRROW

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

Suspend to the rescue interface GistsApi { suspend fun publicGistsForUser(username: String): List } interface Logger { suspend fun log(msg: String): Unit } ΛRROW

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

Wrapper specific code fun GithubUser.process(): Either = if (login.isNotEmpty()) ProcessedUser(this).right() else ProcessingFailed.left() ΛRROW

Slide 15

Slide 15 text

Embracing DSLs fun GithubUser.process(): Either = either.eager { ensure(login.isNotEmpty()) { ProcessingFailed } ProcessedUser(this@process) } ΛRROW

Slide 16

Slide 16 text

Traverse & co fun List.processAll(): Either> = traverse(GithubUser :: process) ΛRROW

Slide 17

Slide 17 text

Traverse = map + bind fun List.processAll(): Either> = either.eager { map { it.process().bind() } } ΛRROW

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

DSLs and smart-contracts val res: Either = either { val x: Int? = null ensureNotNull(x) { "X was zero" } x + 1 } ΛRROW

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

DSLs everywhere suspend fun ResourceScope.dataSource(): HikariDataSource = closeable { HikariDataSource(HikariConfig()) } suspend fun SagaScope.example(): String = saga({ "Update user" }) { println("Rollback user") } ΛRROW

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Evolving APIs Community feedback Iteration makes perfect Breaking Changes ΛRROW

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

IntelliJ ΛRROW

Slide 27

Slide 27 text

OpenRewrite ΛRROW

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

ΛRROW Large-scale automated refactoring Experimental Kotlin Support Support JVM & commonMain Friendly & helpful community First Arrow recipe OpenRewrite

Slide 31

Slide 31 text

Documentation ΛRROW

Slide 32

Slide 32 text

Migration Guide ΛRROW

Slide 33

Slide 33 text

Eco System ΛRROW

Slide 34

Slide 34 text

Community ΛRROW

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

Thank you,
 and don’t forget
 to vote @vergauwen_simon KotlinConf’23 Amsterdam