scala CATS:Map嵌套的任一选项值

eqoofvh9  于 2022-11-09  发布在  Scala
关注(0)|答案(3)|浏览(140)

我有以下功能:

def function(i: Int): IO[Either[String, Option[Int]]] = ???

我想要一个形式的函数:

def foo(either: Either[String, Option[Int]]): IO[Either[String, Option[Int]]]

我希望它具有以下行为:

def foo1(either: Either[String, Option[Int]])
: IO[Either[String, Option[Int]]] = either match {
  case Right(Some(i)) => bar(i)
  case Right(None) => IO.pure(None.asRight)
  case Left(s) => IO.pure(s.asLeft)
}

我想不那么明确地这样做,所以我尝试了EitherT:

def foo2(either: Either[String, Option[Int]]): 
  IO[Either[String, Option[Int]]] = {
    val eitherT = for {
      maybe <- EitherT.fromEither[IO](either)
      int <- EitherT.fromOption(maybe, "???")
      x <- EitherT(bar(int))
    } yield x

  eitherT.value
}

但这意味着Right(None)将被Map到IO(Left("???")),这不是我想要的。

  • 是否有EitherT的替代公式,但没有与foo1实现相同的匹配表达式?
  • 更重要的是,使用map/traverse/biTraverse/etc.(与任何选项/选项都不匹配)的实现会是什么样子?

附:这里的目的是为以下类型定义一个“map”函数:

trait Lookup[F[_], K, A] {
  def get(key: K): F[Either[FormatError, Option[A]]]
}
mlmc2os5

mlmc2os51#

不带match

import cats._
import cats.data._
import cats.implicits._

def bar[F[_] : Applicative](i: Int): F[Either[String, Option[Int]]] =
  (i + 1).some.asRight[String].pure[F]

def foo[F[_] : Applicative : Monad : FlatMap](either: Either[String, Option[Int]]): F[Either[String, Option[Int]]] =
  OptionT(EitherT(either.pure[F])).flatMap { i =>
    OptionT(EitherT(bar[F](i)))
  }.value.value

foo[Id](1.some.asRight)
//res0: cats.Id[Either[String,Option[Int]]] = Right(Some(2))
mgdq6dx1

mgdq6dx12#

import cats.Applicative
import cats.syntax.applicative._

def bar[F[_]](i: Int): F[Either[String, Option[Int]]] = ???

def foo[F[_] : Applicative](either: Either[String, Option[Int]]): F[Either[String, Option[Int]]] =
  either match {
    case Right(Some(i)) => bar(i)
    case a => a.pure[F]
  }
nukf8bse

nukf8bse3#

借助MonadError,我们可以:

  • 业务逻辑实现时去掉1个转换器,def foo中只需要OptionT
  • 不要预先决定我们希望如何处理错误,因此用户应该选择具体的EitherT
import cats._
import cats.data._
import cats.implicits._
import monix.eval._

type ErrorHandler[F[_]] = MonadError[F, String]

def bar[F[_] : ErrorHandler : Applicative](i: Int): F[Option[Int]] =
  if (i > 0) (i + 1).some.pure[F] else implicitly[ErrorHandler[F]].raiseError("error")

def foo[F[_] : ErrorHandler : Applicative : FlatMap](option: Option[Int]): F[Option[Int]] =
  OptionT(option.pure[F]).flatMap { i =>
    OptionT(bar[F](i))
  }.value

type Effect[A] = EitherT[Task, String, A]

import monix.execution.Scheduler.Implicits.global

foo[Effect](1.some).value.runSyncUnsafe()
//Either[String, Option[Int]] = Right(Some(2))
foo[Effect](0.some).value.runSyncUnsafe()
//Either[String, Option[Int]] = Left("error")
foo[Effect](none).value.runSyncUnsafe()
//Right(None)

相关问题