Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize Alternative (part 4): add prependK/appendK specializations for Cats monad transformers #4299

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions core/src/main/scala-2.13+/cats/data/ZipLazyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ object ZipLazyList {

def combineK[A](x: ZipLazyList[A], y: ZipLazyList[A]): ZipLazyList[A] =
ZipLazyList(cats.instances.lazyList.catsStdInstancesForLazyList.combineK(x.value, y.value))

override def prependK[A](a: A, fa: ZipLazyList[A]): ZipLazyList[A] =
ZipLazyList(cats.instances.lazyList.catsStdInstancesForLazyList.prependK(a, fa.value))

override def appendK[A](fa: ZipLazyList[A], a: A): ZipLazyList[A] =
ZipLazyList(cats.instances.lazyList.catsStdInstancesForLazyList.appendK(fa.value, a))
}

implicit def catsDataEqForZipLazyList[A: Eq]: Eq[ZipLazyList[A]] =
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala-2.13+/cats/data/ZipStream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ object ZipStream {

def combineK[A](x: ZipStream[A], y: ZipStream[A]): ZipStream[A] =
ZipStream(cats.instances.stream.catsStdInstancesForStream.combineK(x.value, y.value))

override def prependK[A](a: A, fa: ZipStream[A]): ZipStream[A] =
ZipStream(cats.instances.stream.catsStdInstancesForStream.prependK(a, fa.value))

override def appendK[A](fa: ZipStream[A], a: A): ZipStream[A] =
ZipStream(cats.instances.stream.catsStdInstancesForStream.appendK(fa.value, a))
}

implicit def catsDataEqForZipStream[A: Eq]: Eq[ZipStream[A]] =
Expand Down
64 changes: 58 additions & 6 deletions core/src/main/scala/cats/data/IndexedReaderWriterStateT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,17 @@ sealed abstract private[data] class IRWSTInstances1 extends IRWSTInstances2 {
}

