Monoid 的错误累积
Errors accumulation with Monoid
假设我有一个函数列表 E => Either[Exception, Unit]
在事件 E
上调用并将错误累积到 return Either[List[Exception], Unit]
.
type EventHandler = E => Either[Exception, Unit]
import cats.data.NonEmptyList
def fire(
e: Event,
subscribers: List[EventHandler]
): Either[NonEmptyList[Exception], Unit] = ???
我想用 cats
实现 fire
import cats.implicits._
subscribers.foldMap (_ map (_.toValidatedNel))
.map (.toEither)
.apply(e)
有意义吗?你会如何改进它?
如何更改 fire
以同时调用 subscribers
?
我可能会这样写:
import cats.data.NonEmptyList, cats.implicits._
type Event = String
type EventHandler = Event => Either[Exception, Unit]
def fire(
e: Event,
subscribers: List[EventHandler]
): Either[NonEmptyList[Exception], Unit] =
subscribers.traverse_(_(e).toValidatedNel).toEither
(如果您不在 2.12.1 上或者在但不能使用 -Ypartial-unification
,您将需要 traverseU_
。)
如果您希望调用同时发生,通常您会达到 EitherT[Future, Exception, _]
,但这不会给您带来您想要的错误累积。没有 ValidatedT
,但那是因为 Applicative
是直接组合的。所以你可以这样做:
import cats.Applicative
import cats.data.{ NonEmptyList, ValidatedNel }, cats.implicits._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
type Event = String
type EventHandler = Event => Future[Either[Exception, Unit]]
def fire(
e: Event,
subscribers: List[EventHandler]
): Future[Either[NonEmptyList[Exception], Unit]] =
Applicative[Future].compose[ValidatedNel[Exception, ?]].traverse(subscribers)(
_(e).map(_.toValidatedNel)
).map(_.void.toEither)
(请注意,如果您不使用 kind-projector,则需要写出类型 lambda 而不是使用 ?
。)
并向自己证明它是同时发生的:
fire(
"a",
List(
s => Future { println(s"First: $s"); ().asRight },
s => Future { Thread.sleep(5000); println(s"Second: $s"); ().asRight },
s => Future { println(s"Third: $s"); ().asRight }
)
)
您会立即看到 First
和 Third
。
假设我有一个函数列表 E => Either[Exception, Unit]
在事件 E
上调用并将错误累积到 return Either[List[Exception], Unit]
.
type EventHandler = E => Either[Exception, Unit]
import cats.data.NonEmptyList
def fire(
e: Event,
subscribers: List[EventHandler]
): Either[NonEmptyList[Exception], Unit] = ???
我想用 cats
fire
import cats.implicits._
subscribers.foldMap (_ map (_.toValidatedNel))
.map (.toEither)
.apply(e)
有意义吗?你会如何改进它?
如何更改 fire
以同时调用 subscribers
?
我可能会这样写:
import cats.data.NonEmptyList, cats.implicits._
type Event = String
type EventHandler = Event => Either[Exception, Unit]
def fire(
e: Event,
subscribers: List[EventHandler]
): Either[NonEmptyList[Exception], Unit] =
subscribers.traverse_(_(e).toValidatedNel).toEither
(如果您不在 2.12.1 上或者在但不能使用 -Ypartial-unification
,您将需要 traverseU_
。)
如果您希望调用同时发生,通常您会达到 EitherT[Future, Exception, _]
,但这不会给您带来您想要的错误累积。没有 ValidatedT
,但那是因为 Applicative
是直接组合的。所以你可以这样做:
import cats.Applicative
import cats.data.{ NonEmptyList, ValidatedNel }, cats.implicits._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
type Event = String
type EventHandler = Event => Future[Either[Exception, Unit]]
def fire(
e: Event,
subscribers: List[EventHandler]
): Future[Either[NonEmptyList[Exception], Unit]] =
Applicative[Future].compose[ValidatedNel[Exception, ?]].traverse(subscribers)(
_(e).map(_.toValidatedNel)
).map(_.void.toEither)
(请注意,如果您不使用 kind-projector,则需要写出类型 lambda 而不是使用 ?
。)
并向自己证明它是同时发生的:
fire(
"a",
List(
s => Future { println(s"First: $s"); ().asRight },
s => Future { Thread.sleep(5000); println(s"Second: $s"); ().asRight },
s => Future { println(s"Third: $s"); ().asRight }
)
)
您会立即看到 First
和 Third
。