diff --git a/core/src/main/scala/cats/CommutativeFlatMap.scala b/core/src/main/scala/cats/CommutativeFlatMap.scala new file mode 100644 index 0000000000..b7293de06b --- /dev/null +++ b/core/src/main/scala/cats/CommutativeFlatMap.scala @@ -0,0 +1,14 @@ +package cats + +import simulacrum.typeclass + +/** + * Commutative FlatMap. + * + * Further than a FlatMap, which just allows composition of dependent effectful functions, + * in a Commutative FlatMap those functions can be composed in any order, which guarantees + * that their effects do not interfere. + * + * Must obey the laws defined in cats.laws.CommutativeFlatMapLaws. + */ +@typeclass trait CommutativeFlatMap[F[_]] extends FlatMap[F] diff --git a/core/src/main/scala/cats/CommutativeMonad.scala b/core/src/main/scala/cats/CommutativeMonad.scala new file mode 100644 index 0000000000..369541142a --- /dev/null +++ b/core/src/main/scala/cats/CommutativeMonad.scala @@ -0,0 +1,14 @@ +package cats + +import simulacrum.typeclass + +/** + * Commutative Monad. + * + * Further than a Monad, which just allows composition of dependent effectful functions, + * in a Commutative Monad those functions can be composed in any order, which guarantees + * that their effects do not interfere. + * + * Must obey the laws defined in cats.laws.CommutativeMonadLaws. + */ +@typeclass trait CommutativeMonad[F[_]] extends Monad[F] with CommutativeFlatMap[F] diff --git a/core/src/main/scala/cats/arrow/Arrow.scala b/core/src/main/scala/cats/arrow/Arrow.scala index 080ea912d4..8ca2659b67 100644 --- a/core/src/main/scala/cats/arrow/Arrow.scala +++ b/core/src/main/scala/cats/arrow/Arrow.scala @@ -5,10 +5,16 @@ import cats.functor.Strong import simulacrum.typeclass -@typeclass trait Arrow[F[_, _]] extends Split[F] with Strong[F] with Category[F] { self => +/** + * Must obey the laws defined in cats.laws.ArrowLaws. + */ +@typeclass trait Arrow[F[_, _]] extends Category[F] with Strong[F] { self => /** - * Lift a function into the context of an Arrow + * Lift a function into the context of an Arrow. + * + * In the reference articles "Arrows are Promiscuous...", and in the corresponding Haskell + * library `Control.Arrow`, this function is called `arr`. */ def lift[A, B](f: A => B): F[A, B] @@ -20,6 +26,25 @@ import simulacrum.typeclass compose(swap, compose(first[A, B, C](fa), swap)) } - override def split[A, B, C, D](f: F[A, B], g: F[C, D]): F[(A, C), (B, D)] = + /** + * Create a new computation `F` that splits its input between `f` and `g` + * and combines the output of each. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * scala> import cats.arrow.Arrow + * scala> val toLong: Int => Long = _.toLong + * scala> val toDouble: Float => Double = _.toDouble + * scala> val f: ((Int, Float)) => (Long, Double) = Arrow[Function1].split(toLong, toDouble) + * scala> f((3, 4.0f)) + * res0: (Long, Double) = (3,4.0) + * }}} + * + * Note that the arrow laws do not guarantee the non-interference between the _effects_ of + * `f` and `g` in the context of F. This means that `f *** g` may not be equivalent to `g *** f`. + */ + @simulacrum.op("***", alias = true) + def split[A, B, C, D](f: F[A, B], g: F[C, D]): F[(A, C), (B, D)] = andThen(first(f), second(g)) } diff --git a/core/src/main/scala/cats/arrow/CommutativeArrow.scala b/core/src/main/scala/cats/arrow/CommutativeArrow.scala new file mode 100644 index 0000000000..d074298524 --- /dev/null +++ b/core/src/main/scala/cats/arrow/CommutativeArrow.scala @@ -0,0 +1,13 @@ +package cats +package arrow + +import simulacrum.typeclass + +/** + * In a Commutative Arrow F[_, _], the split operation (or `***`) is commutative, + * which means that there is non-interference between the effect of the paired arrows. + * + * Must obey the laws in CommutativeArrowLaws + */ +@typeclass trait CommutativeArrow[F[_, _]] extends Arrow[F] + diff --git a/core/src/main/scala/cats/arrow/Split.scala b/core/src/main/scala/cats/arrow/Split.scala deleted file mode 100644 index a4313bff46..0000000000 --- a/core/src/main/scala/cats/arrow/Split.scala +++ /dev/null @@ -1,24 +0,0 @@ -package cats -package arrow - -import simulacrum.typeclass - -@typeclass trait Split[F[_, _]] extends Compose[F] { self => - - /** - * Create a new `F` that splits its input between `f` and `g` - * and combines the output of each. - * - * Example: - * {{{ - * scala> import cats.implicits._ - * scala> import cats.arrow.Split - * scala> val toLong: Int => Long = _.toLong - * scala> val toDouble: Float => Double = _.toDouble - * scala> val f: ((Int, Float)) => (Long, Double) = Split[Function1].split(toLong, toDouble) - * scala> f((3, 4.0f)) - * res0: (Long, Double) = (3,4.0) - * }}} - */ - def split[A, B, C, D](f: F[A, B], g: F[C, D]): F[(A, C), (B, D)] -} diff --git a/core/src/main/scala/cats/data/Cokleisli.scala b/core/src/main/scala/cats/data/Cokleisli.scala index 4fdaf0bd37..34a835c16b 100644 --- a/core/src/main/scala/cats/data/Cokleisli.scala +++ b/core/src/main/scala/cats/data/Cokleisli.scala @@ -1,7 +1,7 @@ package cats package data -import cats.arrow.{Arrow, Category, Compose, Split} +import cats.arrow.{Arrow, Category, CommutativeArrow, Compose} import cats.functor.{Contravariant, Profunctor} import cats.{CoflatMap, Comonad, Functor, Monad} import scala.annotation.tailrec @@ -45,37 +45,27 @@ object Cokleisli extends CokleisliInstances { } private[data] sealed abstract class CokleisliInstances extends CokleisliInstances0 { - implicit def catsDataArrowForCokleisli[F[_]](implicit ev: Comonad[F]): Arrow[Cokleisli[F, ?, ?]] = - new CokleisliArrow[F] { def F: Comonad[F] = ev } - implicit def catsDataMonadForCokleisli[F[_], A]: Monad[Cokleisli[F, A, ?]] = new Monad[Cokleisli[F, A, ?]] { - def pure[B](x: B): Cokleisli[F, A, B] = - Cokleisli.pure(x) - - def flatMap[B, C](fa: Cokleisli[F, A, B])(f: B => Cokleisli[F, A, C]): Cokleisli[F, A, C] = - fa.flatMap(f) - - override def map[B, C](fa: Cokleisli[F, A, B])(f: B => C): Cokleisli[F, A, C] = - fa.map(f) - - def tailRecM[B, C](b: B)(fn: B => Cokleisli[F, A, Either[B, C]]): Cokleisli[F, A, C] = - Cokleisli({ (fa: F[A]) => - @tailrec - def loop(c: Cokleisli[F, A, Either[B, C]]): C = c.run(fa) match { - case Right(c) => c - case Left(bb) => loop(fn(bb)) - } - loop(fn(b)) - }) + implicit val catsDataCommutativeArrowForCokleisliId: CommutativeArrow[Cokleisli[Id, ?, ?]] = + new CokleisliArrow[Id] with CommutativeArrow[Cokleisli[Id, ?, ?]] { + def F: Comonad[Id] = Comonad[Id] } + implicit def catsDataMonadForCokleisli[F[_], A]: Monad[Cokleisli[F, A, ?]] = + new CokleisliMonad[F, A] + implicit def catsDataMonoidKForCokleisli[F[_]](implicit ev: Comonad[F]): MonoidK[λ[α => Cokleisli[F, α, α]]] = Category[Cokleisli[F, ?, ?]].algebraK } -private[data] sealed abstract class CokleisliInstances0 { - implicit def catsDataSplitForCokleisli[F[_]](implicit ev: CoflatMap[F]): Split[Cokleisli[F, ?, ?]] = - new CokleisliSplit[F] { def F: CoflatMap[F] = ev } +private[data] sealed abstract class CokleisliInstances0 extends CokleisliInstances1 { + implicit def catsDataArrowForCokleisli[F[_]](implicit ev: Comonad[F]): Arrow[Cokleisli[F, ?, ?]] = + new CokleisliArrow[F] { def F: Comonad[F] = ev } +} + +private[data] sealed abstract class CokleisliInstances1 { + implicit def catsDataComposeForCokleisli[F[_]](implicit ev: CoflatMap[F]): Compose[Cokleisli[F, ?, ?]] = + new CokleisliCompose[F] { def F: CoflatMap[F] = ev } implicit def catsDataProfunctorForCokleisli[F[_]](implicit ev: Functor[F]): Profunctor[Cokleisli[F, ?, ?]] = new CokleisliProfunctor[F] { def F: Functor[F] = ev } @@ -89,7 +79,32 @@ private[data] sealed abstract class CokleisliInstances0 { } } -private trait CokleisliArrow[F[_]] extends Arrow[Cokleisli[F, ?, ?]] with CokleisliSplit[F] with CokleisliProfunctor[F] { + + +private[data] class CokleisliMonad[F[_], A] extends Monad[Cokleisli[F, A, ?]] { + + def pure[B](x: B): Cokleisli[F, A, B] = + Cokleisli.pure(x) + + def flatMap[B, C](fa: Cokleisli[F, A, B])(f: B => Cokleisli[F, A, C]): Cokleisli[F, A, C] = + fa.flatMap(f) + + override def map[B, C](fa: Cokleisli[F, A, B])(f: B => C): Cokleisli[F, A, C] = + fa.map(f) + + def tailRecM[B, C](b: B)(fn: B => Cokleisli[F, A, Either[B, C]]): Cokleisli[F, A, C] = + Cokleisli({ (fa: F[A]) => + @tailrec + def loop(c: Cokleisli[F, A, Either[B, C]]): C = c.run(fa) match { + case Right(c) => c + case Left(bb) => loop(fn(bb)) + } + loop(fn(b)) + }) + +} + +private trait CokleisliArrow[F[_]] extends Arrow[Cokleisli[F, ?, ?]] with CokleisliCompose[F] with CokleisliProfunctor[F] { implicit def F: Comonad[F] def lift[A, B](f: A => B): Cokleisli[F, A, B] = @@ -108,17 +123,14 @@ private trait CokleisliArrow[F[_]] extends Arrow[Cokleisli[F, ?, ?]] with Coklei super[CokleisliProfunctor].dimap(fab)(f)(g) override def split[A, B, C, D](f: Cokleisli[F, A, B], g: Cokleisli[F, C, D]): Cokleisli[F, (A, C), (B, D)] = - super[CokleisliSplit].split(f, g) + Cokleisli(fac => f.run(F.map(fac)(_._1)) -> g.run(F.map(fac)(_._2))) } -private trait CokleisliSplit[F[_]] extends Split[Cokleisli[F, ?, ?]] { +private trait CokleisliCompose[F[_]] extends Compose[Cokleisli[F, ?, ?]] { implicit def F: CoflatMap[F] def compose[A, B, C](f: Cokleisli[F, B, C], g: Cokleisli[F, A, B]): Cokleisli[F, A, C] = f.compose(g) - - def split[A, B, C, D](f: Cokleisli[F, A, B], g: Cokleisli[F, C, D]): Cokleisli[F, (A, C), (B, D)] = - Cokleisli(fac => f.run(F.map(fac)(_._1)) -> g.run(F.map(fac)(_._2))) } private trait CokleisliProfunctor[F[_]] extends Profunctor[Cokleisli[F, ?, ?]] { diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index c9ab66983b..bec95e659f 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -1,7 +1,7 @@ package cats package data -import cats.arrow.{Arrow, Category, Choice, Compose, Split, FunctionK} +import cats.arrow.{Arrow, Category, Choice, CommutativeArrow, Compose, FunctionK} import cats.functor.{Contravariant, Strong} /** @@ -81,7 +81,13 @@ private[data] sealed trait KleisliFunctions { } private[data] sealed abstract class KleisliInstances extends KleisliInstances0 { + implicit def catsDataCommutativeMonadForKleisli[F[_], A, B](implicit F0: CommutativeMonad[F]): CommutativeMonad[Kleisli[F, A, ?]] = + new KleisliMonad[F, A] with CommutativeMonad[Kleisli[F, A, ?]] { + implicit def F: Monad[F] = F0 + } +} +private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 { implicit def catsDataMonoidForKleisli[F[_], A, B](implicit FB0: Monoid[F[B]]): Monoid[Kleisli[F, A, B]] = new KleisliMonoid[F, A, B] { def FB: Monoid[F[B]] = FB0 } @@ -91,11 +97,11 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 { implicit val catsDataMonoidKForKleisliId: MonoidK[λ[α => Kleisli[Id, α, α]]] = catsDataMonoidKForKleisli[Id] - implicit def catsDataArrowForKleisli[F[_]](implicit M: Monad[F]): Arrow[Kleisli[F, ?, ?]] = - new KleisliArrow[F] { def F: Monad[F] = M } + implicit def catsDataCommutativeArrowForKleisli[F[_]](implicit M: CommutativeMonad[F]): CommutativeArrow[Kleisli[F, ?, ?]] = + new KleisliCommutativeArrow[F] {def F: CommutativeMonad[F] = M } - implicit val catsDataArrowForKleisliId: Arrow[Kleisli[Id, ?, ?]] = - catsDataArrowForKleisli[Id] + implicit val catsDataCommutativeArrowForKleisliId: CommutativeArrow[Kleisli[Id, ?, ?]] = + catsDataCommutativeArrowForKleisli[Id] implicit def catsDataMonadReaderForKleisliId[A]: MonadReader[Kleisli[Id, A, ?], A] = catsDataMonadReaderForKleisli[Id, A] @@ -115,25 +121,28 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 { new KleisliApplicativeError[F, A, E] { def F: ApplicativeError[F, E] = AE } } -private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 { +private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 { + implicit def catsDataArrowForKleisli[F[_]](implicit M: Monad[F]): Arrow[Kleisli[F, ?, ?]] = + new KleisliArrow[F] { def F: Monad[F] = M } + implicit def catsDataMonadErrorForKleisli[F[_], A, E](implicit ME: MonadError[F, E]): MonadError[Kleisli[F, A, ?], E] = new KleisliMonadError[F, A, E] { def F: MonadError[F, E] = ME } } -private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 { +private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3 { implicit def catsDataMonadReaderForKleisli[F[_], A](implicit M: Monad[F]): MonadReader[Kleisli[F, A, ?], A] = new KleisliMonadReader[F, A] { def F: Monad[F] = M } } -private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3 { +private[data] sealed abstract class KleisliInstances3 extends KleisliInstances4 { implicit def catsDataChoiceForKleisli[F[_]](implicit M: Monad[F]): Choice[Kleisli[F, ?, ?]] = new KleisliChoice[F] { def F: Monad[F] = M } implicit val catsDataChoiceForKleisliId: Choice[Kleisli[Id, ?, ?]] = catsDataChoiceForKleisli[Id] - implicit def catsDataSplitForKleisli[F[_]](implicit FM: FlatMap[F]): Split[Kleisli[F, ?, ?]] = - new KleisliSplit[F] { def F: FlatMap[F] = FM } + implicit def catsDataComposeForKleisli[F[_]](implicit FM: FlatMap[F]): Compose[Kleisli[F, ?, ?]] = + new KleisliCompose[F] { def F: FlatMap[F] = FM } implicit def catsDataStrongForKleisli[F[_]](implicit F0: Functor[F]): Strong[Kleisli[F, ?, ?]] = new KleisliStrong[F] { def F: Functor[F] = F0 } @@ -148,30 +157,30 @@ private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3 Compose[Kleisli[F, ?, ?]].algebraK } -private[data] sealed abstract class KleisliInstances3 extends KleisliInstances4 { +private[data] sealed abstract class KleisliInstances4 extends KleisliInstances5 { implicit def catsDataApplicativeForKleisli[F[_], A](implicit A: Applicative[F]): Applicative[Kleisli[F, A, ?]] = new KleisliApplicative[F, A] { def F: Applicative[F] = A } } -private[data] sealed abstract class KleisliInstances4 extends KleisliInstances5 { +private[data] sealed abstract class KleisliInstances5 extends KleisliInstances6 { implicit def catsDataApplyForKleisli[F[_], A](implicit A: Apply[F]): Apply[Kleisli[F, A, ?]] = new KleisliApply[F, A] { def F: Apply[F] = A } } -private[data] sealed abstract class KleisliInstances5 { +private[data] sealed abstract class KleisliInstances6 { implicit def catsDataFunctorForKleisli[F[_], A](implicit F0: Functor[F]): Functor[Kleisli[F, A, ?]] = new KleisliFunctor[F, A] { def F: Functor[F] = F0 } } -private trait KleisliArrow[F[_]] extends Arrow[Kleisli[F, ?, ?]] with KleisliSplit[F] with KleisliStrong[F] with KleisliCategory[F] { +private trait KleisliCommutativeArrow[F[_]] extends CommutativeArrow[Kleisli[F, ?, ?]] with KleisliArrow[F] { + implicit def F: CommutativeMonad[F] +} + +private trait KleisliArrow[F[_]] extends Arrow[Kleisli[F, ?, ?]] with KleisliCategory[F] with KleisliStrong[F] { implicit def F: Monad[F] def lift[A, B](f: A => B): Kleisli[F, A, B] = Kleisli(a => F.pure(f(a))) -} - -private trait KleisliSplit[F[_]] extends Split[Kleisli[F, ?, ?]] with KleisliCompose[F] { - implicit def F: FlatMap[F] override def split[A, B, C, D](f: Kleisli[F, A, B], g: Kleisli[F, C, D]): Kleisli[F, (A, C), (B, D)] = Kleisli{ case (a, c) => F.flatMap(f.run(a))(b => F.map(g.run(c))(d => (b, d))) } diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index c3cce1398b..ee2e5a878f 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -3,6 +3,7 @@ package data import cats.kernel.instances.tuple._ import cats.functor.{Bifunctor, Contravariant} +import cats.kernel.CommutativeMonoid import cats.syntax.semigroup._ final case class WriterT[F[_], L, V](run: F[(L, V)]) { @@ -66,6 +67,14 @@ object WriterT extends WriterTInstances with WriterTFunctions { } private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { + implicit def catsDataCommutativeMonadForWriterT[F[_], L](implicit F: CommutativeMonad[F], L: CommutativeMonoid[L]): CommutativeMonad[WriterT[F, L, ?]] = + new WriterTMonad[F, L] with CommutativeMonad[WriterT[F, L, ?]] { + implicit val F0: Monad[F] = F + implicit val L0: Monoid[L] = L + } +} + +private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 { implicit def catsDataMonadForWriterTId[L:Monoid]: Monad[WriterT[Id, L, ?]] = catsDataMonadWriterForWriterT[Id, L] @@ -93,7 +102,7 @@ private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { catsDataMonoidForWriterT[Id, L, V] } -private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 { +private[data] sealed abstract class WriterTInstances1 extends WriterTInstances2 { implicit def catsDataMonadCombineForWriterT[F[_], L](implicit F: MonadCombine[F], L: Monoid[L]): MonadCombine[WriterT[F, L, ?]] = new WriterTMonadCombine[F, L] { implicit val F0: MonadCombine[F] = F @@ -110,7 +119,7 @@ private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 catsDataSemigroupForWriterT[Id, L, V] } -private[data] sealed abstract class WriterTInstances1 extends WriterTInstances2 { +private[data] sealed abstract class WriterTInstances2 extends WriterTInstances3 { implicit def catsDataMonadFilterForWriterT[F[_], L](implicit F: MonadFilter[F], L: Monoid[L]): MonadFilter[WriterT[F, L, ?]] = new WriterTMonadFilter[F, L] { implicit val F0: MonadFilter[F] = F @@ -126,7 +135,7 @@ private[data] sealed abstract class WriterTInstances1 extends WriterTInstances2 catsDataCoflatMapForWriterT[Id, L] } -private[data] sealed abstract class WriterTInstances2 extends WriterTInstances3 { +private[data] sealed abstract class WriterTInstances3 extends WriterTInstances4 { implicit def catsDataMonadWriterForWriterT[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadWriter[WriterT[F, L, ?], L] = new WriterTMonadWriter[F, L] { implicit val F0: Monad[F] = F @@ -139,7 +148,7 @@ private[data] sealed abstract class WriterTInstances2 extends WriterTInstances3 } } -private[data] sealed abstract class WriterTInstances3 extends WriterTInstances4 { +private[data] sealed abstract class WriterTInstances4 extends WriterTInstances5 { implicit def catsDataAlternativeForWriterT[F[_], L](implicit F: Alternative[F], L: Monoid[L]): Alternative[WriterT[F, L, ?]] = new WriterTAlternative[F, L] { implicit val F0: Alternative[F] = F @@ -148,7 +157,7 @@ private[data] sealed abstract class WriterTInstances3 extends WriterTInstances4 } -private[data] sealed abstract class WriterTInstances4 extends WriterTInstances5 { +private[data] sealed abstract class WriterTInstances5 extends WriterTInstances6 { implicit def catsDataApplicativeForWriterT[F[_], L](implicit F: Applicative[F], L: Monoid[L]): Applicative[WriterT[F, L, ?]] = new WriterTApplicative[F, L] { implicit val F0: Applicative[F] = F @@ -161,7 +170,7 @@ private[data] sealed abstract class WriterTInstances4 extends WriterTInstances5 } } -private[data] sealed abstract class WriterTInstances5 extends WriterTInstances6 { +private[data] sealed abstract class WriterTInstances6 extends WriterTInstances7 { implicit def catsDataFlatMapForWriterT1[F[_], L](implicit F: FlatMap[F], L: Monoid[L]): FlatMap[WriterT[F, L, ?]] = new WriterTFlatMap1[F, L] { implicit val F0: FlatMap[F] = F @@ -174,7 +183,7 @@ private[data] sealed abstract class WriterTInstances5 extends WriterTInstances6 } } -private[data] sealed abstract class WriterTInstances6 extends WriterTInstances7 { +private[data] sealed abstract class WriterTInstances7 extends WriterTInstances8 { implicit def catsDataFlatMapForWriterT2[F[_], L](implicit F: Monad[F], L: Semigroup[L]): FlatMap[WriterT[F, L, ?]] = new WriterTFlatMap2[F, L] { implicit val F0: Monad[F] = F @@ -182,7 +191,7 @@ private[data] sealed abstract class WriterTInstances6 extends WriterTInstances7 } } -private[data] sealed abstract class WriterTInstances7 extends WriterTInstances8 { +private[data] sealed abstract class WriterTInstances8 extends WriterTInstances9 { implicit def catsDataApplyForWriterT[F[_], L](implicit F: Apply[F], L: Semigroup[L]): Apply[WriterT[F, L, ?]] = new WriterTApply[F, L] { @@ -191,7 +200,7 @@ private[data] sealed abstract class WriterTInstances7 extends WriterTInstances8 } } -private[data] sealed abstract class WriterTInstances8 extends WriterTInstances9 { +private[data] sealed abstract class WriterTInstances9 extends WriterTInstances10 { implicit def catsDataCoflatMapForWriterT[F[_], L](implicit F: Functor[F]): CoflatMap[WriterT[F, L, ?]] = new WriterTCoflatMap[F, L] { implicit val F0: Functor[F] = F @@ -202,7 +211,7 @@ private[data] sealed abstract class WriterTInstances8 extends WriterTInstances9 } } -private[data] sealed abstract class WriterTInstances9 extends WriterTInstances10 { +private[data] sealed abstract class WriterTInstances10 extends WriterTInstances11 { implicit def catsDataMonadErrorForWriterT[F[_], L, E](implicit F: MonadError[F, E], L: Monoid[L]): MonadError[WriterT[F, L, ?], E] = new WriterTMonadError[F, L, E] { implicit val F0: MonadError[F, E] = F @@ -210,7 +219,7 @@ private[data] sealed abstract class WriterTInstances9 extends WriterTInstances10 } } -private[data] sealed abstract class WriterTInstances10 { +private[data] sealed abstract class WriterTInstances11 { implicit def catsDataApplicativeErrorForWriterT[F[_], L, E](implicit F: ApplicativeError[F, E], L: Monoid[L]): ApplicativeError[WriterT[F, L, ?], E] = new WriterTApplicativeError[F, L, E] { implicit val F0: ApplicativeError[F, E] = F diff --git a/core/src/main/scala/cats/instances/function.scala b/core/src/main/scala/cats/instances/function.scala index 36a239e2de..3aa53cb3df 100644 --- a/core/src/main/scala/cats/instances/function.scala +++ b/core/src/main/scala/cats/instances/function.scala @@ -1,7 +1,7 @@ package cats package instances -import cats.arrow.{Arrow, Category, Choice} +import cats.arrow.{CommutativeArrow, Category, Choice} import cats.functor.Contravariant import annotation.tailrec @@ -67,8 +67,8 @@ private[instances] sealed trait Function1Instances { } } - implicit val catsStdInstancesForFunction1: Choice[Function1] with Arrow[Function1] = - new Choice[Function1] with Arrow[Function1] { + implicit val catsStdInstancesForFunction1: Choice[Function1] with CommutativeArrow[Function1] = + new Choice[Function1] with CommutativeArrow[Function1] { def choice[A, B, C](f: A => C, g: B => C): Either[A, B] => C = { case Left(a) => f(a) case Right(b) => g(b) diff --git a/core/src/main/scala/cats/instances/option.scala b/core/src/main/scala/cats/instances/option.scala index 49c3cf3690..be654f0d3a 100644 --- a/core/src/main/scala/cats/instances/option.scala +++ b/core/src/main/scala/cats/instances/option.scala @@ -5,8 +5,8 @@ import scala.annotation.tailrec trait OptionInstances extends cats.kernel.instances.OptionInstances { - implicit val catsStdInstancesForOption: TraverseFilter[Option] with MonadError[Option, Unit] with MonadCombine[Option] with Monad[Option] with CoflatMap[Option] with Alternative[Option] = - new TraverseFilter[Option] with MonadError[Option, Unit] with MonadCombine[Option] with Monad[Option] with CoflatMap[Option] with Alternative[Option] { + implicit val catsStdInstancesForOption: TraverseFilter[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CommutativeMonad[Option] with CoflatMap[Option] with Alternative[Option] = + new TraverseFilter[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CommutativeMonad[Option] with CoflatMap[Option] with Alternative[Option] { def empty[A]: Option[A] = None diff --git a/core/src/main/scala/cats/package.scala b/core/src/main/scala/cats/package.scala index 88f71bae38..59fab09423 100644 --- a/core/src/main/scala/cats/package.scala +++ b/core/src/main/scala/cats/package.scala @@ -32,8 +32,8 @@ package object cats { * encodes pure unary function application. */ type Id[A] = A - implicit val catsInstancesForId: Bimonad[Id] with Monad[Id] with NonEmptyTraverse[Id] = - new Bimonad[Id] with Monad[Id] with NonEmptyTraverse[Id] { + implicit val catsInstancesForId: Bimonad[Id] with CommutativeMonad[Id] with Comonad[Id] with NonEmptyTraverse[Id] = + new Bimonad[Id] with CommutativeMonad[Id] with Comonad[Id] with NonEmptyTraverse[Id] { def pure[A](a: A): A = a def extract[A](a: A): A = a def flatMap[A, B](a: A)(f: A => B): B = f(a) diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index 5352b51183..67d4b2c235 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -5,6 +5,7 @@ trait AllSyntax extends ApplicativeSyntax with ApplicativeErrorSyntax with ApplySyntax + with ArrowSyntax with BifunctorSyntax with BifoldableSyntax with BitraverseSyntax @@ -38,7 +39,6 @@ trait AllSyntax with SemigroupSyntax with SemigroupKSyntax with ShowSyntax - with SplitSyntax with StrongSyntax with TraverseFilterSyntax with TraverseSyntax diff --git a/core/src/main/scala/cats/syntax/arrow.scala b/core/src/main/scala/cats/syntax/arrow.scala new file mode 100644 index 0000000000..4498e36d85 --- /dev/null +++ b/core/src/main/scala/cats/syntax/arrow.scala @@ -0,0 +1,6 @@ +package cats +package syntax + +import cats.arrow.Arrow + +trait ArrowSyntax extends Arrow.ToArrowOps diff --git a/core/src/main/scala/cats/syntax/package.scala b/core/src/main/scala/cats/syntax/package.scala index 36ab4bf511..44abb61a08 100644 --- a/core/src/main/scala/cats/syntax/package.scala +++ b/core/src/main/scala/cats/syntax/package.scala @@ -5,6 +5,7 @@ package object syntax { object applicative extends ApplicativeSyntax object applicativeError extends ApplicativeErrorSyntax object apply extends ApplySyntax + object arrow extends ArrowSyntax object bifunctor extends BifunctorSyntax object bifoldable extends BifoldableSyntax object bitraverse extends BitraverseSyntax @@ -37,7 +38,6 @@ package object syntax { object semigroup extends SemigroupSyntax object semigroupk extends SemigroupKSyntax object show extends ShowSyntax - object split extends SplitSyntax object strong extends StrongSyntax object monadTrans extends MonadTransSyntax object traverse extends TraverseSyntax diff --git a/core/src/main/scala/cats/syntax/split.scala b/core/src/main/scala/cats/syntax/split.scala deleted file mode 100644 index 95d3202506..0000000000 --- a/core/src/main/scala/cats/syntax/split.scala +++ /dev/null @@ -1,6 +0,0 @@ -package cats -package syntax - -import cats.arrow.Split - -trait SplitSyntax extends Split.ToSplitOps diff --git a/laws/src/main/scala/cats/laws/ArrowLaws.scala b/laws/src/main/scala/cats/laws/ArrowLaws.scala index 80213ac434..f02a8e47ed 100644 --- a/laws/src/main/scala/cats/laws/ArrowLaws.scala +++ b/laws/src/main/scala/cats/laws/ArrowLaws.scala @@ -3,14 +3,14 @@ package laws import cats.arrow.Arrow import cats.instances.function._ +import cats.syntax.arrow._ import cats.syntax.compose._ -import cats.syntax.split._ import cats.syntax.strong._ /** * Laws that must be obeyed by any `cats.arrow.Arrow`. */ -trait ArrowLaws[F[_, _]] extends CategoryLaws[F] with SplitLaws[F] with StrongLaws[F] { +trait ArrowLaws[F[_, _]] extends CategoryLaws[F] with StrongLaws[F] { implicit override def F: Arrow[F] def arrowIdentity[A]: IsEq[F[A, A]] = @@ -34,6 +34,9 @@ trait ArrowLaws[F[_, _]] extends CategoryLaws[F] with SplitLaws[F] with StrongLa def arrowAssociation[A, B, C, D](f: F[A, B]): IsEq[F[((A, C), D), (B, (C, D))]] = (f.first[C].first[D] andThen F.lift(assoc[B, C, D])) <-> (F.lift(assoc[A, C, D]) andThen f.first[(C, D)]) + def splitConsistentWithAndThen[A, B, C, D](f: F[A, B], g: F[C, D]): IsEq[F[(A, C), (B, D)]] = + F.split(f, g) <-> (f.first andThen g.second) + private def fst[A, B](p: (A, B)): A = p._1 private def assoc[A, B, C](p: ((A, B), C)): (A, (B, C)) = (p._1._1, (p._1._2, p._2)) diff --git a/laws/src/main/scala/cats/laws/CommutativeArrowLaws.scala b/laws/src/main/scala/cats/laws/CommutativeArrowLaws.scala new file mode 100644 index 0000000000..9212b896eb --- /dev/null +++ b/laws/src/main/scala/cats/laws/CommutativeArrowLaws.scala @@ -0,0 +1,22 @@ +package cats +package laws + +import cats.arrow.CommutativeArrow +import cats.syntax.compose._ +import cats.syntax.strong._ + +/** Reference: "Causal Commutative Arrows", Journal of Functional Programming + * Figure 4. + */ +trait CommutativeArrowLaws[F[_, _]] extends ArrowLaws[F] { + implicit override def F: CommutativeArrow[F] + + def arrowCommutative[A, B, C, D](f: F[A, B], g: F[C, D]): IsEq[F[(A, C), (B, D)]] = + (f.first[C] >>> g.second[B]) <-> (g.second[A] >>> f.first[D]) + +} + +object CommutativeArrowLaws { + def apply[F[_, _]](implicit ev: CommutativeArrow[F]): CommutativeArrowLaws[F] = + new CommutativeArrowLaws[F] { def F: CommutativeArrow[F] = ev } +} diff --git a/laws/src/main/scala/cats/laws/CommutativeFlatMapLaws.scala b/laws/src/main/scala/cats/laws/CommutativeFlatMapLaws.scala new file mode 100644 index 0000000000..321183097c --- /dev/null +++ b/laws/src/main/scala/cats/laws/CommutativeFlatMapLaws.scala @@ -0,0 +1,19 @@ +package cats +package laws + +/** + * Laws that must be obeyed by any `CommutativeFlatMap`. + */ +trait CommutativeFlatMapLaws[F[_]] extends FlatMapLaws[F] { + implicit override def F: CommutativeFlatMap[F] + + def flatmapCommutative[A, B, C](fa: F[A], fb: F[B], g: (A, B) => F[C]): IsEq[F[C]] = + F.flatMap(fa)( a => F.flatMap(fb)( b => g(a, b))) <-> + F.flatMap(fb)( b => F.flatMap(fa)( a => g(a, b))) + +} + +object CommutativeFlatMapLaws { + def apply[F[_]](implicit ev: CommutativeFlatMap[F]): CommutativeFlatMapLaws[F] = + new CommutativeFlatMapLaws[F] { def F: CommutativeFlatMap[F] = ev } +} diff --git a/laws/src/main/scala/cats/laws/CommutativeMonadLaws.scala b/laws/src/main/scala/cats/laws/CommutativeMonadLaws.scala new file mode 100644 index 0000000000..1172b30165 --- /dev/null +++ b/laws/src/main/scala/cats/laws/CommutativeMonadLaws.scala @@ -0,0 +1,14 @@ +package cats +package laws + +/** + * Laws that must be obeyed by any `CommutativeMonad`. + */ +trait CommutativeMonadLaws[F[_]] extends MonadLaws[F] with CommutativeFlatMapLaws[F] { + implicit override def F: CommutativeMonad[F] +} + +object CommutativeMonadLaws { + def apply[F[_]](implicit ev: CommutativeMonad[F]): CommutativeMonadLaws[F] = + new CommutativeMonadLaws[F] { def F: CommutativeMonad[F] = ev } +} diff --git a/laws/src/main/scala/cats/laws/SplitLaws.scala b/laws/src/main/scala/cats/laws/SplitLaws.scala deleted file mode 100644 index b04745519c..0000000000 --- a/laws/src/main/scala/cats/laws/SplitLaws.scala +++ /dev/null @@ -1,22 +0,0 @@ -package cats -package laws - -import cats.arrow.Split -import cats.syntax.compose._ -import cats.syntax.split._ - -/** - * Laws that must be obeyed by any `cats.arrow.Split`. - */ -trait SplitLaws[F[_, _]] extends ComposeLaws[F] { - implicit override def F: Split[F] - - def splitInterchange[A1, A2, A3, B1, B2, B3](f1: F[A1, A2], f2: F[A2, A3], - g1: F[B1, B2], g2: F[B2, B3]): IsEq[F[(A1, B1), (A3, B3)]] = - ((f1 split g1) andThen (f2 split g2)) <-> ((f1 andThen f2) split (g1 andThen g2)) -} - -object SplitLaws { - def apply[F[_, _]](implicit ev: Split[F]): SplitLaws[F] = - new SplitLaws[F] { def F: Split[F] = ev } -} diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index fe0173fdd4..1cdb684e8d 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -76,8 +76,6 @@ object arbitrary extends ArbitraryInstances0 { B.perturb(seed, _), (a, b) => A.perturb(B.perturb(seed, b), a))) - implicit def catsLawsArbitraryForCokleisli[F[_], A, B](implicit AFA: Arbitrary[F[A]], CFA: Cogen[F[A]], B: Arbitrary[B]): Arbitrary[Cokleisli[F, A, B]] = - Arbitrary(Arbitrary.arbitrary[F[A] => B].map(Cokleisli(_))) implicit def catsLawsArbitraryForOptionT[F[_], A](implicit F: Arbitrary[F[Option[A]]]): Arbitrary[OptionT[F, A]] = Arbitrary(F.arbitrary.map(OptionT.apply)) @@ -161,6 +159,10 @@ object arbitrary extends ArbitraryInstances0 { implicit def catsLawArbitraryForReader[A: Arbitrary: Cogen, B: Arbitrary]: Arbitrary[Reader[A, B]] = catsLawsArbitraryForKleisli[Id, A, B] + + implicit def catsLawArbitraryForCokleisliId[A: Arbitrary: Cogen, B: Arbitrary]: Arbitrary[Cokleisli[Id, A, B]] = + catsLawsArbitraryForCokleisli[Id, A, B] + implicit def catsLawsArbitraryForReaderWriterStateT[F[_]: Applicative, E, L, S, A](implicit F: Arbitrary[(E, S) => F[(L, S, A)]]): Arbitrary[ReaderWriterStateT[F, E, L, S, A]] = Arbitrary(F.arbitrary.map(ReaderWriterStateT(_))) } @@ -179,4 +181,6 @@ private[discipline] sealed trait ArbitraryInstances0 { implicit def catsLawsArbitraryForKleisli[F[_], A, B](implicit AA: Arbitrary[A], CA: Cogen[A], F: Arbitrary[F[B]]): Arbitrary[Kleisli[F, A, B]] = Arbitrary(Arbitrary.arbitrary[A => F[B]].map(Kleisli(_))) + implicit def catsLawsArbitraryForCokleisli[F[_], A, B](implicit AFA: Arbitrary[F[A]], CFA: Cogen[F[A]], B: Arbitrary[B]): Arbitrary[Cokleisli[F, A, B]] = + Arbitrary(Arbitrary.arbitrary[F[A] => B].map(Cokleisli(_))) } diff --git a/laws/src/main/scala/cats/laws/discipline/ArrowTests.scala b/laws/src/main/scala/cats/laws/discipline/ArrowTests.scala index 0a133a106a..67274761ae 100644 --- a/laws/src/main/scala/cats/laws/discipline/ArrowTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/ArrowTests.scala @@ -6,7 +6,7 @@ import cats.arrow.Arrow import org.scalacheck.{Arbitrary, Cogen, Prop} import Prop._ -trait ArrowTests[F[_, _]] extends CategoryTests[F] with SplitTests[F] with StrongTests[F] { +trait ArrowTests[F[_, _]] extends CategoryTests[F] with StrongTests[F] { def laws: ArrowLaws[F] def arrow[A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, G: Arbitrary](implicit @@ -39,7 +39,6 @@ trait ArrowTests[F[_, _]] extends CategoryTests[F] with SplitTests[F] with Stron def bases: Seq[(String, RuleSet)] = Nil def parents: Seq[RuleSet] = Seq( category[A, B, C, D], - split[A, B, C, D, E, G], strong[A, B, C, D, E, G] ) def props: Seq[(String, Prop)] = Seq( @@ -49,7 +48,8 @@ trait ArrowTests[F[_, _]] extends CategoryTests[F] with SplitTests[F] with Stron "arrow functor" -> forAll(laws.arrowFunctor[A, B, C, D] _), "arrow exchange" -> forAll(laws.arrowExchange[A, B, C, D] _), "arrow unit" -> forAll(laws.arrowUnit[A, B, C] _), - "arrow association" -> forAll(laws.arrowAssociation[A, B, C, D] _) + "arrow association" -> forAll(laws.arrowAssociation[A, B, C, D] _), + "split consistent with andThen" -> forAll(laws.splitConsistentWithAndThen[A, B, C, D] _) ) } } diff --git a/laws/src/main/scala/cats/laws/discipline/CommutativeArrowTests.scala b/laws/src/main/scala/cats/laws/discipline/CommutativeArrowTests.scala new file mode 100644 index 0000000000..93c0742e41 --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/CommutativeArrowTests.scala @@ -0,0 +1,46 @@ +package cats +package laws +package discipline + +import cats.arrow.CommutativeArrow +import org.scalacheck.{Arbitrary, Cogen, Prop} +import Prop._ + +trait CommutativeArrowTests[F[_, _]] extends ArrowTests[F] { + def laws: CommutativeArrowLaws[F] + + def commutativeArrow[A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, G: Arbitrary](implicit + ArbFAB: Arbitrary[F[A, B]], + ArbFBC: Arbitrary[F[B, C]], + ArbFCD: Arbitrary[F[C, D]], + ArbFDE: Arbitrary[F[D, E]], + ArbFEG: Arbitrary[F[E, G]], + CogenA: Cogen[A], + CogenB: Cogen[B], + CogenC: Cogen[C], + CogenD: Cogen[D], + CogenE: Cogen[E], + EqFAA: Eq[F[A, A]], + EqFAB: Eq[F[A, B]], + EqFAC: Eq[F[A, C]], + EqFAD: Eq[F[A, D]], + EqFAG: Eq[F[A, G]], + EqFACB: Eq[F[(A, C), B]], + EqFACBC: Eq[F[(A, C), (B, C)]], + EqFACBD: Eq[F[(A, C), (B, D)]], + EqFADCD: Eq[F[(A, D), (C, D)]], + EqFADCG: Eq[F[(A, D), (C, G)]], + EqFAEDE: Eq[F[(A, E), (D, E)]], + EqFEAED: Eq[F[(E, A), (E, D)]], + EqFACDBCD: Eq[F[((A, C), D), (B, (C, D))]] + ): RuleSet = + new DefaultRuleSet( + name = "commutative arrow", + parent = Some(arrow[A, B, C, D, E, G]), + "arrow commutativity" -> forAll(laws.arrowCommutative[A, B, C, D] _)) +} + +object CommutativeArrowTests { + def apply[F[_, _]: CommutativeArrow]: CommutativeArrowTests[F] = + new CommutativeArrowTests[F] { def laws: CommutativeArrowLaws[F] = CommutativeArrowLaws[F] } +} diff --git a/laws/src/main/scala/cats/laws/discipline/CommutativeFlatMapTests.scala b/laws/src/main/scala/cats/laws/discipline/CommutativeFlatMapTests.scala new file mode 100644 index 0000000000..70a69fe2bd --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/CommutativeFlatMapTests.scala @@ -0,0 +1,45 @@ +package cats +package laws +package discipline + +import cats.laws.discipline.CartesianTests.Isomorphisms +import org.scalacheck.{Arbitrary, Cogen, Prop} +import Prop._ + +trait CommutativeFlatMapTests[F[_]] extends FlatMapTests[F] { + def laws: CommutativeFlatMapLaws[F] + + def commutativeFlatMap[A: Arbitrary: Eq, B: Arbitrary: Eq, C: Arbitrary: Eq](implicit + ArbFA: Arbitrary[F[A]], + ArbFB: Arbitrary[F[B]], + ArbFC: Arbitrary[F[C]], + ArbFAtoB: Arbitrary[F[A => B]], + ArbFBtoC: Arbitrary[F[B => C]], + CogenA: Cogen[A], + CogenB: Cogen[B], + CogenC: Cogen[C], + EqFA: Eq[F[A]], + EqFB: Eq[F[B]], + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + EqFInt: Eq[F[Int]], + iso: Isomorphisms[F] + ): RuleSet = { + new RuleSet { + def name: String = "commutative flatMap" + def bases: Seq[(String, RuleSet)] = Nil + def parents: Seq[RuleSet] = Seq(flatMap[A, B, C]) + def props: Seq[(String, Prop)] = Seq( + "flatmap commutativity" -> forAll(laws.flatmapCommutative[A, B, C] _) + ) + } + } + +} + +object CommutativeFlatMapTests { + def apply[F[_]: CommutativeFlatMap]: CommutativeFlatMapTests[F] = + new CommutativeFlatMapTests[F] { + def laws: CommutativeFlatMapLaws[F] = CommutativeFlatMapLaws[F] + } +} diff --git a/laws/src/main/scala/cats/laws/discipline/CommutativeMonadTests.scala b/laws/src/main/scala/cats/laws/discipline/CommutativeMonadTests.scala new file mode 100644 index 0000000000..c2a0975759 --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/CommutativeMonadTests.scala @@ -0,0 +1,42 @@ +package cats +package laws +package discipline + +import cats.laws.discipline.CartesianTests.Isomorphisms +import org.scalacheck.{Arbitrary, Cogen, Prop} + +trait CommutativeMonadTests[F[_]] extends MonadTests[F] with CommutativeFlatMapTests[F] { + def laws: CommutativeMonadLaws[F] + + def commutativeMonad[A: Arbitrary: Eq, B: Arbitrary: Eq, C: Arbitrary: Eq](implicit + ArbFA: Arbitrary[F[A]], + ArbFB: Arbitrary[F[B]], + ArbFC: Arbitrary[F[C]], + ArbFAtoB: Arbitrary[F[A => B]], + ArbFBtoC: Arbitrary[F[B => C]], + CogenA: Cogen[A], + CogenB: Cogen[B], + CogenC: Cogen[C], + EqFA: Eq[F[A]], + EqFB: Eq[F[B]], + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + EqFInt: Eq[F[Int]], + iso: Isomorphisms[F] + ): RuleSet = { + new RuleSet { + def name: String = "commutative monad" + def bases: Seq[(String, RuleSet)] = Nil + def parents: Seq[RuleSet] = Seq(monad[A, B, C], commutativeFlatMap[A, B, C]) + def props: Seq[(String, Prop)] = Nil + } + } + +} + +object CommutativeMonadTests { + def apply[F[_]: CommutativeMonad]: CommutativeMonadTests[F] = + new CommutativeMonadTests[F] { + def laws: CommutativeMonadLaws[F] = CommutativeMonadLaws[F] + } +} diff --git a/laws/src/main/scala/cats/laws/discipline/SplitTests.scala b/laws/src/main/scala/cats/laws/discipline/SplitTests.scala deleted file mode 100644 index 3b11c2e3a9..0000000000 --- a/laws/src/main/scala/cats/laws/discipline/SplitTests.scala +++ /dev/null @@ -1,31 +0,0 @@ -package cats -package laws -package discipline - -import cats.arrow.Split -import org.scalacheck.Arbitrary -import org.scalacheck.Prop -import Prop._ - -trait SplitTests[F[_, _]] extends ComposeTests[F] { - def laws: SplitLaws[F] - - def split[A, B, C, D, E, G](implicit - ArbFAB: Arbitrary[F[A, B]], - ArbFBC: Arbitrary[F[B, C]], - ArbFCD: Arbitrary[F[C, D]], - ArbFDE: Arbitrary[F[D, E]], - ArbFEG: Arbitrary[F[E, G]], - EqFAD: Eq[F[A, D]], - EqFADCG: Eq[F[(A, D), (C, G)]] - ): RuleSet = - new DefaultRuleSet( - name = "split", - parent = Some(compose[A, B, C, D]), - "split interchange" -> forAll(laws.splitInterchange[A, B, C, D, E, G] _)) -} - -object SplitTests { - def apply[F[_, _]: Split]: SplitTests[F] = - new SplitTests[F] { def laws: SplitLaws[F] = SplitLaws[F] } -} diff --git a/tests/src/test/scala/cats/tests/CokleisliTests.scala b/tests/src/test/scala/cats/tests/CokleisliTests.scala index 214c02999b..837c4c747e 100644 --- a/tests/src/test/scala/cats/tests/CokleisliTests.scala +++ b/tests/src/test/scala/cats/tests/CokleisliTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.arrow.{Arrow, Split} +import cats.arrow.{Arrow, CommutativeArrow} import cats.data.{Cokleisli, NonEmptyList} import cats.functor.{Contravariant, Profunctor} import cats.laws.discipline._ @@ -33,14 +33,9 @@ class CokleisliTests extends SlowCatsSuite { checkAll("Cokleisli[Option, Int, Int]", ProfunctorTests[Cokleisli[Option, ?, ?]].profunctor[Int, Int, Int, Int, Int, Int]) checkAll("Profunctor[Cokleisli[Option, ?, ?]]", SerializableTests.serializable(Profunctor[Cokleisli[Option, ?, ?]])) - checkAll("Cokleisli[Option, Int, Int]", SplitTests[Cokleisli[Option, ?, ?]].split[Int, Int, Int, Int, Int, Int]) - checkAll("Split[Cokleisli[Option, ?, ?]]", SerializableTests.serializable(Split[Cokleisli[Option, ?, ?]])) - checkAll("Cokleisli[Option, Int, Int]", ContravariantTests[Cokleisli[Option, ?, Int]].contravariant[Int, Int, Int]) checkAll("Contravariant[Cokleisli[Option, ?, Int]]", SerializableTests.serializable(Contravariant[Cokleisli[Option, ?, Int]])) - checkAll("Cokleisli[NonEmptyList, Int, Int]", ArrowTests[Cokleisli[NonEmptyList, ?, ?]].arrow[Int, Int, Int, Int, Int, Int]) - checkAll("Arrow[Cokleisli[NonEmptyList, ?, ?]]", SerializableTests.serializable(Arrow[Cokleisli[NonEmptyList, ?, ?]])) checkAll("Cokleisli[NonEmptyList, Int, Int]", MonoidKTests[λ[α => Cokleisli[NonEmptyList, α, α]]].monoidK[Int]) checkAll("MonoidK[λ[α => Cokleisli[NonEmptyList, α, α]]]", SerializableTests.serializable(MonoidK[λ[α => Cokleisli[NonEmptyList, α, α]]])) @@ -48,6 +43,17 @@ class CokleisliTests extends SlowCatsSuite { checkAll("Cokleisli[List, Int, Int]", SemigroupKTests[λ[α => Cokleisli[List, α, α]]].semigroupK[Int]) checkAll("SemigroupK[λ[α => Cokleisli[List, α, α]]]", SerializableTests.serializable(SemigroupK[λ[α => Cokleisli[List, α, α]]])) + checkAll("Cokleisli[NonEmptyList, Int, Int]", ArrowTests[Cokleisli[NonEmptyList, ?, ?]].arrow[Int, Int, Int, Int, Int, Int]) + checkAll("Arrow[Cokleisli[NonEmptyList, ?, ?]]", SerializableTests.serializable(Arrow[Cokleisli[NonEmptyList, ?, ?]])) + + { + implicit def cokleisliIdEq[A, B](implicit A: Arbitrary[A], FB: Eq[B]): Eq[Cokleisli[Id, A, B]] = + Eq.by[Cokleisli[Id, A, B], A => B](_.run) + + checkAll("Cokleisli[Id, Int, Int]", CommutativeArrowTests[Cokleisli[Id, ?, ?]].commutativeArrow[Int, Int, Int, Int, Int, Int]) + checkAll("CommutativeArrow[Cokleisli[Id, ?, ?]]", SerializableTests.serializable(CommutativeArrow[Cokleisli[Id, ?, ?]])) + } + test("contramapValue with Id consistent with lmap"){ forAll { (c: Cokleisli[Id, Int, Long], f: Char => Int) => c.contramapValue[Char](f) should === (c.lmap(f)) diff --git a/tests/src/test/scala/cats/tests/FunctionTests.scala b/tests/src/test/scala/cats/tests/FunctionTests.scala index e545030f2d..4c893160b3 100644 --- a/tests/src/test/scala/cats/tests/FunctionTests.scala +++ b/tests/src/test/scala/cats/tests/FunctionTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.arrow.{Arrow, Choice} +import cats.arrow.{CommutativeArrow, Choice} import cats.functor.Contravariant import cats.laws.discipline._ import cats.laws.discipline.eq._ @@ -27,8 +27,8 @@ class FunctionTests extends CatsSuite { checkAll("Function1[Int, Int]", MonadReaderTests[Int => ?, Int].monadReader[Int, Int, Int]) checkAll("MonadReader[Int => ?, Int]", SerializableTests.serializable(MonadReader[Int => ?, Int])) - checkAll("Function1[Int, Int]", ArrowTests[Function1].arrow[Int, Int, Int, Int, Int, Int]) - checkAll("Arrow[Function1]", SerializableTests.serializable(Arrow[Function1])) + checkAll("Function1[Int, Int]", CommutativeArrowTests[Function1].commutativeArrow[Int, Int, Int, Int, Int, Int]) + checkAll("Arrow[Function1]", SerializableTests.serializable(CommutativeArrow[Function1])) checkAll("Function1[Int, Int]", ChoiceTests[Function1].choice[Int, Int, Int, Int]) checkAll("Choice[Function1]", SerializableTests.serializable(Choice[Function1])) diff --git a/tests/src/test/scala/cats/tests/IdTests.scala b/tests/src/test/scala/cats/tests/IdTests.scala index a55c6bfec0..a991b9a03a 100644 --- a/tests/src/test/scala/cats/tests/IdTests.scala +++ b/tests/src/test/scala/cats/tests/IdTests.scala @@ -9,8 +9,8 @@ class IdTests extends CatsSuite { checkAll("Id[Int]", BimonadTests[Id].bimonad[Int, Int, Int]) checkAll("Bimonad[Id]", SerializableTests.serializable(Bimonad[Id])) - checkAll("Id[Int]", MonadTests[Id].monad[Int, Int, Int]) - checkAll("Monad[Id]", SerializableTests.serializable(Monad[Id])) + checkAll("Id[Int]", CommutativeMonadTests[Id].commutativeMonad[Int, Int, Int]) + checkAll("CommutativeMonad[Id]", SerializableTests.serializable(CommutativeMonad[Id])) checkAll("Id[Int]", TraverseTests[Id].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[Id]", SerializableTests.serializable(Traverse[Id])) diff --git a/tests/src/test/scala/cats/tests/KleisliTests.scala b/tests/src/test/scala/cats/tests/KleisliTests.scala index 4b11b361ce..9e33992889 100644 --- a/tests/src/test/scala/cats/tests/KleisliTests.scala +++ b/tests/src/test/scala/cats/tests/KleisliTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.arrow.{Arrow, Choice, Split, FunctionK} +import cats.arrow.{Arrow, Choice, CommutativeArrow, FunctionK} import cats.data.{EitherT, Kleisli, Reader} import cats.functor.{Contravariant, Strong} import cats.laws.discipline._ @@ -33,10 +33,19 @@ class KleisliTests extends CatsSuite { checkAll("Kleisli[Option, Int, Int]", CartesianTests[Kleisli[Option, Int, ?]].cartesian[Int, Int, Int]) checkAll("Cartesian[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Cartesian[Kleisli[Option, Int, ?]])) + checkAll("Kleisli[Option, Int, ?]", CommutativeMonadTests[Kleisli[Option, Int, ?]].commutativeMonad[Int, Int, Int]) + checkAll("CommutativeMonad[Kleisli[Option, Int, ?]]",SerializableTests.serializable(CommutativeMonad[Kleisli[Option, Int, ?]])) + + { + implicit val catsDataArrowForKleisli = Kleisli.catsDataArrowForKleisli[List] + checkAll("Kleisli[List, Int, Int]", ArrowTests[Kleisli[List, ?, ?]].arrow[Int, Int, Int, Int, Int, Int]) + checkAll("Arrow[Kleisli[List, ?, ?]]", SerializableTests.serializable(Arrow[Kleisli[List, ?, ?]])) + } + { - implicit val catsDataArrowForKleisli = Kleisli.catsDataArrowForKleisli[Option] - checkAll("Kleisli[Option, Int, Int]", ArrowTests[Kleisli[Option, ?, ?]].arrow[Int, Int, Int, Int, Int, Int]) - checkAll("Arrow[Kleisli[Option, ?, ?]]", SerializableTests.serializable(Arrow[Kleisli[Option, ?, ?]])) + implicit val catsDataCommutativeArrowForKleisli = Kleisli.catsDataCommutativeArrowForKleisli[Option] + checkAll("Kleisli[Option, Int, Int]", CommutativeArrowTests[Kleisli[Option, ?, ?]].commutativeArrow[Int, Int, Int, Int, Int, Int]) + checkAll("CommutativeArrow[Kleisli[Option, ?, ?]]", SerializableTests.serializable(CommutativeArrow[Kleisli[Option, ?, ?]])) } { @@ -57,12 +66,6 @@ class KleisliTests extends CatsSuite { checkAll("MonadReader[Reader[?, ?], Int]", SerializableTests.serializable(MonadReader[Reader[Int, ?], Int])) } - { - implicit val kleisliSplit = Kleisli.catsDataSplitForKleisli[Option] - checkAll("Kleisli[Option, Int, Int]", SplitTests[Kleisli[Option, ?, ?]].split[Int, Int, Int, Int, Int, Int]) - checkAll("Split[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Split[Kleisli[Option, ?, ?]])) - } - { implicit val catsDataStrongForKleisli = Kleisli.catsDataStrongForKleisli[Option] checkAll("Kleisli[Option, Int, Int]", StrongTests[Kleisli[Option, ?, ?]].strong[Int, Int, Int, Int, Int, Int]) @@ -208,7 +211,6 @@ class KleisliTests extends CatsSuite { MonoidK[λ[α => Kleisli[List, α, α]]] Arrow[Kleisli[List, ?, ?]] Choice[Kleisli[List, ?, ?]] - Split[Kleisli[List, ?, ?]] Strong[Kleisli[List, ?, ?]] FlatMap[Kleisli[List, Int, ?]] Semigroup[Kleisli[List, Int, String]] @@ -223,8 +225,8 @@ class KleisliTests extends CatsSuite { Monoid[Kleisli[Id, Int, String]] MonoidK[λ[α => Kleisli[Id, α, α]]] Arrow[Kleisli[Id, ?, ?]] + CommutativeArrow[Kleisli[Id, ?, ?]] Choice[Kleisli[Id, ?, ?]] - Split[Kleisli[Id, ?, ?]] Strong[Kleisli[Id, ?, ?]] FlatMap[Kleisli[Id, Int, ?]] Semigroup[Kleisli[Id, Int, String]] @@ -239,8 +241,8 @@ class KleisliTests extends CatsSuite { Monoid[Reader[Int, String]] MonoidK[λ[α => Reader[α, α]]] Arrow[Reader[?, ?]] + CommutativeArrow[Reader[?, ?]] Choice[Reader[?, ?]] - Split[Reader[?, ?]] Strong[Reader[?, ?]] FlatMap[Reader[Int, ?]] Semigroup[Reader[Int, String]] diff --git a/tests/src/test/scala/cats/tests/NonEmptyListTests.scala b/tests/src/test/scala/cats/tests/NonEmptyListTests.scala index 8b00719d8c..3c4ccf9e6e 100644 --- a/tests/src/test/scala/cats/tests/NonEmptyListTests.scala +++ b/tests/src/test/scala/cats/tests/NonEmptyListTests.scala @@ -2,9 +2,10 @@ package cats package tests import cats.kernel.laws.{GroupLaws, OrderLaws} + import cats.data.{NonEmptyList, NonEmptyVector} -import cats.laws.discipline.{ComonadTests, MonadTests, NonEmptyTraverseTests, ReducibleTests, SemigroupKTests, SerializableTests} import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.{ComonadTests, NonEmptyTraverseTests, MonadTests, ReducibleTests, SemigroupKTests, SerializableTests} class NonEmptyListTests extends CatsSuite { // Lots of collections here.. telling ScalaCheck to calm down a bit diff --git a/tests/src/test/scala/cats/tests/OptionTests.scala b/tests/src/test/scala/cats/tests/OptionTests.scala index c16367455f..bd86eba79f 100644 --- a/tests/src/test/scala/cats/tests/OptionTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTests.scala @@ -14,8 +14,8 @@ class OptionTests extends CatsSuite { checkAll("Option[Int]", MonadCombineTests[Option].monadCombine[Int, Int, Int]) checkAll("MonadCombine[Option]", SerializableTests.serializable(MonadCombine[Option])) - checkAll("Option[Int]", MonadTests[Option].monad[Int, Int, Int]) - checkAll("Monad[Option]", SerializableTests.serializable(Monad[Option])) + checkAll("Option[Int]", CommutativeMonadTests[Option].commutativeMonad[Int, Int, Int]) + checkAll("CommutativeMonad[Option]", SerializableTests.serializable(CommutativeMonad[Option])) checkAll("Option[Int] with Option", TraverseFilterTests[Option].traverseFilter[Int, Int, Int, Int, Option, Option]) checkAll("TraverseFilter[Option]", SerializableTests.serializable(TraverseFilter[Option])) diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index 726512e2a2..2ec46d9050 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -391,4 +391,7 @@ class WriterTTests extends CatsSuite { checkAll("WriterT[Option, ListWrapper[Int], ?]", MonadErrorTests[WriterT[Option, ListWrapper[Int], ?], Unit].monadError[Int, Int, Int]) checkAll("MonadError[WriterT[Option, ListWrapper[Int], ?], Unit]", SerializableTests.serializable(MonadError[WriterT[Option, ListWrapper[Int], ?], Unit])) } + + checkAll("WriterT[Option, Int, ?]", CommutativeMonadTests[WriterT[Option, Int, ?]].commutativeMonad[Int, Int, Int]) + checkAll("CommutativeMonad[WriterT[Option, Int, ?]]",SerializableTests.serializable(CommutativeMonad[WriterT[Option, Int, ?]])) }