结合EitherT和OptionT的Scalaz错误

时间:2017-10-22 17:59:50

标签: scala scalaz

我正在使用带有OptionT的monad变换器EitherT scalaZ做一个例子,但是我有一个我不理解的编译错误。

这是我的示例代码

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", "osmosis_paul@gmail.com"))
  }

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


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

编译错误说

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))

任何想法都错了吗?

问候。

1 个答案:

答案 0 :(得分:4)

问题在于,在for表达式中,您不能根据需要混合和匹配不同的monad。在这种特殊情况下,您尝试将OptionT monad与EitherT monad混合。请记住,monad变形金刚本身就是monad。一旦看到这一行username <- EitherT(authenticate("secret1234")),编译器就会将EitherT推断为for表达式中使用的monad,并期望它用于其余部分。一种可能的解决方案是更改getUser方法返回的类型,例如:

def getUser(username: String): Future[Error \/ User] = Future {
  \/.right(User("paul", "osmosis_paul@gmail.com"))
}

当然,您还必须按如下方式更改for表达式:

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

这种类型对齐,编译器会很乐意接受它们。