Потратив на это несколько часов, я вполне уверен, что по состоянию на март 2019 года эта функция непосредственно не реализована у кошек.Тем не менее, уже существующая catsDataMonadErrorFForEitherT
монада действительно позволяет реализовать ее в основном несложным образом.
implicit class EitherTFutureAdditions[A, B](et: EitherT[Future, A, B]) {
val me = EitherT.catsDataMonadErrorFForEitherT[Future, Throwable, A]
def recoverLeft(pf: PartialFunction[Throwable, A]): EitherT[Future, A, B] =
me.recoverWith[B](et) { case t: Throwable =>
EitherT.fromEither[Future](Left(pf(t)))
}
}
Я не уверен, каковы последствия для производительности построения монады в рамках универсальный неявный класс есть, но он работает.Если вам не нужен общий случай, вы можете заменить [A, B]
на явные типы.
Пока я занимался этим, я также написал recoverWithFlat
, handleErrorLeft
и handleErrorWithFlat
и упаковалвсе это в файл EitherTUtils.scala
// Place this in a new file and then use it like so:
//
// import EitherTUtils.EitherTFutureAdditions
//
// val et: EitherT[Future, String, Int] =
// EitherT(Future.failed[Either[String, Int]](new Exception("example")))
// et recoverLeft {
// case e: Exception => s"Failed with reason ${e.getMessage}"
// }
//
object EitherTUtils {
/**
* Convenience additions for recovering and handling Future.failed within an EitherT
*
* @see [[cats.ApplicativeError]] for recover, recoverWith, handleError, handleErrorWith, and attemptT
*
* @param et a Futured EitherT
* @tparam A the Either's left type
* @tparam B the Either's right type
*/
implicit class EitherTFutureAdditions[A, B](et: EitherT[Future, A, B]) {
val me = EitherT.catsDataMonadErrorFForEitherT[Future, Throwable, A]
/**
* Recover from certain errors from this EitherT's Future (if failed) by mapping them to the EitherT's
* left value.
*
* @see [[recoverWithFlat]] for mapping to an Either[Future, A, B]
*
* @see [[handleErrorWithFlat]] to handle any/all errors.
*/
def recoverLeft(pf: PartialFunction[Throwable, A]): EitherT[Future, A, B] =
me.recoverWith[B](et) {
case t: Throwable =>
EitherT.fromEither[Future](Left(pf(t)))
}
/**
* Recover from certain errors from this EitherT's Future (if failed) by mapping them to the EitherT's
* value.
*
* @see [[recoverLeft]] for mapping to an EitherT's left value.
*
* @see [[handleErrorWithFlat]] to handle any/all errors.
*/
def recoverWithFlat(pf: PartialFunction[Throwable, Either[A, B]]): EitherT[Future, A, B] =
me.recoverWith[B](et) {
case t: Throwable =>
EitherT.fromEither[Future](pf(t))
}
/**
* Handle any error from this EitherT's Future (if failed) by mapping them to the EitherT's left value.
*
* @see [[recoverWithFlat]] for handling only certain errors
*
* @see [[handleErrorLeft]] for mapping to the EitherT's left value
*/
def handleErrorLeft(pf: PartialFunction[Throwable, A]): EitherT[Future, A, B] =
me.handleErrorWith[B](et) { t =>
EitherT.fromEither[Future](Left[A, B](pf(t)))
}
/**
* Handle any error from this EitherT's Future (if failed) by mapping them to the EitherT's value.
*
* @see [[recoverWithFlat]] for handling only certain errors
*
* @see [[handleErrorLeft]] for mapping to the EitherT's left value
*/
def handleErrorWithFlat(pf: PartialFunction[Throwable, Either[A, B]]): EitherT[Future, A, B] =
me.handleErrorWith[B](et) { t =>
EitherT.fromEither[Future](pf(t))
}
}
}
Я подумал, что это может быть моим первым вкладом в кошек, но после нескольких часов навигации по макету библиотеки я понял, что изменения не будут тривиальными, и я неу меня еще нет уровня знаний, чтобы представлять их таким образом, чтобы не требовал значительной работы от других участников проекта.
Я могу попробовать еще раз, когда лучше пойму структуру библиотеки кошек.