1
votes

I´m doing an example using monad transformer EitherT of scalaZ with OptionT but I have a compilation error which I dont understand.

Here my example code

class EitherTMonadTransformer {

  case class Error(msg: String)

  case class User(username: String, email: String)

  def authenticate(token: String): Future[Error \/ String] = Future {
    \/.right("token")
  }

  def getUser(username: String): Future[Option[User]] = Future {
    Some(User("paul", "[email protected]"))
  }

  val userObj: Future[\/[Error, Nothing]] =
    (for {
      username <- EitherT(authenticate("secret1234"))
      user <- OptionT(getUser(username))
    } yield user.username).run


  @Test
  def eitherTAndOptionT(): Unit = {
    println(userObj)
  }
}

The compilation error says

Error:(32, 12) type mismatch;
 found   : scalaz.OptionT[scala.concurrent.Future,String]
 required: scalaz.EitherT[scala.concurrent.Future,EitherTMonadTransformer.this.Error,?]
      user <- OptionT(getUser(username))

Any idea what´s wrong?

Regards.

1

1 Answers

4
votes

The problem is that, within a for expression, you cannot mix and match different monads as you wish. In this particular case you're trying to mix the OptionT monad with the EitherT one. Remember that monad transformers are themselves monad. The compiler, as soon as sees this line username <- EitherT(authenticate("secret1234")), will infer EitherT as the monad used in the for expression and expect it for the rest of it. One possible solution is changing the type returned by your getUser method, e.g.:

def getUser(username: String): Future[Error \/ User] = Future {
  \/.right(User("paul", "[email protected]"))
}

Of course you will have to change the for expression as follows too:

val userObj: Future[\/[Error, String]] =
  (for {
    username <- EitherT(authenticate("secret1234"))
    user <- EitherT(getUser(username))
  } yield user.username).run

This way the types align and the compiler will happily accept them.