我想用Cats EitherT
和OptionT
来处理Future[Either[Error, Option[T]]
类型。假设有以下方法:
def findTeacher(id: Int): Future[Either[String, Option[Teacher]]]
def findSchool(teacher: Teacher): Future[Either[String, Option[School]]]
现在,如果我想随后在for-comprehension中调用它们,我可以像这样使用EitherT
和OptionT
:
def getSchoolByTeacherId(id: Int): Future[Either[String, Option[School]]] = {
val result = for {
maybeTeacher <- EitherT(findTeacher(id))
schoolF = maybeTeacher.map(findSchool).getOrElse(Future.successful(Right(None)))
school <- EitherT(schoolF)
} yield {
school
}
result.value
}
我想知道是否有可能通过将OptionT
与EitherT
相结合使其更简洁?
如果正确理解你的问题,你想建立一个EitherT
和OptionT
的组合monad-transformer。使用Cats,您可以尝试这样的事情:
type FutureEither[X] = EitherT[Future, String, X]
type OResult[X] = OptionT[FutureEither, X]
object OResult {
implicit def apply[A](value: Future[Either[String, Option[A]]]): OResult[A] = OptionT[FutureEither, A](EitherT(value))
implicit class OResultOps[A](val value: OResult[A]) extends AnyVal {
@inline
def directValue: Future[Either[String, Option[A]]] = value.value.value
}
}
然后你可以重写你的getSchoolByTeacherId
import OResult._
def getSchoolByTeacherId(id: Int): Future[Either[String, Option[School]]] = {
val result = for {
teacher <- OResult(findTeacher(id))
school <- findSchool(teacher)
} yield school
result.directValue
}
不幸的是,即使OResult.apply
是implicit
,你仍然必须在你的理解的第一行明确地写它,但这允许在更多的线上跳过它。