Kleisli Monad Transformer

Introduction

In Scalaz library, a wrapper exists for functions of type A => M[B] where M is a Monad called Kleisli. This blog post is an explanation about this wrapper.

Why need Kleisli?

As an example is always a great starting point to convey any concept, I will explain a simple use case where we can use Kleisli. As explained above, Kleisli is a wrapper for functions of type A => M[B] where M is a Monad. Imagine an enterprise application, which serves a REQUEST Url /foo/bar?a=1, and returns back response after talking to 3 Services S1, S2, S3 and performing 2 DB operations D1 and D2 in sequence. So we essentially do 5 IO operations to fulfill this client request. As you know, if a function has a side-effect it makes the function impure and makes it harder to compose. Functional composition is the core of doing functional programming. How do we get around this problem? Well the solution is to “Describe the IO Computation and then finally executing it”. We describe IO operations in an IO Monad[Yes, scalaz has an IO Monad type, by looking at the type, we know that there will be a side-effect], but the key take away is we just describe the IO computations and then combine/compose them and finally execute all of them at one shot using unsafePerformIO method on the IO Monad. Now coming back to the original problem, we are doing 5 IO operations in sequence, how do we compose/combine functions which take some input of type A and return IO[?]? Well, the answer is to use Kleisi as it is a wrapper for functions of type A => M[B]

Code Example

Below code sample shows a simple use case where”Given some Key we will get Some Name assuming that we need to make a database call to get Something details querying with the some key”:

DAO


import scalaz._
import Scalaz._

trait MSomething[M[_]] {
 def findSomethingByKey(key: String): M[Option[Something]]
}

object MSomething {
 def findSomethingByKey[M[_]](key: String)(implicit M: MSomething[M]): M[Option[Something]] =
 M.findSomethingByKey(key)
}

trait MSomethingMongo extends MSomething[({type λ[α] = Kleisli[IO, Configuration, α]})#λ] {
 def findSomethingByKey(key: String) =
 // You return a Kleisli Monad which wraps a function taking a config
 // and returns an IO Monad (config => IO).
 Kleisli(config =>
 IO {
 // Assume that we make an actual Mongo db call
 Some(
Something(id = key, name = s"the $key, queried from ${config.s}")
 )
 }
 )
}

object MSomethingMongo extends MSomethingMongo

Intermediate Layer accessing DAO


import scalaz._
import Scalaz._
import effect._

object Intermediate {
def get[M[+_]: Monad : MSomething] (somethingName: String): M[Option[Something]] = {
for {
something <- MSomething.findSomethingByKey[M](somethingName)
} yield something
}
}

Model


sealed case class Something(
  id: String,
  name: String
)
// AppConfig is like the uber configuration which every IO monad gets access to.
case class AppConfig(s: String)

Runner – Main Application


import scalaz._
import Scalaz._
import effect._
import kleisliEffect._

object Runner extends App {
  val appConfig = AppConfig("appConfig")
  println(MSomethingMongo.findSomethingByKey("Hello")(appConfig).unsafePerformIO())
  implicit val a = MSomethingMongo   

 type KIO[+A] = Kleisli[IO, AppConfig, A]

  val resultKleisli: KIO[Option[Something]]  = Intermediate.get[KIO]("Hello")
  println(resultKleisli(appConfig).unsafePerformIO())

}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s