I want to use Cats EitherT
and OptionT
to handle the type Future[Either[Error, Option[T]]
. Suppose the following methods:
def findTeacher(id: Int): Future[Either[String, Option[Teacher]]]
def findSchool(teacher: Teacher): Future[Either[String, Option[School]]]
Now if I want to call them subsequently in a for-comprehension I can use EitherT
and OptionT
like this:
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
}
I wonder if it's possible to make it more concise maybe by combining OptionT
with EitherT
?
If understand your question correctly, you want to build a combined monad-transformer of EitherT
and OptionT
. Using Cats you may try something like this:
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
}
}
And then you may re-write your getSchoolByTeacherId
as
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
}
Unfortunately, even though OResult.apply
is implicit
you still have to write it explicitly in the first line of your for-comprehension but this allows skipping it on the further lines.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With