Составьте ReaderT с помощью либо для понимания и аппликативного чистого - PullRequest
2 голосов
/ 23 марта 2019

Вот функции, которые возвращают ReaderT и Either в качестве типа возврата:

  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 вызова и написать что-то через аппликативную чистоту:

  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, чтобы проверить, погода это Right или Left.

Но есть ли способ составить его с чистым и минимизировать ручное усилие. Как это сделать правильно?

1 Ответ

2 голосов
/ 23 марта 2019

Вместо 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, и нет никакого способа обойти это, если вы хотите попытаться извлечь значение из FailFast.

...