sealed abstract private[data] class IRWSTInstances2 extends IRWSTInstances3 {
implicit def catsDataNonEmptyAlternativeForIRWST[F[_], E, L, S](implicit
FM: Monad[F],
FA: NonEmptyAlternative[F],
L0: Monoid[L]
): NonEmptyAlternative[IndexedReaderWriterStateT[F, E, L, S, S, *]] =
new RWSTNonEmptyAlternative[F, E, L, S] {
implicit def G: NonEmptyAlternative[F] = FA
implicit def F: Monad[F] = FM
implicit def L: Monoid[L] = L0
}

implicit def catsDataAlternativeForIRWST[F[_], E, L, S](implicit
FM: Monad[F],
FA: Alternative[F],
Expand Down Expand Up @@ -789,6 +800,10 @@ sealed abstract private[data] class RWSTMonad[F[_], E, L, S]

sealed abstract private[data] class IRWSTSemigroupK[F[_], E, L, SA, SB] extends IRWSTSemigroupK1[F, E, L, SA, SB]

sealed abstract private[data] class RWSTNonEmptyAlternative[F[_], E, L, S]
extends IRWSTFunctor[F, E, L, S, S]
with RWSTNonEmptyAlternative1[F, E, L, S]

sealed abstract private[data] class RWSTAlternative[F[_], E, L, S]
extends IRWSTFunctor[F, E, L, S, S]
with RWSTAlternative1[F, E, L, S]
Expand Down Expand Up @@ -821,8 +836,43 @@ private trait IRWSTSemigroupK1[F[_], E, L, SA, SB] extends SemigroupK[IndexedRea
}
}

private trait RWSTAlternative1[F[_], E, L, S]
private trait RWSTNonEmptyAlternative1[F[_], E, L, S]
Comment on lines -824 to +839
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if I missed it. Did we add law tests for this instance?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, no worries, here they are:

trait NonEmptyAlternativeLaws[F[_]] extends ApplicativeLaws[F] with SemigroupKLaws[F] {

trait NonEmptyAlternativeTests[F[_]] extends ApplicativeTests[F] with SemigroupKTests[F] {

Copy link
Member

@armanbilge armanbilge Oct 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I mean specifically for the IndexedReaderWriterStateT instance of NonEmptyAlternative.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accordingly, AlternativeLaws and AlternativeTests were changed to extend NonEmptyAlternative*:

trait AlternativeLaws[F[_]] extends NonEmptyAlternativeLaws[F] with MonoidKLaws[F] {

trait AlternativeTests[F[_]] extends NonEmptyAlternativeTests[F] with MonoidKTests[F] {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@armanbilge is it what you're asking about?

{
implicit val G: Monad[ListWrapper] = ListWrapper.monad
val SA = IRWST.catsDataAlternativeForIRWST[ListWrapper, Boolean, String, MiniInt](ListWrapper.monad,
ListWrapper.alternative,
Monoid[String]
)
checkAll(
"IndexedReaderWriterStateT[ListWrapper, String, String, Int, Int, *]",
AlternativeTests[IRWST[ListWrapper, Boolean, String, MiniInt, MiniInt, *]](SA).alternative[Int, Int, Int]
)
checkAll("Alternative[IndexedReaderWriterStateT[ListWrapper, String, String, Int, Int, *]]",
SerializableTests.serializable(SA)
)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That part of tests was not changed, though, iirc – it included tests for NonEmptyAlternativeLaws automatically due to the inheritance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks. I was asking, because for the other instances you added tests using ListWrapper.nonEmptyAlternative in addition to existing tests using the ListWrapper.alternative, as far as I can tell.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see... That's a fair concern.. And honestly, I don't remember why. There are three causes possible:

  1. I simply forgot to add it.
  2. I lost it after a series of successive rebases.
  3. Most likely, little green men came and stole that piece of code from the repo.

But I think, despite of the cause, it makes sense to return it back.

extends IRWSTSemigroupK1[F, E, L, S, S]
with NonEmptyAlternative[ReaderWriterStateT[F, E, L, S, *]] {

implicit def F: Monad[F]
def G: NonEmptyAlternative[F]
implicit def L: Monoid[L]

def pure[A](a: A): ReaderWriterStateT[F, E, L, S, A] =
ReaderWriterStateT.pure[F, E, L, S, A](a)

def ap[A, B](
ff: ReaderWriterStateT[F, E, L, S, A => B]
)(
fa: ReaderWriterStateT[F, E, L, S, A]
): ReaderWriterStateT[F, E, L, S, B] =
ff.flatMap(f => fa.map(f)(F))(F, L)

override def prependK[A](
a: A,
fa: IndexedReaderWriterStateT[F, E, L, S, S, A]
): IndexedReaderWriterStateT[F, E, L, S, S, A] =
IndexedReaderWriterStateT { (e, s) =>
G.prependK((L.empty, s, a), fa.run(e, s))
}

override def appendK[A](
fa: IndexedReaderWriterStateT[F, E, L, S, S, A],
a: A
): IndexedReaderWriterStateT[F, E, L, S, S, A] =
IndexedReaderWriterStateT { (e, s) =>
G.appendK(fa.run(e, s), (L.empty, s, a))
}
}

private trait RWSTAlternative1[F[_], E, L, S]
extends RWSTNonEmptyAlternative1[F, E, L, S]
with Alternative[ReaderWriterStateT[F, E, L, S, *]] {

implicit def F: Monad[F]
Expand All @@ -831,11 +881,13 @@ private trait RWSTAlternative1[F[_], E, L, S]

def empty[A]: ReaderWriterStateT[F, E, L, S, A] = ReaderWriterStateT.liftF(G.empty[A])

def pure[A](a: A): ReaderWriterStateT[F, E, L, S, A] = ReaderWriterStateT.pure[F, E, L, S, A](a)
override def pure[A](a: A): ReaderWriterStateT[F, E, L, S, A] =
super.pure(a)

def ap[A, B](
override def ap[A, B](
ff: ReaderWriterStateT[F, E, L, S, A => B]
)(fa: ReaderWriterStateT[F, E, L, S, A]): ReaderWriterStateT[F, E, L, S, B] =
ff.flatMap(f => fa.map(f)(F))(F, L)

)(
fa: ReaderWriterStateT[F, E, L, S, A]
): ReaderWriterStateT[F, E, L, S, B] =
super.ap(ff)(fa)
}
30 changes: 25 additions & 5 deletions core/src/main/scala/cats/data/IndexedStateT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -299,19 +299,25 @@ sealed abstract private[data] class IndexedStateTInstances extends IndexedStateT
}

sealed abstract private[data] class IndexedStateTInstances1 extends IndexedStateTInstances2 {
implicit def catsDataNonEmptyAlternativeForIndexedStateT[F[_], S](implicit
FM: Monad[F],
FA: NonEmptyAlternative[F]
): NonEmptyAlternative[IndexedStateT[F, S, S, *]] with Monad[IndexedStateT[F, S, S, *]] =
new IndexedStateTNonEmptyAlternative[F, S] { implicit def F = FM; implicit def G = FA }

implicit def catsDataMonadErrorForIndexedStateT[F[_], S, E](implicit
F0: MonadError[F, E]
): MonadError[IndexedStateT[F, S, S, *], E] =
new IndexedStateTMonadError[F, S, E] { implicit def F = F0 }
}

sealed abstract private[data] class IndexedStateTInstances2 extends IndexedStateTInstances3 {
implicit def catsDataSemigroupKForIndexedStateT[F[_], SA, SB](implicit
F0: Monad[F],
G0: SemigroupK[F]
): SemigroupK[IndexedStateT[F, SA, SB, *]] =
new IndexedStateTSemigroupK[F, SA, SB] { implicit def F = F0; implicit def G = G0 }
}

sealed abstract private[data] class IndexedStateTInstances2 extends IndexedStateTInstances3 {
implicit def catsDataMonadForIndexedStateT[F[_], S](implicit F0: Monad[F]): Monad[IndexedStateT[F, S, S, *]] =
new IndexedStateTMonad[F, S] { implicit def F = F0 }
}
Expand Down Expand Up @@ -507,14 +513,28 @@ sealed abstract private[data] class IndexedStateTContravariantMonoidal[F[_], S]
)
}

sealed abstract private[data] class IndexedStateTAlternative[F[_], S]
sealed abstract private[data] class IndexedStateTNonEmptyAlternative[F[_], S]
extends IndexedStateTMonad[F, S]
with Alternative[IndexedStateT[F, S, S, *]] {
def G: Alternative[F]
with NonEmptyAlternative[IndexedStateT[F, S, S, *]] {

def G: NonEmptyAlternative[F]

def combineK[A](x: IndexedStateT[F, S, S, A], y: IndexedStateT[F, S, S, A]): IndexedStateT[F, S, S, A] =
IndexedStateT[F, S, S, A](s => G.combineK(x.run(s), y.run(s)))(G)

override def prependK[A](a: A, fa: IndexedStateT[F, S, S, A]): IndexedStateT[F, S, S, A] =
IndexedStateT[F, S, S, A](s => G.prependK((s, a), fa.run(s)))(G)

override def appendK[A](fa: IndexedStateT[F, S, S, A], a: A): IndexedStateT[F, S, S, A] =
IndexedStateT[F, S, S, A](s => G.appendK(fa.run(s), (s, a)))(G)
}

sealed abstract private[data] class IndexedStateTAlternative[F[_], S]
extends IndexedStateTNonEmptyAlternative[F, S]
with Alternative[IndexedStateT[F, S, S, *]] {

def G: Alternative[F]

def empty[A]: IndexedStateT[F, S, S, A] =
IndexedStateT.liftF[F, S, A](G.empty[A])(G)
}
Expand Down
20 changes: 19 additions & 1 deletion core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,11 @@ sealed abstract private[data] class KleisliInstances2 extends KleisliInstances3
}

sealed abstract private[data] class KleisliInstances3 extends KleisliInstances4 {
implicit def catsDataNonEmptyAlternativeForKleisli[F[_], A](implicit
F0: NonEmptyAlternative[F]
): NonEmptyAlternative[Kleisli[F, A, *]] =
new KleisliNonEmptyAlternative[F, A] { def F: NonEmptyAlternative[F] = F0 }

implicit def catsDataMonoidKForKleisli[F[_], A](implicit F0: MonoidK[F]): MonoidK[Kleisli[F, A, *]] =
new KleisliMonoidK[F, A] { def F: MonoidK[F] = F0 }

Expand Down Expand Up @@ -642,9 +647,22 @@ sealed private[data] trait KleisliMonoidK[F[_], A] extends MonoidK[Kleisli[F, A,
override def empty[B]: Kleisli[F, A, B] = Kleisli.liftF(F.empty[B])
}

private[data] trait KleisliNonEmptyAlternative[F[_], A]
extends NonEmptyAlternative[Kleisli[F, A, *]]
with KleisliApplicative[F, A]
with KleisliSemigroupK[F, A] {
implicit def F: NonEmptyAlternative[F]

override def prependK[B](b: B, fa: Kleisli[F, A, B]): Kleisli[F, A, B] =
Kleisli(a => F.prependK(b, fa.run(a)))

override def appendK[B](fa: Kleisli[F, A, B], b: B): Kleisli[F, A, B] =
Kleisli(a => F.appendK(fa.run(a), b))
}

private[data] trait KleisliAlternative[F[_], A]
extends Alternative[Kleisli[F, A, *]]
with KleisliApplicative[F, A]
with KleisliNonEmptyAlternative[F, A]
with KleisliMonoidK[F, A] {
implicit def F: Alternative[F]
}
Expand Down
21 changes: 20 additions & 1 deletion core/src/main/scala/cats/data/Nested.scala
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ sealed abstract private[data] class NestedInstances3 extends NestedInstances4 {
}

sealed abstract private[data] class NestedInstances4 extends NestedInstances5 {
implicit def catsDataNonEmptyAlternativeForNested[F[_]: NonEmptyAlternative, G[_]: Applicative]
: NonEmptyAlternative[Nested[F, G, *]] =
new NestedNonEmptyAlternative[F, G] {
val FG: NonEmptyAlternative[λ[α => F[G[α]]]] = NonEmptyAlternative[F].compose[G]
}

implicit def catsDataApplicativeErrorForNested[F[_], G[_], E](implicit
F: ApplicativeError[F, E],
G0: Applicative[G]
Expand Down Expand Up @@ -322,9 +328,22 @@ private[data] trait NestedMonoidK[F[_], G[_]] extends MonoidK[Nested[F, G, *]] w
def empty[A]: Nested[F, G, A] = Nested(FG.empty[A])
}

private[data] trait NestedNonEmptyAlternative[F[_], G[_]]
extends NonEmptyAlternative[Nested[F, G, *]]
with NestedApplicative[F, G]
with NestedSemigroupK[F, G] {
def FG: NonEmptyAlternative[λ[α => F[G[α]]]]

override def prependK[A](a: A, fa: Nested[F, G, A]): Nested[F, G, A] =
Nested(FG.prependK(a, fa.value))

override def appendK[A](fa: Nested[F, G, A], a: A): Nested[F, G, A] =
Nested(FG.appendK(fa.value, a))
}

private[data] trait NestedAlternative[F[_], G[_]]
extends Alternative[Nested[F, G, *]]
with NestedApplicative[F, G]
with NestedNonEmptyAlternative[F, G]
with NestedMonoidK[F, G] {
def FG: Alternative[λ[α => F[G[α]]]]
}
Expand Down
25 changes: 24 additions & 1 deletion core/src/main/scala/cats/data/Tuple2K.scala
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ sealed abstract private[data] class Tuple2KInstances1 extends Tuple2KInstances2
def F: Alternative[F] = FF
def G: Alternative[G] = GG
}

implicit def catsDataFoldableForTuple2K[F[_], G[_]](implicit
FF: Foldable[F],
GF: Foldable[G]
Expand Down Expand Up @@ -175,6 +176,14 @@ sealed abstract private[data] class Tuple2KInstances2 extends Tuple2KInstances3
def F: MonoidK[F] = FF
def G: MonoidK[G] = GG
}
implicit def catsDataNonEmptyAlternativeForTuple2K[F[_], G[_]](implicit
FF: NonEmptyAlternative[F],
GG: NonEmptyAlternative[G]
): NonEmptyAlternative[λ[α => Tuple2K[F, G, α]]] =
new Tuple2KNonEmptyAlternative[F, G] {
def F: NonEmptyAlternative[F] = FF
def G: NonEmptyAlternative[G] = GG
}
}

sealed abstract private[data] class Tuple2KInstances3 extends Tuple2KInstances4 {
Expand Down Expand Up @@ -352,9 +361,23 @@ sealed private[data] trait Tuple2KMonoidK[F[_], G[_]]
Tuple2K(F.empty[A], G.empty[A])
}

sealed private[data] trait Tuple2KNonEmptyAlternative[F[_], G[_]]
extends NonEmptyAlternative[λ[α => Tuple2K[F, G, α]]]
with Tuple2KApplicative[F, G]
with Tuple2KSemigroupK[F, G] {
def F: NonEmptyAlternative[F]
def G: NonEmptyAlternative[G]

override def prependK[A](a: A, fa: Tuple2K[F, G, A]): Tuple2K[F, G, A] =
Tuple2K(F.prependK(a, fa.first), G.prependK(a, fa.second))

override def appendK[A](fa: Tuple2K[F, G, A], a: A): Tuple2K[F, G, A] =
Tuple2K(F.appendK(fa.first, a), G.appendK(fa.second, a))
}

sealed private[data] trait Tuple2KAlternative[F[_], G[_]]
extends Alternative[λ[α => Tuple2K[F, G, α]]]
with Tuple2KApplicative[F, G]
with Tuple2KNonEmptyAlternative[F, G]
with Tuple2KMonoidK[F, G] {
def F: Alternative[F]
def G: Alternative[G]
Expand Down
26 changes: 24 additions & 2 deletions core/src/main/scala/cats/data/WriterT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,15 @@ sealed abstract private[data] class WriterTInstances8 extends WriterTInstances9
}

sealed abstract private[data] class WriterTInstances9 extends WriterTInstances10 {
implicit def catsDataNonEmptyAlternativeForWriterT[F[_], L](implicit
F: NonEmptyAlternative[F],
L: Monoid[L]
): NonEmptyAlternative[WriterT[F, L, *]] =
new WriterTNonEmptyAlternative[F, L] {
implicit val F0: NonEmptyAlternative[F] = F
implicit val L0: Monoid[L] = L
}

implicit def catsDataMonoidKForWriterT[F[_], L](implicit F: MonoidK[F]): MonoidK[WriterT[F, L, *]] =
new WriterTMonoidK[F, L] {
implicit val F0: MonoidK[F] = F
Expand Down Expand Up @@ -739,10 +748,23 @@ sealed private[data] trait WriterTMonoidK[F[_], L] extends MonoidK[WriterT[F, L,
def empty[A]: WriterT[F, L, A] = WriterT(F0.empty)
}

sealed private[data] trait WriterTNonEmptyAlternative[F[_], L]
extends NonEmptyAlternative[WriterT[F, L, *]]
with WriterTSemigroupK[F, L]
with WriterTApplicative[F, L] {
implicit override def F0: NonEmptyAlternative[F]

override def prependK[A](a: A, fa: WriterT[F, L, A]): WriterT[F, L, A] =
WriterT(F0.prependK((L0.empty, a), fa.run))

override def appendK[A](fa: WriterT[F, L, A], a: A): WriterT[F, L, A] =
WriterT(F0.appendK(fa.run, (L0.empty, a)))
}

sealed private[data] trait WriterTAlternative[F[_], L]
extends Alternative[WriterT[F, L, *]]
with WriterTMonoidK[F, L]
with WriterTApplicative[F, L] {
with WriterTNonEmptyAlternative[F, L]
with WriterTMonoidK[F, L] {
implicit override def F0: Alternative[F]
}

Expand Down
4 changes: 4 additions & 0 deletions mima.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ ThisBuild / mimaBinaryIssueFilters ++= {
Seq(
exclude[MissingClassProblem]("cats.compat.compat$package"),
exclude[MissingClassProblem]("cats.compat.compat$package$")
) ++
Seq( // https://github.com/typelevel/cats/pull/4299
exclude[ReversedMissingMethodProblem]("cats.data.RWSTAlternative1.cats$data$RWSTAlternative1$$super$pure"),
exclude[ReversedMissingMethodProblem]("cats.data.RWSTAlternative1.cats$data$RWSTAlternative1$$super$ap")
)
}

Expand Down
Loading