Search code examples
monad-transformersarrow-kt

How to call a polymorphic function which depends on multiple Typeclasses in Kotlin/Arrow


I've created a function which is polymorphic in the Monad it needs to use, instead it depends on the typeclass instances that exists for this Monad. It looks like this:

fun <M> M.logic(...): Kind<M, String>
      where M: MonadReader<M, Dependency>,
            M: Effect<M> =
   fx.monad() {
      val dependency = ask().bind()
      val response = effect { ...using dependency here... }.bind()
      response
   }

I'm using the MonadReader to get a dependency, and I'm using Effect, well, for effects. Now I assumed, that all I need is to use some Monad Transformers to get to this constellation of a Monad "at the end of the world" (i.e. in main()). Something like ReaderT<ForIO, Dependency, Unit>.

However, I can't seem to create a suitable M (or any context) to call this method on. How can I call this method on an exact monad that has the necessary typeclass instances?


Solution

  • This is the code snippet you're after:

    fun <M, F> M.logic(): Kind<F, String>
      where M: MonadReader<F, String>,
            M: Async<F> =
      fx.monad {
        val dependency = ask().bind()
        val response = effect { dependency }.bind()
        response
      }
    
    object Transformer: 
      Async<KleisliPartialOf<ForIO, String>> by ReaderT.async(IO.effect()),
      KleisliMonadReader<ForIO, String> by ReaderT.monadReader(IO.monad())
    

    Note that Async is what you're after, not the poorly named Effect. And you need two generics, one for the composition and another for the content.

    Transformer.run {
      logic()
    }
    

    The Async instance for Reader was added on January 2020, and will be available on the next release, either 0.10.5 or 0.11.0: https://github.com/arrow-kt/arrow/commit/6aaae6998de612eb0eec948697f1c477649230be