Skip to content

Instantly share code, notes, and snippets.

@szoio

szoio/MonadT.scala

Last active May 25, 2020
Embed
What would you like to do?
Generic Monad Transformer
import cats._
import cats.effect.IO
import cats.implicits._
case class MonadT[F[_], G[_], A](value: F[G[A]])
object MonadT {
implicit def monad[F[_], G[_]](implicit MF: Monad[F], MG: Monad[G], TG: Traverse[G]): Monad[({ type T[A] = MonadT[F, G, A]})#T] = {
type T[A] = MonadT[F, G, A]
new Monad[T] {
override def pure[A](a: A): T[A] = MonadT(MF.pure(MG.pure(a)))
override def flatMap[A, B](fa: T[A])(f: A => T[B]): T[B] =
MonadT(MF.flatMap(fa.value)(ga => TG.flatTraverse(ga)(a => f(a).value)))
override def tailRecM[A, B](a: A)(f: A => T[Either[A, B]]): T[B] = ???
}
}
}
implicit class MonadTOps[F[_], G[_], A](fg: F[G[A]]) {
def asMonad: MonadT[F, G, A] = MonadT(fg)
}
// IO[Option[_]]
(for {
x <- IO(Option[Int](2)).asMonad
y <- IO(Option[Int](3)).asMonad
} yield x + y).value.unsafeRunSync
// IO[Either[Throwable, _]]
(for {
x <- IO(Either.right[Throwable, Int](6)).asMonad
y <- IO(Either.right[Throwable, Int](7)).asMonad
} yield x * y).value.unsafeRunSync
// IO[Either[Throwable, _]]
(for {
x <- IO(Either.left[Throwable, Int](new RuntimeException("errors out here."))).asMonad
y <- IO(Either.right[Throwable, Int](??? /* shouldn't be called */)).asMonad
} yield x * y).value.unsafeRunSync
// List[Option[_]]
(for {
x <- List(Option(11), Option(21), None).asMonad
y <- List(Option(x), Option(x + 1), Option(x + 2)).asMonad
} yield y).value
// Option[List[_]]
(for {
x <- Option(List(11, 21)).asMonad
y <- Option(List(x + 1, x + 2)).asMonad
} yield y).value
@szoio

This comment has been minimized.

Copy link
Owner Author

@szoio szoio commented May 25, 2020

I used the following libs:

  "org.typelevel" %% "cats-core" % "2.1.1",
  "org.typelevel" %% "cats-effect" % "2.1.3",
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment