Compose ReaderT with Either through for-comprehension and applicative pure

Compose ReaderT with Either through for-comprehension and applicative pure

以下是 return ReaderTEither 作为 return 类型的函数:

  import cats.data.{ReaderT}
  type FailFast[A] = Either[List[String], A]

  def getValue(name: String):ReaderT[FailFast, Map[String, String], String] =
    ReaderT((map) => map.get(name)
      .toRight(List(s"$name field not specified")))

  def nonNullable(name: String)(data: String): FailFast[String] =
    Right(data).ensure(List(s"$name cannot be nullable"))(_ != null)

  def nonBlank(name: String)(data: String): FailFast[String] =
    Right(data).ensure(List(s"$name cannot be blank"))(_.nonEmpty)

下面是这些函数的组合,运行良好:

  def readNotEmptyValue(name: String): ReaderT[FailFast, Map[String, String], String] =
    for {
      value <- getValue(name)
      _ <- ReaderT((_:Map[String, String]) => nonNullable(name)(value))
      res <- ReaderT((_:Map[String, String]) => nonBlank(name)(value))
    } yield res

我想摆脱这个 ReaderT.apply 调用,并通过 applicative pure 写一些东西:

  type Q[A] = ReaderT[FailFast, Map[String, String], A]
  import cats.syntax.applicative._
  import cats.instances.either._
  def readNotEmptyValue(name: String): ReaderT[FailFast, Map[String, String], String] =
    for {
      value <- getValue(name)
      _ <- nonBlank(name)(value).pure[Q]
      res <- nonBlank(name)(value).pure[Q]
    } yield res.right.get

不幸的是,最后一个解决方案不适用于负面案例。我肯定可以使用 match 来检查,天气是 RightLeft

但是有没有一种方法可以将其纯编写并最大程度地减少手动工作。如何正确操作?

而不是 Applicative.pure,您可以使用 EitherOps.liftTo 来消除 ReaderT.apply 的冗长:

def readNotEmptyValue(name: String): ReaderT[FailFast, Map[String, String], String] = 
  for {
    value <- getValue(name)
    _ <- nonBlank(name)(value).liftTo[Q]
    res <- nonBlank(name)(value).liftTo[Q]
  } yield res

否则,您仍在处理 FailFast[String] 而不是 String 的实例,如果您想尝试从 [=16= 中提取值,则没有办法绕过它].