Slide 1

Slide 1 text

Kotlin + Λrrow for FP Scala Devs

Slide 2

Slide 2 text

Lang Constructs

Slide 3

Slide 3 text

Declara'ons Scala | Kotlin ------------|-------------------- val | val var | var object | object trait | interface class | class def | fun trait | interface + | out - | in [A] | type | typealias match | when - | suspend (non blocking F -> A) - | with (scoped syntax)

Slide 4

Slide 4 text

Func%ons Scala object { def hello[A](a: A): String = s"hello $a" } Kotlin fun hello(a: A): String = "hello $a"

Slide 5

Slide 5 text

Nullable types (Lang backed Option) Scala val maybeString: Option = None Kotlin val maybeString: String? = null

Slide 6

Slide 6 text

Data classes Scala case class Person(name: String, age: Int) Kotlin data class Person(val name: String, val age: Int)

Slide 7

Slide 7 text

Sealed classes Scala sealed abstract class ErrorType case object MyError1 extends ErrorType case object MyError2 extends ErrorType case class MyError3(underlying: Throwable) extends ErrorType Kotlin sealed class ErrorType object MyError1 : ErrorType() object MyError2 : ErrorType() data class MyError3(val underlying: Throwable): ErrorType

Slide 8

Slide 8 text

Pa#ern Matching Scala errorType match { case MyError1 => ??? case MyError2 => ??? case MyError3(ex) => throw ex } Kotlin when (errorType) { is MyError1 => TODO() is MyError2 => TODO() is MyError3 => throw errorType.underlying //note smart cast }

Slide 9

Slide 9 text

object Scala object MySingleton Kotlin object MySingleton

Slide 10

Slide 10 text

Companion object Scala case class Person(name: String, age: Int) object Person {...} Kotlin data class Person(val name: String, val age: Int) { companion object { ... } }

Slide 11

Slide 11 text

Type aliases Scala type X = String Kotlin typealias X = String

Slide 12

Slide 12 text

