使用Monoid积累错误

时间:2017-03-14 18:51:34

标签: scala concurrency monoids scala-cats

假设我有一个函数列表E => Either[Exception, Unit]可以在事件E上调用并累积错误以返回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] = ???

我希望fire

实施cats
 import cats.implicits._

 subscribers.foldMap (_ map (_.toValidatedNel))
            .map (.toEither)
            .apply(e)

有意义吗?你会如何改进它? 如何更改fire同时调用subscribers

1 个答案:

答案 0 :(得分:6)

我可能会这样写:

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 }
  )
)

您会立即看到FirstThird