A few weeks ago my friend Dmitry Karlinsky wrote an article about “Demystifying functional effect systems in Scala”, which was a fascinating read. In less that fifty lines of code Dmitry has built an IO Monad, similar to what users of ZIO/Cats-Effect/Monix are familiar with.
This article prompted me to start thinking what a similar solution would look like in Kotlin. Would that be even possible, having a type system which is in many ways simpler than the one Scala language has?
As an exercise, let’s try to build a similar system in Kotlin, and see where we end up.
_Note: all Scala code in this article is credited to _Dmitry Karlinsky
Let’s start with building our Kotlin IO Monad, which we’ll call KIO
, in a very un-imaginative manner.
Whereas in Scala we would do something like:
sealed trait TIO[+A] {
def flatMap[B](f: A => TIO[B]): TIO[B] = TIO.FlatMap(this, f)
def map[B](f: A => B): TIO[B] = flatMap(a => TIO.succeed(f(a)))
// a convenience operator for sequencing effects, where the result of the
// first effect is ignored
def *> [B](that: TIO[B]): TIO[B] = flatMap(_ => that)
}
object TIO {
// Effect constructor
case class Effect[+A](a: () => A) extends TIO[A]
// Effect combinator
case class FlatMap[A, B](tio: TIO[A], f: A => TIO[B]) extends TIO[B]
// constructor functions
def succeed[A](a: A): TIO[A] = Effect(() => a)
def effect[A](a: => A): TIO[A] = Effect(() => a)
}
view raw
tio-introducing-algebra.scala hosted with ❤ by GitHub
In Kotlin same algebra will look similar to this:
sealed class KIO<out A> {
fun <B> flatMap(f: (A) -> KIO<B>): KIO<B> {
return FlatMap(this, f)
}
fun <B> map(f: (A) -> B): KIO<B> {
return flatMap { a ->
Effect {
f(a)
}
}
}
}
data class Effect<out A>(val f: () -> A) : KIO<A>()
data class FlatMap<A, out B>(
val input: KIO<A>,
val f: (A) -> KIO<B>
) : KIO<B>()
view raw
kio-algebra.kt hosted with ❤ by GitHub
Sealed traits and case classes in Scala are quite similar to sealed classes and data classes in Kotlin, so the change isn’t dramatic.
I decided not to use single line declarations for map
and flatMap
in Kotlin, to add a bit more clarity on what is going on there.
Basically, what we’re saying is that our program can be described as a chain of events.
Those events can be one of two types:
SideEffect
that provides us some input from the outside world (notice it does not get anything from us — ()->A
InvokeFunction
— that provided a function and an input value, returns us some output — (A)->B
#kotlin #scala #function