Skip to content

Commit

Permalink
Override map2Eval and combineKEval for monad transformers
Browse files Browse the repository at this point in the history
  • Loading branch information
LukaJCB committed Jun 5, 2020
1 parent 1f8cf3c commit 2e4b52f
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 3 deletions.
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/data/EitherT.scala
Expand Up @@ -1040,6 +1040,12 @@ private[data] trait EitherTSemigroupK[F[_], L] extends SemigroupK[EitherT[F, L,
case l @ Left(_) => y.value
case r @ Right(_) => F.pure(r)
})

override def combineKEval[A](x: EitherT[F, L, A], y: Eval[EitherT[F, L, A]]): Eval[EitherT[F, L, A]] =
Eval.now(EitherT(F.flatMap(x.value) {
case l @ Left(_) => y.value.value
case r @ Right(_) => F.pure(r: Either[L, A])
}))
}

private[data] trait EitherTFunctor[F[_], L] extends Functor[EitherT[F, L, *]] {
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/scala/cats/data/Kleisli.scala
Expand Up @@ -541,6 +541,9 @@ sealed private[data] trait KleisliSemigroupK[F[_], A] extends SemigroupK[Kleisli

override def combineK[B](x: Kleisli[F, A, B], y: Kleisli[F, A, B]): Kleisli[F, A, B] =
Kleisli(a => F.combineK(x.run(a), y.run(a)))

override def combineKEval[B](x: Kleisli[F, A, B], y: Eval[Kleisli[F, A, B]]): Eval[Kleisli[F, A, B]] =
Eval.now(Kleisli(a => F.combineKEval(x.run(a), y.map(_.run(a))).value))
}

sealed private[data] trait KleisliMonoidK[F[_], A] extends MonoidK[Kleisli[F, A, *]] with KleisliSemigroupK[F, A] {
Expand Down Expand Up @@ -622,6 +625,11 @@ private[data] trait KleisliApply[F[_], A] extends Apply[Kleisli[F, A, *]] with K
override def ap[B, C](f: Kleisli[F, A, B => C])(fa: Kleisli[F, A, B]): Kleisli[F, A, C] =
fa.ap(f)

override def map2Eval[B, C, Z](fa: Kleisli[F, A, B], fb: Eval[Kleisli[F, A, C]])(
f: (B, C) => Z
): Eval[Kleisli[F, A, Z]] =
Eval.now(Kleisli(a => F.map2Eval(fa.run(a), fb.map(_.run(a)))(f).value))

override def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] =
Kleisli(a => F.product(fb.run(a), fc.run(a)))
}
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/data/OptionT.scala
Expand Up @@ -540,6 +540,12 @@ private[data] trait OptionTSemigroupK[F[_]] extends SemigroupK[OptionT[F, *]] {
implicit def F: Monad[F]

def combineK[A](x: OptionT[F, A], y: OptionT[F, A]): OptionT[F, A] = x.orElse(y)

override def combineKEval[A](x: OptionT[F, A], y: Eval[OptionT[F, A]]): Eval[OptionT[F, A]] =
Eval.now(OptionT(F.flatMap(x.value) {
case oa @ Some(_) => F.pure(oa)
case None => y.value.value
}))
}

private[data] trait OptionTMonoidK[F[_]] extends MonoidK[OptionT[F, *]] with OptionTSemigroupK[F] {
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/data/WriterT.scala
Expand Up @@ -704,6 +704,9 @@ sealed private[data] trait WriterTSemigroupK[F[_], L] extends SemigroupK[WriterT

def combineK[A](x: WriterT[F, L, A], y: WriterT[F, L, A]): WriterT[F, L, A] =
WriterT(F0.combineK(x.run, y.run))

override def combineKEval[A](x: WriterT[F, L, A], y: Eval[WriterT[F, L, A]]): Eval[WriterT[F, L, A]] =
F0.combineKEval(x.run, y.map(_.run)).map(WriterT(_))
}

sealed private[data] trait WriterTMonoidK[F[_], L] extends MonoidK[WriterT[F, L, *]] with WriterTSemigroupK[F, L] {
Expand Down
22 changes: 19 additions & 3 deletions tests/src/test/scala/cats/tests/KleisliSuite.scala
Expand Up @@ -9,9 +9,7 @@ import cats.laws.discipline.arbitrary._
import cats.laws.discipline.eq._
import cats.laws.discipline.SemigroupalTests.Isomorphisms
import cats.laws.discipline.{DeferTests, MonoidKTests, SemigroupKTests}
import cats.syntax.flatMap._
import cats.syntax.functor._
import cats.syntax.traverse._
import cats.implicits._
import cats.platform.Platform
import cats.tests.Helpers.CSemi

Expand Down Expand Up @@ -321,6 +319,24 @@ class KleisliSuite extends CatsSuite {
result.run(()).value
}

test("map2Eval is lazy") {
var count = 0
val l = Kleisli { (n: Int) => count += 1; Option.empty[String] }

l.map2Eval(Eval.now(l))(_ + _).value.run(0)

count shouldBe 1
}

test("combineKEval is lazy") {
var count = 0
val l = Kleisli { (n: Int) => count += 1; Option(n.toString) }

l.combineKEval(Eval.now(l)).value.run(0)

count shouldBe 1
}

test("auto contravariant") {
trait A1
trait A2
Expand Down

0 comments on commit 2e4b52f

Please sign in to comment.