Path Dependent Types Scala abstract class Foo[A] { type X = A } Kotlin abstract class Foo[A] { typealias X = A // will not compile }

Slide 13

Slide 13 text

Syntax Extensions Scala implicit class StringOps(value: String): AnyVal { def quote: String = s"--- $value" } Kotlin fun String.quote(): String = "--- $value"

Slide 14

Slide 14 text

Variance Scala class Option[+A] //A is covariant class Functio1[-I, +O] // I is contravariant class Leaf[A] // A is invariant Kotlin class Option //A is covariant class Functio1n // I is contravariant class Leaf // A is invariant

Slide 15

Slide 15 text

Variance constrains Scala def run[A, B <: A]: Nothing = ??? def run[A, B >: A]: Nothing = ??? //No Kotlin equivalent Kotlin fun run(): Nothing = TODO()

Slide 16

Slide 16 text

FP Libraries Scala : cats, scalaz Kotlin: Λrrow

Slide 17

Slide 17 text

Higher Kinded Types Scala class Service[F[_]] Kotlin class Service // No way to express kind shape F<_>

Slide 18

Slide 18 text

Higher Kinded Types Emula4on Scala class Option[+A] Kotlin class ForOption private constructor() typealias OptionOf = arrow.Kind inline fun OptionOf.fix(): Option = this as Option class Option : Kind>

Slide 19

Slide 19 text

Higher Kinded Types Emula4on Kotlin import arrow.higherkind @higherkind class Option : OptionOf

Slide 20

Slide 20 text

Implicit Resolu.on Scala import cats.Functor class Service[F[_]](implicit F: Functor[F]) { def doStuff: F[String] = F.pure("Hello Tagless World") } new Service[Option]

Slide 21

Slide 21 text

Implicit Resolu.on (Run.me) Kotlin import javax.inject import arrow.* import arrow.typeclasses.Functor @typeclass interface Service : TC { fun FF(): Functor fun doStuff(): Kind = FF().pure("Hello Tagless World") } val result: OptionOf = service().doStuff() van normalizedResult: Option = result.fix()

Slide 22

Slide 22 text

Implicit Resolu.on (Compile .me) Kotlin import javax.inject import arrow.* import dagger.* import arrow.dagger.instances.* import arrow.typeclasses.Functor class Service @Inject constructor(val FF: Functor) { fun doStuff(): Kind = FF.pure("Hello Tagless World") } @Singleton @Component(modules = [ArrowInstances::class]) interface Runtime { fun optionService(): Service companion object { val implicits : Implicits = DaggerRuntime.create() } } val result: OptionOf = Runtime.optionService().doStuff() van normalizedResult: Option = result.fix()

Slide 23

Slide 23 text

Higher Kinded Types Par1al applica1on Scala Monad[Either[String, ?]] Kotlin Monad> //Partial extensions are generated by @higherkind

Slide 24

Slide 24 text

Cartesian Builder Scala import cats.implicits._ case class Result(n: Int, s: String, c: Character) (Option(1), Option("2"), Option('3')).mapN { case (n, s, c) => Result(n, s, c) }

Slide 25

Slide 25 text

Cartesian Builder Kotlin import arrow.* import arrow.typeclasses.* data class Result(val n: Int, val s: String, val c: Character) Option.applicative().map(Option(1), Option("2"), Option('3'), { Result(n, s, c) })

Slide 26

Slide 26 text

Cartesian Builder Kotlin import arrow.* import arrow.typeclasses.* @generic data class Result(val n: Int, val s: String, val c: Character) Option.applicative().mapToResult(Option(1), Option("2"), Option('3')) // Option(Result(1, "2", '3'))

Slide 27

Slide 27 text

Monad Comprehensions Scala for { a <- Option(1) b <- Option(1) x <- Option(1) } yield a + b + c //Option(3)

Slide 28

Slide 28 text

Monad Comprehensions Kotlin import arrow.* import arrow.typeclasses.* Option.monad().binding { val a = Option(1).bind() val b = Option(1).bind() val c = Option(1).bind() a + b + c } //Option(3)

Slide 29

Slide 29 text

Arrow Monad Comprehensions Built atop Kotlin Corou.nes suspend fun bind(m: () -> Kind): B = suspendCoroutineOrReturn { c -> val labelHere = c.stackLabels // save the whole coroutine stack labels returnedMonad = flatMap(m(), { x: B -> c.stackLabels = labelHere c.resume(x) returnedMonad }) COROUTINE_SUSPENDED }

Slide 30

Slide 30 text

Arrow Monad Comprehensions binding import arrow.* import arrow.typeclasses.* Try.monad().binding { val a = Try { 1 }.bind() val b = Try { 1 }.bind() a + b } //Success(2)

Slide 31

Slide 31 text

Arrow Monad Comprehensions binding import arrow.* import arrow.typeclasses.* Try.monad().binding { val a = Try { 1 }.bind() val b = Try { 1 }.bind() a + b } //Success(2)

Slide 32

Slide 32 text

Arrow Monad Comprehensions bindingCatch import arrow.* import arrow.typeclasses.* Try.monad().bindingCatch { val a = Try { 1 }.bind() val b = Try { throw RuntimeException("BOOM") }.bind() a + b } //Failure(RuntimeException(BOOM))

Slide 33

Slide 33 text

Arrow Monad Comprehensions bindingStackSafe import arrow.* import arrow.typeclasses.* val tryMonad = Try.monad() fun stackSafeTestProgram(n: Int, stopAt: Int): Free = tryMonad.bindingStackSafe { val v = Try {n + 1}.bind() val r = if (v < stopAt) stackSafeTestProgram(M, v, stopAt).bind() else Try { v }.bind() r } stackSafeTestProgram(0, 50000).run(tryMonad) //Success(50000)

Slide 34

Slide 34 text

Arrow Monad Comprehensions binding(context: CoroutineContext) import arrow.* import arrow.typeclasses.* Try.monad().binding(UIThread) { //flatMap execution happens in the UI thread val a = IO { draw(1) }.bind() val b = IO { draw(1) }.bind() a + b }

Slide 35

Slide 35 text

Arrow already includes most things you need for FP • Basic Data Types (Try, Eval, Op5on, NonEmptyList, Validated, ...) • FP Type classes (Functor, Applica5ve, Monad...) • Op5cs (Lenses, Prisms, Iso...) • Generic (.tupled(), .tupleLabelled(), HListN, TupleN...) • Effects (Async, Effect, IO, DeferredK, ObservableK...) • MTL (FunctorFilter, TraverseFilter...) • Free (Free, FreeApplica5ve...)

Slide 36

Slide 36 text

Side projects • Ank: Doc type checker like tut • Helios: Json lib: Jawn port and based on arrow op9cs for its DSL • Kollect: Fetch port • Bow: Arrow for Swi@

Slide 37

Slide 37 text

Thanks for watching! • @raulraja • @47deg • Λrrow • Λrrow Tutorial Videos