Future[Either[Error, Option[User]]] 的 Monad 转换器

Monad transformer for Future[Either[Error, Option[User]]]

考虑 retrieveUser 的签名,其中检索不存在的用户未被建模为错误,也就是说,它被建模为 Future[Right[None]]:

def retrieveUser(email: String): Future[Either[Error, Option[User]]]

是否存在一个 monad 转换器 MT 这样我们就可以写

(for {
  user <- MT(retrieveUser(oldEmail))
  _    <- MT(updateUser(user.setEmail(newEmail)))
} {}).run

使用 EitherT 我能做的最好的是:

EitherT(retrieveUser(oldEmail)).flatMap {
  case Some(user) =>
    EitherT(updateUser(user.setEmail(newEmail)))

  case None => 
    EitherT.right(Future.successful({}))
}.run

问题是 EitherT(retrieveUser(email)) 上的映射会导致 Option[User],而不是未装箱的 User,这会破坏 for-comprehension。

我假设参数的顺序与 EitherT form Scala Cats 中的顺序相同:EitherT[F[_], A, B] 本质上只是 F[Either[A, B]].

的包装器

同样,OptionT[F, A]F[Option[A]] 的包装。

因此,

OptionT[EitherT[Future, Error, ?], A]

的包装
EitherT[Future, Error, Option[A]]

这又是一个包装器

Future[Either[Error, Option[A]]]

因此,

OptionT[EitherT[Future, Error, ?], User](
  EitherT[Future, Error, Option[User]](retrieveUser(oldEmail))
)

应该类型检查(使用 the non/kind-projector),并且使用 -Ypartial-unification 类型也应该自动推断,所以你可以尝试使用

OptionT(EitherT(retrieveUser(oldEmail))

在 for-comprehensions 里面。