Skip to content

Commit

Permalink
Removed and replaced inconsistent Parallel derivation for EitherT (#3777
Browse files Browse the repository at this point in the history
)

Co-authored-by: Lars Hupel <lars.hupel@mytum.de>
  • Loading branch information
djspiewak and larsrh authored Feb 9, 2021
1 parent 77b4d9c commit 59ec9c0
Showing 1 changed file with 59 additions and 1 deletion.
60 changes: 59 additions & 1 deletion core/src/main/scala/cats/data/EitherT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,34 @@ abstract private[data] class EitherTInstances extends EitherTInstances1 {
EitherT(F.defer(fa.value))
}

implicit def catsDataParallelForEitherTWithParallelEffect[M[_], E: Semigroup](implicit
@deprecated("This implicit provides inconsistent effect layering semantics; see #3776 for more discussion", "2.4.0")
def catsDataParallelForEitherTWithParallelEffect[M[_], E: Semigroup](implicit
P: Parallel[M]
): Parallel.Aux[EitherT[M, E, *], Nested[P.F, Validated[E, *], *]] =
accumulatingParallel[M, E]

/**
* An alternative [[Parallel]] implementation which merges the semantics of
* the outer Parallel (the F[_] effect) with the effects of the inner
* one (the Either). The inner Parallel has the semantics of [[Validated]],
* while the outer has the semantics of parallel ''evaluation'' (in most cases).
* The default Parallel for [[EitherT]], when the nested F also has a Parallel,
* is to strictly take the semantics of the nested F and to short-circuit any
* lefts (often, errors) in a left-to-right fashion, mirroring the semantics of
* [[Applicative]] on EitherT. This instance is different in that it will not
* ''short-circuit'' but instead accumulate all lefts according to the supplied
* [[Semigroup]], similar to Validated.
*
* {{{
* implicit val p: Parallel[EitherT[IO, Chain[Error], *]] = EitherT.accumulatingParallel
*
* val a = EitherT(IO(Chain(error1).asLeft[Unit]))
* val b = EitherT(IO(Chain(error2).asLeft[Unit]))
*
* (a, b).parTupled // => EitherT(IO(Chain(error1, error2).asLeft[Unit]))
* }}}
*/
def accumulatingParallel[M[_], E: Semigroup](implicit
P: Parallel[M]
): Parallel.Aux[EitherT[M, E, *], Nested[P.F, Validated[E, *], *]] =
new Parallel[EitherT[M, E, *]] {
Expand Down Expand Up @@ -956,6 +983,37 @@ abstract private[data] class EitherTInstances extends EitherTInstances1 {
}
}
}

implicit def catsDataParallelForEitherTWithParallelEffect2[M[_], E](implicit
P: Parallel[M]
): Parallel.Aux[EitherT[M, E, *], Nested[P.F, Either[E, *], *]] =
new Parallel[EitherT[M, E, *]] {
type F[x] = Nested[P.F, Either[E, *], x]

implicit val monadM: Monad[M] = P.monad
implicit val monadEither: Monad[Either[E, *]] = cats.instances.either.catsStdInstancesForEither

def applicative: Applicative[Nested[P.F, Either[E, *], *]] =
cats.data.Nested.catsDataApplicativeForNested(P.applicative, implicitly)

def monad: Monad[EitherT[M, E, *]] = cats.data.EitherT.catsDataMonadErrorForEitherT

def sequential: Nested[P.F, Either[E, *], *] ~> EitherT[M, E, *] =
new (Nested[P.F, Either[E, *], *] ~> EitherT[M, E, *]) {
def apply[A](nested: Nested[P.F, Either[E, *], A]): EitherT[M, E, A] = {
val mva = P.sequential(nested.value)
EitherT(Functor[M].map(mva)(x => x))
}
}

def parallel: EitherT[M, E, *] ~> Nested[P.F, Either[E, *], *] =
new (EitherT[M, E, *] ~> Nested[P.F, Either[E, *], *]) {
def apply[A](eitherT: EitherT[M, E, A]): Nested[P.F, Either[E, *], A] = {
val fea = P.parallel(eitherT.value)
Nested(P.applicative.map(fea)(x => x))
}
}
}
}

abstract private[data] class EitherTInstances1 extends EitherTInstances2 {
Expand Down

0 comments on commit 59ec9c0

Please sign in to comment.