Skip to content

Instantly share code, notes, and snippets.

@taisukeoe
Last active August 19, 2019 18:05
Show Gist options
  • Save taisukeoe/5b81778e582bc61f9a27a0863a1a2670 to your computer and use it in GitHub Desktop.
Save taisukeoe/5b81778e582bc61f9a27a0863a1a2670 to your computer and use it in GitHub Desktop.
MonadErrorのコレクションに対し、Error値を蓄積しながらsequenceする(Applicative合成する)方法
import cats.
import cats.implicits._
case class MyError(errorMsg: String)
object MyError {
implicit val s: Semigroup[MyError] = new cats.kernel.Semigroup[MyError] {
override def combine(t1: MyError, t2: MyError): MyError = MyError(t1.errorMsg + "\n" + t2.errorMsg)
}
}
val a: Either[MyError, String] = MyError("boom!").asLeft
val b: Either[MyError, String] = MyError("ouch!").asLeft
val list = List(a, b)
list.sequence
// catsEitherのApplicative合成(ap)はfail fast。最初のエラー値と共に失敗する。
// Left(MyError(boom!))
// このエラー値を蓄積する方法はないだろうか?
//ValidatedのApplicative.apの実装は、Semigroup.combineを利用して蓄積することを利用する
def meSequenceG[T[_]: Traverse, F[_], E: Semigroup, A](l: T[F[A]])(implicit ME: MonadError[F, E]): F[T[A]] =
ME.rethrow(l.traverse(_.attempt.map(_.toValidated)).map(_.sequence.toEither))
println(meSequenceG(list))
// Left(MyError(boom!
// ouch!))
//ValidatedのApplicative.apの実装と同様に、Error値を蓄積するEitherのApplicativeインスタンスを定義する
object AccumulativeEither {
def applicative[E](implicit S: Semigroup[E]): Applicative[({ type λ[AA] = Either[E, AA]})#λ] = new Applicative[({
type λ[A] = Either[E, A]
})#λ] {
override def pure[A](x: A): Either[E, A] = x.asRight
override def ap[A, B](ff: Either[E, A => B])(fa: Either[E, A]): Either[E, B] = (fa, ff) match {
case (Right(a), Right(f)) => f(a).asRight
case (Left(e1), Left(e2)) => S.combine(e2, e1).asLeft
case (Left(e), _) => e.asLeft
case (_, Left(e)) => e.asLeft
}
}
}
def meSequenceE[T[_], F[_], E, A](l: T[F[A]])(implicit ME: MonadError[F, E], S: Semigroup[E], TR: Traverse[T]): F[T[A]] =
ME.rethrow(l.traverse(_.attempt).map(TR.sequence(_)(AccumulativeEither.applicative)))
println(meSequenceE(list))
// Left(MyError(boom!
// ouch!))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment