猫:映射嵌套选项值
cats: mapping nested either of option value
我有以下功能:
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)
将映射到 IO(Left("???"))
,这不是我想要的。
是否有 EitherT
的替代公式,不带等同于 foo1
实现的匹配表达式?
更重要的是,使用 map/traverse/biTraverse/etc.
(并且不匹配任何 option/eithers)的实现会是什么样子?
p.s。这里的目的是为以下类型定义一个 "map" 函数:
trait Lookup[F[_], K, A] {
def get(key: K): F[Either[FormatError, Option[A]]]
}
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]
}
没有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))
有了MonadError
我们可以:
- 业务逻辑实现时去掉1个transformer,所以
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)
我有以下功能:
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)
将映射到 IO(Left("???"))
,这不是我想要的。
是否有
EitherT
的替代公式,不带等同于foo1
实现的匹配表达式?更重要的是,使用
map/traverse/biTraverse/etc.
(并且不匹配任何 option/eithers)的实现会是什么样子?
p.s。这里的目的是为以下类型定义一个 "map" 函数:
trait Lookup[F[_], K, A] {
def get(key: K): F[Either[FormatError, Option[A]]]
}
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]
}
没有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))
有了MonadError
我们可以:
- 业务逻辑实现时去掉1个transformer,所以
def foo
只需要 - 不要预先决定我们要如何处理错误,所以用户应该选择具体的
EitherT
:
OptionT
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)