Search code examples
scalamonad-transformersscala-cats

Lift full types into monad transformers


I have these 3 monad transformers

type T[A] = OptionT[Future, A]
type E[A] = EitherT[Future, String, A]
type P[A] = OptionT[E, A]

I want to lift the corresponding full types (meaning exact corresponding type) into these. So for T, I want to lift Future[Option[Int]] into it. for E, I want to lift Future(Either[String, Int]) and for P, I want to lift (Future[Either[String, Option[Int]]]) into it.

I wrote this code and it compiles. except that I need a more succinct way of achieving the same thing.

val x : T[Int] = OptionT(Future(Option(10)))
val y : E[Int] = EitherT(Future(Right(10).asInstanceOf[Either[String, Int]]))
val z : P[Int] = OptionT(EitherT(Future(Right(Option(10)).asInstanceOf[Either[String, Option[Int]]])))

I am using Cats 1.1.0 and Scala 2.12.3.

The asInstanceOf thing is very annoying. but if I change the last line to

val z : P[Int] = OptionT(EitherT(Future(Right(Option(10)))))

I get this compiler error

[info] Compiling 1 Scala source to 
[error] /Users//code/dallasscalacats/src/main/scala/com//Transformers.scala:32: no type parameters for method apply: (value: F[Either[A,B]])cats.data.EitherT[F,A,B] in object EitherT exist so that it can be applied to arguments (scala.concurrent.Future[scala.util.Right[Nothing,Option[Int]]])
[error]  --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error]  found   : scala.concurrent.Future[scala.util.Right[Nothing,Option[Int]]]
[error]  required: ?F[Either[?A,?B]]
[error]     val z : P[Int] = OptionT(EitherT(Future(Right(Option(10)))))
[error]                              ^
[error] /Users//code/dallasscalacats/src/main/scala/com//Transformers.scala:32: type mismatch;
[error]  found   : scala.concurrent.Future[scala.util.Right[Nothing,Option[Int]]]
[error]  required: F[Either[A,B]]
[error]     val z : P[Int] = OptionT(EitherT(Future(Right(Option(10)))))
[error]                                            ^
[error] /Users//code/dallasscalacats/src/main/scala/com//Transformers.scala:32: type mismatch;
[error]  found   : cats.data.EitherT[F,A,B]
[error]  required: com.abhi.Transformers.E[Option[Int]]
[error]     (which expands to)  cats.data.EitherT[scala.concurrent.Future,String,Option[Int]]
[error]     val z : P[Int] = OptionT(EitherT(Future(Right(Option(10)))))
[error]                                     ^
[error] three errors found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 0 s, completed Jul 11, 2018 9:46:21 PM
>

Solution

  • You can use implicits method of import cats.implicits._ then you can write something like

    val z: P[Int] = OptionT(EitherT(Future(10.some.asRight[String])))
    

    and of course, You can write your own implicits as well

    implicit class EitherFuture[A, B](val e: Future[A Either B]) extends AnyVal {
      def asEitherT: EitherT[Future, A, B] = EitherT(e)
    }
    
    implicit class OptionEitherT[A](val e: EitherT[Future, String, Option[A]]) extends AnyVal {
      def asOptionT = OptionT(e)
    }
    
    val zz: P[Int] = Future(10.some.asRight[String]).asEitherT.asOptionT