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


IO Monad

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

Functional Effect System in Kotlin
2.65 GEEK