From 00e5ab4d92121518bf3a59fb48171f17ace92e39 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Fri, 2 Oct 2015 18:12:41 +0200 Subject: [PATCH 1/6] Generalize ApplyBuilder to Monoidal. --- core/src/main/scala/cats/Applicative.scala | 2 - core/src/main/scala/cats/Apply.scala | 15 ++-- core/src/main/scala/cats/FlatMap.scala | 3 + core/src/main/scala/cats/Monoidal.scala | 15 ++++ core/src/main/scala/cats/data/Const.scala | 9 ++ core/src/main/scala/cats/data/Func.scala | 16 ++-- core/src/main/scala/cats/data/Kleisli.scala | 9 ++ core/src/main/scala/cats/data/Prod.scala | 8 +- core/src/main/scala/cats/data/Validated.scala | 19 +++- core/src/main/scala/cats/syntax/all.scala | 1 + core/src/main/scala/cats/syntax/apply.scala | 22 +---- .../src/main/scala/cats/syntax/monoidal.scala | 28 ++++++ core/src/main/scala/cats/syntax/package.scala | 1 + docs/src/main/tut/apply.md | 10 ++- docs/src/main/tut/const.md | 12 ++- docs/src/main/tut/validated.md | 2 + .../scala/cats/free/FreeApplicative.scala | 4 +- .../main/scala/cats/laws/MonoidalLaws.scala | 16 ++++ .../main/scala/cats/laws/TraverseLaws.scala | 13 ++- project/Boilerplate.scala | 86 +++++++++++++------ .../test/scala/cats/state/StateTTests.scala | 2 +- 21 files changed, 223 insertions(+), 70 deletions(-) create mode 100644 core/src/main/scala/cats/Monoidal.scala create mode 100644 core/src/main/scala/cats/syntax/monoidal.scala create mode 100644 laws/src/main/scala/cats/laws/MonoidalLaws.scala diff --git a/core/src/main/scala/cats/Applicative.scala b/core/src/main/scala/cats/Applicative.scala index 88c6193b3c..70bdbf40d6 100644 --- a/core/src/main/scala/cats/Applicative.scala +++ b/core/src/main/scala/cats/Applicative.scala @@ -28,8 +28,6 @@ import simulacrum.typeclass */ def pureEval[A](x: Eval[A]): F[A] = pure(x.value) - override def map[A, B](fa: F[A])(f: A => B): F[B] = ap(fa)(pure(f)) - /** * Two sequentially dependent Applicatives can be composed. * diff --git a/core/src/main/scala/cats/Apply.scala b/core/src/main/scala/cats/Apply.scala index 99f3e0b6d7..153bd68c27 100644 --- a/core/src/main/scala/cats/Apply.scala +++ b/core/src/main/scala/cats/Apply.scala @@ -8,19 +8,19 @@ import simulacrum.typeclass * Must obey the laws defined in cats.laws.ApplyLaws. */ @typeclass(excludeParents=List("ApplyArityFunctions")) -trait Apply[F[_]] extends Functor[F] with ApplyArityFunctions[F] { self => +trait Apply[F[_]] extends Functor[F] with Monoidal[F] with ApplyArityFunctions[F] { self => /** * Given a value and a function in the Apply context, applies the * function to the value. */ - def ap[A, B](fa: F[A])(f: F[A => B]): F[B] + def ap[A, B](fa: F[A])(ff: F[A => B]): F[B] /** * ap2 is a binary version of ap, defined in terms of ap. */ - def ap2[A, B, Z](fa: F[A], fb: F[B])(f: F[(A, B) => Z]): F[Z] = - ap(fb)(ap(fa)(map(f)(f => (a: A) => (b: B) => f(a, b)))) + def ap2[A, B, Z](fa: F[A], fb: F[B])(ff: F[(A, B) => Z]): F[Z] = + map(product(fa, product(fb, ff))) { case (a, (b, f)) => f(a, b) } /** * Applies the pure (binary) function f to the effectful values fa and fb. @@ -28,7 +28,7 @@ trait Apply[F[_]] extends Functor[F] with ApplyArityFunctions[F] { self => * map2 can be seen as a binary version of [[cats.Functor]]#map. */ def map2[A, B, Z](fa: F[A], fb: F[B])(f: (A, B) => Z): F[Z] = - ap(fb)(map(fa)(a => (b: B) => f(a, b))) + map(product(fa, fb)) { case (a, b) => f(a, b) } /** * Two sequentially dependent Applys can be composed. @@ -45,6 +45,7 @@ trait Apply[F[_]] extends Functor[F] with ApplyArityFunctions[F] { self => def F: Apply[F] = self def G: Apply[G] = GG } + } trait CompositeApply[F[_], G[_]] @@ -54,4 +55,8 @@ trait CompositeApply[F[_], G[_]] def ap[A, B](fa: F[G[A]])(f: F[G[A => B]]): F[G[B]] = F.ap(fa)(F.map(f)(gab => G.ap(_)(gab))) + + def product[A, B](fa: F[G[A]], fb: F[G[B]]): F[G[(A, B)]] = + F.map2(fa, fb)(G.product) + } diff --git a/core/src/main/scala/cats/FlatMap.scala b/core/src/main/scala/cats/FlatMap.scala index 0e47d032dd..800ec6afbf 100644 --- a/core/src/main/scala/cats/FlatMap.scala +++ b/core/src/main/scala/cats/FlatMap.scala @@ -29,6 +29,9 @@ import simulacrum.typeclass override def ap[A, B](fa: F[A])(ff: F[A => B]): F[B] = flatMap(ff)(f => map(fa)(f)) + override def product[A, B](fa: F[A], fb: F[B]): F[(A, B)] = + flatMap(fa)(a => map(fb)(b => (a, b))) + /** * Pair `A` with the result of function application. */ diff --git a/core/src/main/scala/cats/Monoidal.scala b/core/src/main/scala/cats/Monoidal.scala new file mode 100644 index 0000000000..3b56bce150 --- /dev/null +++ b/core/src/main/scala/cats/Monoidal.scala @@ -0,0 +1,15 @@ +package cats + +import simulacrum.typeclass + +/** + * Monoidal allows us to express uncurried function application within a context, + * whatever the context variance is. + * + * It is worth noting that the couple Monoidal and [[Functor]] is interdefinable with [[Apply]]. + */ +@typeclass trait Monoidal[F[_]] { + def product[A, B](fa: F[A], fb: F[B]): F[(A, B)] +} + +object Monoidal extends MonoidalArityFunctions \ No newline at end of file diff --git a/core/src/main/scala/cats/data/Const.scala b/core/src/main/scala/cats/data/Const.scala index 48ab0f0667..711d4dc6f3 100644 --- a/core/src/main/scala/cats/data/Const.scala +++ b/core/src/main/scala/cats/data/Const.scala @@ -76,6 +76,12 @@ sealed abstract class ConstInstances0 extends ConstInstances1 { def ap[A, B](fa: Const[C, A])(f: Const[C, A => B]): Const[C, B] = f.retag[B] combine fa.retag[B] + + def map[A, B](fa: Const[C, A])(f: A => B): Const[C, B] = + fa.retag[B] + + def product[A, B](fa: Const[C, A], fb: Const[C, B]): Const[C, (A, B)] = + fa.retag[(A, B)] combine fb.retag[(A, B)] } } @@ -89,6 +95,9 @@ sealed abstract class ConstInstances1 { def ap[A, B](fa: Const[C, A])(f: Const[C, A => B]): Const[C, B] = fa.retag[B] combine f.retag[B] + def product[A, B](fa: Const[C, A], fb: Const[C, B]): Const[C, (A, B)] = + fa.retag[(A, B)] combine fb.retag[(A, B)] + def map[A, B](fa: Const[C, A])(f: A => B): Const[C, B] = fa.retag[B] } diff --git a/core/src/main/scala/cats/data/Func.scala b/core/src/main/scala/cats/data/Func.scala index c5834ffa09..bca2472df1 100644 --- a/core/src/main/scala/cats/data/Func.scala +++ b/core/src/main/scala/cats/data/Func.scala @@ -54,19 +54,21 @@ abstract class FuncInstances1 { sealed trait FuncFunctor[F[_], C] extends Functor[Lambda[X => Func[F, C, X]]] { def F: Functor[F] - override def map[A, B](fa: Func[F, C, A])(f: A => B): Func[F, C, B] = + def map[A, B](fa: Func[F, C, A])(f: A => B): Func[F, C, B] = fa.map(f)(F) } sealed trait FuncApply[F[_], C] extends Apply[Lambda[X => Func[F, C, X]]] with FuncFunctor[F, C] { def F: Apply[F] - override def ap[A, B](fa: Func[F, C, A])(f: Func[F, C, A => B]): Func[F, C, B] = + def ap[A, B](fa: Func[F, C, A])(f: Func[F, C, A => B]): Func[F, C, B] = Func.func(c => F.ap(fa.run(c))(f.run(c))) + def product[A, B](fa: Func[F, C, A], fb: Func[F, C, B]): Func[F, C, (A, B)] = + Func.func(c => F.product(fa.run(c), fb.run(c))) } sealed trait FuncApplicative[F[_], C] extends Applicative[Lambda[X => Func[F, C, X]]] with FuncApply[F, C] { def F: Applicative[F] - override def pure[A](a: A): Func[F, C, A] = + def pure[A](a: A): Func[F, C, A] = Func.func(c => F.pure(a)) } @@ -119,10 +121,12 @@ abstract class AppFuncInstances { sealed trait AppFuncApplicative[F[_], C] extends Applicative[Lambda[X => AppFunc[F, C, X]]] { def F: Applicative[F] - override def map[A, B](fa: AppFunc[F, C, A])(f: A => B): AppFunc[F, C, B] = + def map[A, B](fa: AppFunc[F, C, A])(f: A => B): AppFunc[F, C, B] = fa.map(f) - override def ap[A, B](fa: AppFunc[F, C, A])(f: AppFunc[F, C, A => B]): AppFunc[F, C, B] = + def ap[A, B](fa: AppFunc[F, C, A])(f: AppFunc[F, C, A => B]): AppFunc[F, C, B] = Func.appFunc[F, C, B](c => F.ap(fa.run(c))(f.run(c)))(F) - override def pure[A](a: A): AppFunc[F, C, A] = + def product[A, B](fa: AppFunc[F, C, A], fb: AppFunc[F, C, B]): AppFunc[F, C, (A, B)] = + Func.appFunc[F, C, (A, B)](c => F.product(fa.run(c), fb.run(c)))(F) + def pure[A](a: A): AppFunc[F, C, A] = Func.appFunc[F, C, A](c => F.pure(a))(F) } diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index e619d26aa8..b8af889ad6 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -147,6 +147,12 @@ sealed abstract class KleisliInstances1 extends KleisliInstances2 { def ap[B, C](fa: Kleisli[F, A, B])(f: Kleisli[F, A, B => C]): Kleisli[F, A, C] = fa(f) + + def map[B, C](fb: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = + fb.map(f) + + def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] = + Kleisli.function(a => Applicative[F].product(fb.run(a), fc.run(a))) } } @@ -155,6 +161,9 @@ sealed abstract class KleisliInstances2 extends KleisliInstances3 { def ap[B, C](fa: Kleisli[F, A, B])(f: Kleisli[F, A, B => C]): Kleisli[F, A, C] = fa(f) + def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] = + Kleisli.function(a => Apply[F].product(fb.run(a), fc.run(a))) + def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = fa.map(f) } diff --git a/core/src/main/scala/cats/data/Prod.scala b/core/src/main/scala/cats/data/Prod.scala index 91ba2a31f3..4af4f7d7c2 100644 --- a/core/src/main/scala/cats/data/Prod.scala +++ b/core/src/main/scala/cats/data/Prod.scala @@ -71,20 +71,22 @@ sealed abstract class ProdInstance4 { sealed trait ProdFunctor[F[_], G[_]] extends Functor[Lambda[X => Prod[F, G, X]]] { def F: Functor[F] def G: Functor[G] - override def map[A, B](fa: Prod[F, G, A])(f: A => B): Prod[F, G, B] = Prod(F.map(fa.first)(f), G.map(fa.second)(f)) + def map[A, B](fa: Prod[F, G, A])(f: A => B): Prod[F, G, B] = Prod(F.map(fa.first)(f), G.map(fa.second)(f)) } sealed trait ProdApply[F[_], G[_]] extends Apply[Lambda[X => Prod[F, G, X]]] with ProdFunctor[F, G] { def F: Apply[F] def G: Apply[G] - override def ap[A, B](fa: Prod[F, G, A])(f: Prod[F, G, A => B]): Prod[F, G, B] = + def ap[A, B](fa: Prod[F, G, A])(f: Prod[F, G, A => B]): Prod[F, G, B] = Prod(F.ap(fa.first)(f.first), G.ap(fa.second)(f.second)) + def product[A, B](fa: Prod[F, G, A], fb: Prod[F, G, B]): Prod[F, G, (A, B)] = + Prod(F.product(fa.first, fb.first), G.product(fa.second, fb.second)) } sealed trait ProdApplicative[F[_], G[_]] extends Applicative[Lambda[X => Prod[F, G, X]]] with ProdApply[F, G] { def F: Applicative[F] def G: Applicative[G] - override def pure[A](a: A): Prod[F, G, A] = Prod(F.pure(a), G.pure(a)) + def pure[A](a: A): Prod[F, G, A] = Prod(F.pure(a), G.pure(a)) } sealed trait ProdSemigroupK[F[_], G[_]] extends SemigroupK[Lambda[X => Prod[F, G, X]]] { diff --git a/core/src/main/scala/cats/data/Validated.scala b/core/src/main/scala/cats/data/Validated.scala index 7e135c4374..c94e38d65e 100644 --- a/core/src/main/scala/cats/data/Validated.scala +++ b/core/src/main/scala/cats/data/Validated.scala @@ -111,7 +111,6 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable { b => that.fold(_ => false, AA.eqv(b, _)) ) - /** * From Apply: * if both the function and this value are Valid, apply the function @@ -119,7 +118,18 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable { def ap[EE >: E, B](f: Validated[EE, A => B])(implicit EE: Semigroup[EE]): Validated[EE,B] = (this, f) match { case (Valid(a), Valid(f)) => Valid(f(a)) - case (Invalid(e1), Invalid(e2)) => Invalid(EE.combine(e2,e1)) + case (Invalid(e1), Invalid(e2)) => Invalid(EE.combine(e2, e1)) + case (e@Invalid(_), _) => e + case (_, e@Invalid(_)) => e + } + + /** + * From Product + */ + def product[EE >: E, B](fb: Validated[EE, B])(implicit EE: Semigroup[EE]): Validated[EE, (A, B)] = + (this, fb) match { + case (Valid(a), Valid(b)) => Valid((a, b)) + case (Invalid(e1), Invalid(e2)) => Invalid(EE.combine(e1, e2)) case (e @ Invalid(_), _) => e case (_, e @ Invalid(_)) => e } @@ -196,8 +206,11 @@ sealed abstract class ValidatedInstances extends ValidatedInstances1 { override def map[A, B](fa: Validated[E,A])(f: A => B): Validated[E, B] = fa.map(f) - override def ap[A,B](fa: Validated[E,A])(f: Validated[E,A=>B]): Validated[E, B] = + def ap[A,B](fa: Validated[E,A])(f: Validated[E,A=>B]): Validated[E, B] = fa.ap(f)(E) + + def product[A, B](fa: Validated[E, A], fb: Validated[E, B]): Validated[E, (A, B)] = + fa.product(fb)(E) } } diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index 08ba6c48cf..c94fbaae9d 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -3,6 +3,7 @@ package syntax trait AllSyntax extends ApplySyntax + with MonoidalSyntax with BifunctorSyntax with CoflatMapSyntax with ComonadSyntax diff --git a/core/src/main/scala/cats/syntax/apply.scala b/core/src/main/scala/cats/syntax/apply.scala index 95b89b0bd9..650c44c613 100644 --- a/core/src/main/scala/cats/syntax/apply.scala +++ b/core/src/main/scala/cats/syntax/apply.scala @@ -2,31 +2,17 @@ package cats package syntax trait ApplySyntax1 { - implicit def applySyntaxU[FA](fa: FA)(implicit U: Unapply[Apply, FA]): ApplyOps[U.M, U.A] = - new ApplyOps[U.M, U.A] { + implicit def applySyntaxU[FA](fa: FA)(implicit U: Unapply[Apply, FA]): Apply.Ops[U.M, U.A] = + new Apply.Ops[U.M, U.A] { val self = U.subst(fa) val typeClassInstance = U.TC } } trait ApplySyntax extends ApplySyntax1 { - implicit def applySyntax[F[_], A](fa: F[A])(implicit F: Apply[F]): ApplyOps[F, A] = - new ApplyOps[F,A] { + implicit def applySyntax[F[_], A](fa: F[A])(implicit F: Apply[F]): Apply.Ops[F, A] = + new Apply.Ops[F,A] { val self = fa val typeClassInstance = F } } - -abstract class ApplyOps[F[_], A] extends Apply.Ops[F, A] { - def |@|[B](fb: F[B]): ApplyBuilder[F]#ApplyBuilder2[A, B] = new ApplyBuilder[F] |@| self |@| fb - - /** - * combine both contexts but only return the right value - */ - def *>[B](fb: F[B]): F[B] = typeClassInstance.map2(self, fb)((a,b) => b) - - /** - * combine both contexts but only return the left value - */ - def <*[B](fb: F[B]): F[A] = typeClassInstance.map2(self, fb)((a,b) => a) -} diff --git a/core/src/main/scala/cats/syntax/monoidal.scala b/core/src/main/scala/cats/syntax/monoidal.scala new file mode 100644 index 0000000000..b4126200b5 --- /dev/null +++ b/core/src/main/scala/cats/syntax/monoidal.scala @@ -0,0 +1,28 @@ +package cats +package syntax + +trait MonoidalSyntax1 { + implicit def monoidalSyntaxU[FA](fa: FA)(implicit U: Unapply[Monoidal, FA]): MonoidalOps[U.M, U.A] = + new MonoidalOps[U.M, U.A] { + val self = U.subst(fa) + val typeClassInstance = U.TC + } +} + +trait MonoidalSyntax extends MonoidalSyntax1 { + implicit def monoidalSyntax[F[_], A](fa: F[A])(implicit F: Monoidal[F]): MonoidalOps[F, A] = + new MonoidalOps[F, A] { + val self = fa + val typeClassInstance = F + } +} + +abstract class MonoidalOps[F[_], A] extends Monoidal.Ops[F, A] { + def |@|[B](fb: F[B]): MonoidalBuilder[F]#MonoidalBuilder2[A, B] = + new MonoidalBuilder[F] |@| self |@| fb + + def *>[B](fb: F[B])(implicit F: Functor[F]): F[B] = F.map(typeClassInstance.product(self, fb)) { case (a, b) => b } + + def <*[B](fb: F[B])(implicit F: Functor[F]): F[A] = F.map(typeClassInstance.product(self, fb)) { case (a, b) => a } + +} diff --git a/core/src/main/scala/cats/syntax/package.scala b/core/src/main/scala/cats/syntax/package.scala index f94e6d9e36..31fd5f5c8d 100644 --- a/core/src/main/scala/cats/syntax/package.scala +++ b/core/src/main/scala/cats/syntax/package.scala @@ -3,6 +3,7 @@ package cats package object syntax { object all extends AllSyntax object apply extends ApplySyntax + object monoidal extends MonoidalSyntax object bifunctor extends BifunctorSyntax object coflatMap extends CoflatMapSyntax object comonad extends ComonadSyntax diff --git a/docs/src/main/tut/apply.md b/docs/src/main/tut/apply.md index 4650c3b326..49968a0968 100644 --- a/docs/src/main/tut/apply.md +++ b/docs/src/main/tut/apply.md @@ -25,6 +25,9 @@ implicit val optionApply: Apply[Option] = new Apply[Option] { fa.flatMap (a => f.map (ff => ff(a))) def map[A,B](fa: Option[A])(f: A => B): Option[B] = fa map f + + def product[A, B](fa: Option[A], fb: Option[B]): Option[(A, B)] = + fa.flatMap(a => fb.map(b => (a, b))) } implicit val listApply: Apply[List] = new Apply[List] { @@ -32,6 +35,9 @@ implicit val listApply: Apply[List] = new Apply[List] { fa.flatMap (a => f.map (ff => ff(a))) def map[A,B](fa: List[A])(f: A => B): List[B] = fa map f + + def product[A, B](fa: List[A], fb: List[B]): List[(A, B)] = + fa.zip(fb) } ``` @@ -118,7 +124,7 @@ In order to use it, first import `cats.syntax.all._` or `cats.syntax.apply._`. Here we see that the following two functions, `f1` and `f2`, are equivalent: ```tut -import cats.syntax.apply._ +import cats.syntax.monoidal._ def f1(a: Option[Int], b: Option[Int], c: Option[Int]) = (a |@| b |@| c) map { _ * _ * _ } @@ -133,7 +139,7 @@ f2(Some(1), Some(2), Some(3)) All instances created by `|@|` have `map`, `ap`, and `tupled` methods of the appropriate arity: ```tut -import cats.syntax.apply._ +import cats.syntax.monoidal._ val option2 = Option(1) |@| Option(2) val option3 = option2 |@| Option.empty[Int] diff --git a/docs/src/main/tut/const.md b/docs/src/main/tut/const.md index 0c29ddc3e6..e8e5ada2d4 100644 --- a/docs/src/main/tut/const.md +++ b/docs/src/main/tut/const.md @@ -219,13 +219,17 @@ implicit def constApplicative[Z]: Applicative[Const[Z, ?]] = def pure[A](a: A): Const[Z, A] = ??? def ap[A, B](fa: Const[Z, A])(f: Const[Z, A => B]): Const[Z, B] = ??? + + def map[A, B](fa: Const[Z, A])(f: A => B): Const[Z, B] = ??? + + def product[A, B](fa: Const[Z, A],fb: Const[Z, B]): Const[Z, (A, B)] = ??? } ``` Recall that `Const[Z, A]` means we have a `Z` value in hand, and don't really care about the `A` type parameter. Therefore we can more or less treat the type `Const[Z, A]` as just `Z`. -In both functions we have a problem. In `pure`, we have an `A` value, but want to return a `Z` value. We have +In functions `pure` and `ap` we have a problem. In `pure`, we have an `A` value, but want to return a `Z` value. We have no function `A => Z`, so our only option is to completely ignore the `A` value. But we still don't have a `Z`! Let's put that aside for now, but still keep it in the back of our minds. @@ -242,6 +246,12 @@ implicit def constApplicative[Z : Monoid]: Applicative[Const[Z, ?]] = def ap[A, B](fa: Const[Z, A])(f: Const[Z, A => B]): Const[Z, B] = Const(Monoid[Z].combine(fa.getConst, f.getConst)) + + def map[A, B](fa: Const[Z, A])(f: A => B): Const[Z, B] = + Const(fa.getConst) + + def product[A, B](fa: Const[Z, A],fb: Const[Z, B]): Const[Z, (A, B)] = + Const(Monoid[Z].combine(fa.getConst, fb.getConst)) } ``` diff --git a/docs/src/main/tut/validated.md b/docs/src/main/tut/validated.md index 57de21abcd..2b81d54fcd 100644 --- a/docs/src/main/tut/validated.md +++ b/docs/src/main/tut/validated.md @@ -197,6 +197,8 @@ implicit def validatedApplicative[E : Semigroup]: Applicative[Validated[E, ?]] = } def pure[A](x: A): Validated[E, A] = Validated.valid(x) + def map[A, B](fa: Validated[E, A])(f: A => B): Validated[E, B] = fa.map(f) + def product[A, B](fa: Validated[E, A], fb: Validated[E, B]): Validated[E, (A, B)] = ap(fb)(fa.map(a => b => (a, b))) } ``` diff --git a/free/src/main/scala/cats/free/FreeApplicative.scala b/free/src/main/scala/cats/free/FreeApplicative.scala index 399ae67eaf..5d4a815ab0 100644 --- a/free/src/main/scala/cats/free/FreeApplicative.scala +++ b/free/src/main/scala/cats/free/FreeApplicative.scala @@ -81,7 +81,9 @@ object FreeApplicative { implicit final def freeApplicative[S[_]]: Applicative[FA[S, ?]] = { new Applicative[FA[S, ?]] { - def ap[A, B](fa: FA[S, A])(f: FA[S, A => B]): FA[S, B] = fa.ap(f) + def product[A, B](fa: FA[S, A], fb: FA[S, B]): FA[S, (A, B)] = ap(fb)(fa.map(a => b => (a, b))) + def map[A, B](fa: FA[S, A])(f: A => B): FA[S, B] = fa.map(f) + override def ap[A, B](fa: FA[S, A])(f: FA[S, A => B]): FA[S, B] = fa.ap(f) def pure[A](a: A): FA[S, A] = Pure(a) } } diff --git a/laws/src/main/scala/cats/laws/MonoidalLaws.scala b/laws/src/main/scala/cats/laws/MonoidalLaws.scala new file mode 100644 index 0000000000..71e3de77c1 --- /dev/null +++ b/laws/src/main/scala/cats/laws/MonoidalLaws.scala @@ -0,0 +1,16 @@ +package cats.laws + +import cats.Monoidal + +trait MonoidalLaws[F[_]] { + + implicit def F: Monoidal[F] + +} + +object MonoidalLaws { + + def apply[F[_]](implicit ev: Monoidal[F]): MonoidalLaws[F] = + new MonoidalLaws[F] { def F: Monoidal[F] = ev } + +} \ No newline at end of file diff --git a/laws/src/main/scala/cats/laws/TraverseLaws.scala b/laws/src/main/scala/cats/laws/TraverseLaws.scala index 228fcdfb30..aaed8523f0 100644 --- a/laws/src/main/scala/cats/laws/TraverseLaws.scala +++ b/laws/src/main/scala/cats/laws/TraverseLaws.scala @@ -39,12 +39,21 @@ trait TraverseLaws[F[_]] extends FunctorLaws[F] with FoldableLaws[F] { ): IsEq[(M[F[B]], N[F[B]])] = { type MN[Z] = (M[Z], N[Z]) implicit val MN = new Applicative[MN] { - override def pure[X](x: X): MN[X] = (M.pure(x), N.pure(x)) - override def ap[X, Y](fa: MN[X])(f: MN[X => Y]): MN[Y] = { + def pure[X](x: X): MN[X] = (M.pure(x), N.pure(x)) + def ap[X, Y](fa: MN[X])(f: MN[X => Y]): MN[Y] = { val (fam, fan) = fa val (fm, fn) = f (M.ap(fam)(fm), N.ap(fan)(fn)) } + def map[X, Y](fx: MN[X])(f: X => Y): MN[Y] = { + val (mx, nx) = fx + (M.map(mx)(f), N.map(nx)(f)) + } + def product[X, Y](fx: MN[X], fy: MN[Y]): MN[(X, Y)] = { + val (mx, nx) = fx + val (my, ny) = fy + (M.product(mx, my), N.product(nx, ny)) + } } val lhs: MN[F[B]] = fa.traverse[MN, B](a => (f(a), g(a))) val rhs: MN[F[B]] = (fa.traverse(f), fa.traverse(g)) diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index cdb87f4690..eb9241d403 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -25,7 +25,8 @@ object Boilerplate { val templates: Seq[Template] = Seq( - GenApplyBuilders, + GenMonoidalBuilders, + GenMonoidalArityFunctions, GenApplyArityFunctions ) @@ -84,8 +85,8 @@ object Boilerplate { The block otherwise behaves as a standard interpolated string with regards to variable substitution. */ - object GenApplyBuilders extends Template { - def filename(root: File) = root / "cats" / "syntax" / "ApplyBuilder.scala" + object GenMonoidalBuilders extends Template { + def filename(root: File) = root / "cats" / "syntax" / "MonoidalBuilder.scala" def content(tv: TemplateVals) = { import tv._ @@ -94,15 +95,27 @@ object Boilerplate { val tpesString = synTypes mkString ", " val params = (synVals zip tpes) map { case (v,t) => s"$v:$t"} mkString ", " val next = if (arity + 1 <= maxArity) { - s"def |@|[Z](z: F[Z]) = new ApplyBuilder${arity + 1}(${`a..n`}, z)" + s"def |@|[Z](z: F[Z]) = new MonoidalBuilder${arity + 1}(${`a..n`}, z)" } else { "" } val n = if (arity == 1) { "" } else { arity.toString } + val map = + if (arity == 1) s"def map[Z](f: (${`A..N`}) => Z)(implicit functor: Functor[F]): F[Z] = functor.map(${`a..n`})(f)" + else s"def map[Z](f: (${`A..N`}) => Z)(implicit functor: Functor[F], monoidal: Monoidal[F]): F[Z] = Monoidal.map$n(${`a..n`})(f)" + + val contramap = + if (arity == 1) s"def contramap[Z](f: Z => (${`A..N`}))(implicit contravariant: Contravariant[F]): F[Z] = contravariant.contramap(${`a..n`})(f)" + else s"def contramap[Z](f: Z => (${`A..N`}))(implicit contravariant: Contravariant[F], monoidal: Monoidal[F]): F[Z] = Monoidal.contramap$n(${`a..n`})(f)" + + val imap = + if (arity == 1) s"def imap[Z](f: (${`A..N`}) => Z)(g: Z => (${`A..N`}))(implicit invariant: Invariant[F]): F[Z] = invariant.imap(${`a..n`})(f)(g)" + else s"def imap[Z](f: (${`A..N`}) => Z)(g: Z => (${`A..N`}))(implicit invariant: Invariant[F], monoidal: Monoidal[F]): F[Z] = Monoidal.imap$n(${`a..n`})(f)(g)" + val tupled = if (arity != 1) { - s"def tupled(implicit F: Apply[F]): F[(${`A..N`})] = F.tuple$n(${`a..n`})" + s"def tupled(implicit invariant: Invariant[F], monoidal: Monoidal[F]): F[(${`A..N`})] = Monoidal.tuple$n(${`a..n`})" } else { "" } @@ -111,13 +124,17 @@ object Boilerplate { |package cats |package syntax | - |private[syntax] class ApplyBuilder[F[_]] { - | def |@|[A](a: F[A]) = new ApplyBuilder1(a) + |import cats.functor.{Contravariant, Invariant} | - - private[syntax] class ApplyBuilder$arity[${`A..N`}](${params}) { + |private[syntax] class MonoidalBuilder[F[_]] { + | def |@|[A](a: F[A]) = new MonoidalBuilder1(a) + | + - private[syntax] class MonoidalBuilder$arity[${`A..N`}]($params) { - $next - - def ap[Z](f: F[(${`A..N`}) => Z])(implicit F: Apply[F]): F[Z] = F.ap$n(${`a..n`})(f) - - def map[Z](f: (${`A..N`}) => Z)(implicit F: Apply[F]): F[Z] = F.map$n(${`a..n`})(f) + - def ap[Z](f: F[(${`A..N`}) => Z])(implicit apply: Apply[F]): F[Z] = apply.ap$n(${`a..n`})(f) + - $map + - $contramap + - $imap - $tupled - } |} @@ -132,9 +149,7 @@ object Boilerplate { import tv._ val tpes = synTypes map { tpe => s"F[$tpe]" } - val tpesString = synTypes mkString ", " val fargs = (0 until arity) map { "f" + _ } - val fargsS = fargs mkString ", " val fparams = (fargs zip tpes) map { case (v,t) => s"$v:$t"} mkString ", " val a = arity / 2 @@ -147,15 +162,6 @@ object Boilerplate { def apN(n: Int) = if (n == 1) { "ap" } else { s"ap$n" } def allArgs = (0 until arity) map { "a" + _ } mkString "," - val map = if (arity == 3) { - " ap(f2)(map2(f0, f1)((a, b) => c => f(a, b, c)))" - } else { - block""" - - map2(tuple$a($fArgsA), tuple$b($fArgsB)) { - - case (($argsA), ($argsB)) => f($allArgs) - - } - """ - } val apply = block""" - ${apN(b)}($fArgsB)(${apN(a)}($fArgsA)(map(f)(f => @@ -166,15 +172,43 @@ object Boilerplate { block""" |package cats |trait ApplyArityFunctions[F[_]] { self: Apply[F] => - | def tuple2[A, B](fa: F[A], fb: F[B]): F[(A, B)] = map2(fa, fb)((_, _)) - | + | def tuple2[A, B](f1: F[A], f2: F[B]): F[(A, B)] = Monoidal.tuple2(f1, f2)(self, self) - def ap$arity[${`A..N`}, Z]($fparams)(f: F[(${`A..N`}) => Z]):F[Z] = $apply - - def map$arity[${`A..N`}, Z]($fparams)(f: (${`A..N`}) => Z):F[Z] = $map - - def tuple$arity[${`A..N`}]($fparams):F[(${`A..N`})] = - - map$arity($fargsS)((${`_.._`})) + - def map$arity[${`A..N`}, Z]($fparams)(f: (${`A..N`}) => Z): F[Z] = Monoidal.map$arity($fparams)(f)(self, self) + - def tuple$arity[${`A..N`}, Z]($fparams): F[(${`A..N`})] = Monoidal.tuple$arity($fparams)(self, self) |} """ } } + object GenMonoidalArityFunctions extends Template { + def filename(root: File) = root / "cats" / "MonoidalArityFunctions.scala" + override def range = 2 to maxArity + def content(tv: TemplateVals) = { + import tv._ + + val tpes = synTypes map { tpe => s"F[$tpe]" } + val fargs = (0 until arity) map { "f" + _ } + val fparams = (fargs zip tpes) map { case (v,t) => s"$v:$t"} mkString ", " + val fargsS = fargs mkString ", " + + val nestedProducts = (0 until (arity - 2)).foldRight(s"monoidal.product(f${arity - 2}, f${arity - 1})")((i, acc) => s"monoidal.product(f$i, $acc)") + val `nested (a..n)` = (0 until (arity - 2)).foldRight(s"(a${arity - 2}, a${arity - 1})")((i, acc) => s"(a$i, $acc)") + + block""" + |package cats + |trait MonoidalArityFunctions { + - def map$arity[F[_], ${`A..N`}, Z]($fparams)(f: (${`A..N`}) => Z)(implicit monoidal: Monoidal[F], functor: Functor[F]): F[Z] = + - functor.map($nestedProducts) { case ${`nested (a..n)`} => f(${`a..n`}) } + - def contramap$arity[F[_], ${`A..N`}, Z]($fparams)(f: Z => (${`A..N`}))(implicit monoidal: Monoidal[F], contravariant: functor.Contravariant[F]):F[Z] = + - contravariant.contramap($nestedProducts) { z => val ${`(a..n)`} = f(z); ${`nested (a..n)`} } + - def imap$arity[F[_], ${`A..N`}, Z]($fparams)(f: (${`A..N`}) => Z)(g: Z => (${`A..N`}))(implicit monoidal: Monoidal[F], invariant: functor.Invariant[F]):F[Z] = + - invariant.imap($nestedProducts) { case ${`nested (a..n)`} => f(${`a..n`}) } { z => val ${`(a..n)`} = g(z); ${`nested (a..n)`} } + - def tuple$arity[F[_], ${`A..N`}]($fparams)(implicit monoidal: Monoidal[F], invariant: functor.Invariant[F]):F[(${`A..N`})] = + - imap$arity($fargsS)((${`_.._`}))(identity) + |} + """ + } + } + } diff --git a/state/src/test/scala/cats/state/StateTTests.scala b/state/src/test/scala/cats/state/StateTTests.scala index 5368b122c2..8fe855fb62 100644 --- a/state/src/test/scala/cats/state/StateTTests.scala +++ b/state/src/test/scala/cats/state/StateTTests.scala @@ -27,7 +27,7 @@ class StateTTests extends CatsSuite { } } - test("Apply syntax is usable on State") { + test("Monoidal syntax is usable on State") { val x = add1 *> add1 x.runS(0).run should === (2) } From 1073bbe1c69fb502776c45e74d4b80a8bbf2e6a5 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Thu, 15 Oct 2015 18:36:41 +0200 Subject: [PATCH 2/6] wip laws --- laws/src/main/scala/cats/laws/ApplyLaws.scala | 2 +- .../main/scala/cats/laws/MonoidalLaws.scala | 26 +++++++++++++---- .../cats/laws/discipline/MonoidalTests.scala | 28 +++++++++++++++++++ 3 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala diff --git a/laws/src/main/scala/cats/laws/ApplyLaws.scala b/laws/src/main/scala/cats/laws/ApplyLaws.scala index 8f71da8114..c6c2a76859 100644 --- a/laws/src/main/scala/cats/laws/ApplyLaws.scala +++ b/laws/src/main/scala/cats/laws/ApplyLaws.scala @@ -7,7 +7,7 @@ import cats.syntax.functor._ /** * Laws that must be obeyed by any `Apply`. */ -trait ApplyLaws[F[_]] extends FunctorLaws[F] { +trait ApplyLaws[F[_]] extends FunctorLaws[F] with MonoidalLaws[F] { implicit override def F: Apply[F] def applyComposition[A, B, C](fa: F[A], fab: F[A => B], fbc: F[B => C]): IsEq[F[C]] = { diff --git a/laws/src/main/scala/cats/laws/MonoidalLaws.scala b/laws/src/main/scala/cats/laws/MonoidalLaws.scala index 71e3de77c1..eccc435799 100644 --- a/laws/src/main/scala/cats/laws/MonoidalLaws.scala +++ b/laws/src/main/scala/cats/laws/MonoidalLaws.scala @@ -1,16 +1,32 @@ -package cats.laws +package cats +package laws -import cats.Monoidal +import functor.Contravariant trait MonoidalLaws[F[_]] { - implicit def F: Monoidal[F] + implicit def F: Monoidal[F] with Functor[F] + + def covariantProductAssociativity[A, B, C](fa: F[A], fb: F[B], fc: F[C]): IsEq[F[(A, B, C)]] = + F.map(F.product(fa, F.product(fb, fc))) { case (a, (b, c)) => (a, b, c) } <-> F.map(F.product(F.product(fa, fb), fc)) { case ((a, b), c) => (a, b, c) } + +} + +trait ContravariantMonoidalLaws[F[_]] { + + implicit def F: Monoidal[F] with Contravariant[F] + + def contravariantProductAssociativity[A, B, C](fa: F[A], fb: F[B], fc: F[C]): IsEq[F[(A, B, C)]] = + F.contramap[(A, (B, C)), (A, B, C)](F.product(fa, F.product(fb, fc))) { case (a, b, c) => (a, (b, c)) } <-> F.contramap[((A, B), C), (A, B, C)](F.product(F.product(fa, fb), fc)) { case (a, b, c) => ((a, b), c) } } object MonoidalLaws { - def apply[F[_]](implicit ev: Monoidal[F]): MonoidalLaws[F] = - new MonoidalLaws[F] { def F: Monoidal[F] = ev } + def covariant[F[_]](implicit ev: Monoidal[F] with Functor[F]): MonoidalLaws[F] = + new MonoidalLaws[F] { val F: Monoidal[F] with Functor[F] = ev } + + def contravariant[F[_]](implicit ev: Monoidal[F] with Contravariant[F]): ContravariantMonoidalLaws[F] = + new ContravariantMonoidalLaws[F] { val F: Monoidal[F] with Contravariant[F] = ev } } \ No newline at end of file diff --git a/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala b/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala new file mode 100644 index 0000000000..56dc6653b2 --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala @@ -0,0 +1,28 @@ +package cats +package laws +package discipline + +import org.scalacheck.Arbitrary +import org.scalacheck.Prop +import Prop._ +import org.typelevel.discipline.Laws + +trait MonoidalTests[F[_]] extends Laws { + def laws: MonoidalLaws[F] + + def monoidal[A : Arbitrary, B : Arbitrary, C : Arbitrary](implicit ArbF: ArbitraryK[F], EqFA: Eq[F[A]], EqFB: Eq[F[B]], EqFC: Eq[F[C]]): RuleSet = { + implicit def ArbFA: Arbitrary[F[A]] = ArbF.synthesize[A] + implicit def ArbFB: Arbitrary[F[B]] = ArbF.synthesize[B] + implicit def ArbFC: Arbitrary[F[C]] = ArbF.synthesize[C] + new DefaultRuleSet( + name = "monoidal", + parent = None, + "invariant associativity" -> forAll(laws.covariantProductAssociativity[A, B, C] _) + ) + } +} + +object MonoidalTests { + def apply[F[_] : Monoidal : Functor]: MonoidalTests[F] = + new MonoidalTests[F] { def laws: MonoidalLaws[F] = MonoidalLaws.covariant[F] } +} \ No newline at end of file From f195da085e7ff29d48c0efab5f261b514affd399 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 18 Nov 2015 23:07:30 +0100 Subject: [PATCH 3/6] Add Monoidal laws: left identity, right identity and associativity. --- laws/src/main/scala/cats/laws/ApplyLaws.scala | 2 +- .../main/scala/cats/laws/MonoidalLaws.scala | 26 ++++-------- .../cats/laws/discipline/Arbitrary.scala | 3 ++ .../main/scala/cats/laws/discipline/Eq.scala | 6 +++ .../cats/laws/discipline/MonoidalTests.scala | 41 +++++++++++++++---- .../scala/cats/tests/CokleisliTests.scala | 4 ++ .../test/scala/cats/tests/ConstTests.scala | 7 +++- .../test/scala/cats/tests/EitherTests.scala | 8 +++- .../src/test/scala/cats/tests/FuncTests.scala | 5 +++ .../test/scala/cats/tests/FunctionTests.scala | 8 ++++ .../src/test/scala/cats/tests/IorTests.scala | 7 +++- .../test/scala/cats/tests/KleisliTests.scala | 4 ++ .../src/test/scala/cats/tests/ListTests.scala | 7 +++- .../src/test/scala/cats/tests/MapTests.scala | 7 +++- .../test/scala/cats/tests/OneAndTests.scala | 11 ++++- .../test/scala/cats/tests/OptionTTests.scala | 11 +++-- .../test/scala/cats/tests/OptionTests.scala | 6 ++- .../src/test/scala/cats/tests/ProdTests.scala | 5 +++ .../test/scala/cats/tests/StreamTests.scala | 6 ++- .../scala/cats/tests/StreamingTests.scala | 6 ++- .../scala/cats/tests/ValidatedTests.scala | 7 +++- .../test/scala/cats/tests/VectorTests.scala | 6 ++- .../src/test/scala/cats/tests/XorTests.scala | 7 +++- 23 files changed, 159 insertions(+), 41 deletions(-) diff --git a/laws/src/main/scala/cats/laws/ApplyLaws.scala b/laws/src/main/scala/cats/laws/ApplyLaws.scala index c6c2a76859..8f71da8114 100644 --- a/laws/src/main/scala/cats/laws/ApplyLaws.scala +++ b/laws/src/main/scala/cats/laws/ApplyLaws.scala @@ -7,7 +7,7 @@ import cats.syntax.functor._ /** * Laws that must be obeyed by any `Apply`. */ -trait ApplyLaws[F[_]] extends FunctorLaws[F] with MonoidalLaws[F] { +trait ApplyLaws[F[_]] extends FunctorLaws[F] { implicit override def F: Apply[F] def applyComposition[A, B, C](fa: F[A], fab: F[A => B], fbc: F[B => C]): IsEq[F[C]] = { diff --git a/laws/src/main/scala/cats/laws/MonoidalLaws.scala b/laws/src/main/scala/cats/laws/MonoidalLaws.scala index eccc435799..474671a6e2 100644 --- a/laws/src/main/scala/cats/laws/MonoidalLaws.scala +++ b/laws/src/main/scala/cats/laws/MonoidalLaws.scala @@ -1,32 +1,24 @@ package cats package laws -import functor.Contravariant - trait MonoidalLaws[F[_]] { - implicit def F: Monoidal[F] with Functor[F] - - def covariantProductAssociativity[A, B, C](fa: F[A], fb: F[B], fc: F[C]): IsEq[F[(A, B, C)]] = - F.map(F.product(fa, F.product(fb, fc))) { case (a, (b, c)) => (a, b, c) } <-> F.map(F.product(F.product(fa, fb), fc)) { case ((a, b), c) => (a, b, c) } + implicit def F: Monoidal[F] -} + def associativity[A, B, C](fa: F[A], fb: F[B], fc: F[C]): (F[(A, (B, C))], F[((A, B), C)]) = + (F.product(fa, F.product(fb, fc)), F.product(F.product(fa, fb), fc)) -trait ContravariantMonoidalLaws[F[_]] { + def leftIdentity[A](funit: F[Unit], fa: F[A]): (F[(Unit, A)], F[A]) = + (F.product(funit, fa), fa) - implicit def F: Monoidal[F] with Contravariant[F] - - def contravariantProductAssociativity[A, B, C](fa: F[A], fb: F[B], fc: F[C]): IsEq[F[(A, B, C)]] = - F.contramap[(A, (B, C)), (A, B, C)](F.product(fa, F.product(fb, fc))) { case (a, b, c) => (a, (b, c)) } <-> F.contramap[((A, B), C), (A, B, C)](F.product(F.product(fa, fb), fc)) { case (a, b, c) => ((a, b), c) } + def rightIdentity[A](fa: F[A], funit: F[Unit]): (F[(A, Unit)], F[A]) = + (F.product(fa, funit), fa) } object MonoidalLaws { - def covariant[F[_]](implicit ev: Monoidal[F] with Functor[F]): MonoidalLaws[F] = - new MonoidalLaws[F] { val F: Monoidal[F] with Functor[F] = ev } - - def contravariant[F[_]](implicit ev: Monoidal[F] with Contravariant[F]): ContravariantMonoidalLaws[F] = - new ContravariantMonoidalLaws[F] { val F: Monoidal[F] with Contravariant[F] = ev } + def apply[F[_]](implicit ev: Monoidal[F]): MonoidalLaws[F] = + new MonoidalLaws[F] { val F = ev } } \ No newline at end of file diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index 25f7320c89..aa26cf5f29 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -82,4 +82,7 @@ object arbitrary { // until this is provided by scalacheck implicit def partialFunctionArbitrary[A, B](implicit F: Arbitrary[A => Option[B]]): Arbitrary[PartialFunction[A, B]] = Arbitrary(F.arbitrary.map(Function.unlift)) + + implicit def thunkArbitrary[A](implicit F: Arbitrary[A]): Arbitrary[() => A] = + Arbitrary(F.arbitrary.map(a => () => a)) } diff --git a/laws/src/main/scala/cats/laws/discipline/Eq.scala b/laws/src/main/scala/cats/laws/discipline/Eq.scala index 1e2ce7ef8e..d79a1a6a84 100644 --- a/laws/src/main/scala/cats/laws/discipline/Eq.scala +++ b/laws/src/main/scala/cats/laws/discipline/Eq.scala @@ -21,12 +21,18 @@ object eq { } } + // Temporary, see https://github.com/non/algebra/pull/82 implicit def tuple2Eq[A, B](implicit A: Eq[A], B: Eq[B]): Eq[(A, B)] = new Eq[(A, B)] { def eqv(x: (A, B), y: (A, B)): Boolean = A.eqv(x._1, y._1) && B.eqv(x._2, y._2) } + implicit def tuple3Eq[A, B, C](implicit EqA: Eq[A], EqB: Eq[B], EqC: Eq[C]): Eq[(A, B, C)] = + new Eq[(A, B, C)] { + def eqv(x: (A, B, C), y: (A, B, C)): Boolean = EqA.eqv(x._1, y._1) && EqB.eqv(x._2, y._2) && EqC.eqv(x._3, y._3) + } + /** * Create an approximation of Eq[Semigroup[A]] by generating values for A * and comparing the application of the two combine functions. diff --git a/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala b/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala index 56dc6653b2..b480103438 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala @@ -10,19 +10,46 @@ import org.typelevel.discipline.Laws trait MonoidalTests[F[_]] extends Laws { def laws: MonoidalLaws[F] - def monoidal[A : Arbitrary, B : Arbitrary, C : Arbitrary](implicit ArbF: ArbitraryK[F], EqFA: Eq[F[A]], EqFB: Eq[F[B]], EqFC: Eq[F[C]]): RuleSet = { - implicit def ArbFA: Arbitrary[F[A]] = ArbF.synthesize[A] - implicit def ArbFB: Arbitrary[F[B]] = ArbF.synthesize[B] - implicit def ArbFC: Arbitrary[F[C]] = ArbF.synthesize[C] + def monoidal[A : Arbitrary, B : Arbitrary, C : Arbitrary](implicit + iso: MonoidalTests.Isomorphisms[F], + ArbFA: Arbitrary[F[A]], + ArbFB: Arbitrary[F[B]], + ArbFC: Arbitrary[F[C]], + ArbFUnit: Arbitrary[F[Unit]], + EqFA: Eq[F[A]], + EqFABC: Eq[F[(A, B, C)]] + ): RuleSet = { new DefaultRuleSet( name = "monoidal", parent = None, - "invariant associativity" -> forAll(laws.covariantProductAssociativity[A, B, C] _) + "associativity" -> forAll((fa: F[A], fb: F[B], fc: F[C]) => iso.`((a, b), c) ≅ (a, (b, c))`(laws.associativity(fa, fb, fc))), + "left identity" -> forAll((fa: F[A], funit: F[Unit]) => iso.`(unit, a) ≅ a`(laws.leftIdentity(funit, fa))), + "right identity" -> forAll((fa: F[A], funit: F[Unit]) => iso.`(a, unit) ≅ a`(laws.rightIdentity(fa, funit))) ) } } object MonoidalTests { - def apply[F[_] : Monoidal : Functor]: MonoidalTests[F] = - new MonoidalTests[F] { def laws: MonoidalLaws[F] = MonoidalLaws.covariant[F] } + def apply[F[_] : Monoidal](implicit ev: Isomorphisms[F]): MonoidalTests[F] = + new MonoidalTests[F] { val laws: MonoidalLaws[F] = MonoidalLaws[F] } + + trait Isomorphisms[F[_]] { + def `((a, b), c) ≅ (a, (b, c))`[A, B, C](fs: (F[(A, (B, C))], F[((A, B), C)]))(implicit EqFABC: Eq[F[(A, B, C)]]): Prop + def `(unit, a) ≅ a`[A](fs: (F[(Unit, A)], F[A]))(implicit EqFA: Eq[F[A]]): Prop + def `(a, unit) ≅ a`[A](fs: (F[(A, Unit)], F[A]))(implicit EqFA: Eq[F[A]]): Prop + } + + object Isomorphisms { + import algebra.laws._ + implicit def covariant[F[_]](implicit F: Functor[F]): Isomorphisms[F] = + new Isomorphisms[F] { + def `((a, b), c) ≅ (a, (b, c))`[A, B, C](fs: (F[(A, (B, C))], F[((A, B), C)]))(implicit EqFABC: Eq[F[(A, B, C)]]) = + F.map(fs._1) { case (a, (b, c)) => (a, b, c) } ?== F.map(fs._2) { case ((a, b), c) => (a, b, c) } + def `(unit, a) ≅ a`[A](fs: (F[(Unit, A)], F[A]))(implicit EqFA: Eq[F[A]]) = + F.map(fs._1)(_._2) ?== fs._2 + def `(a, unit) ≅ a`[A](fs: (F[(A, Unit)], F[A]))(implicit EqFA: Eq[F[A]]) = + F.map(fs._1)(_._1) ?== fs._2 + } + } + } \ No newline at end of file diff --git a/tests/src/test/scala/cats/tests/CokleisliTests.scala b/tests/src/test/scala/cats/tests/CokleisliTests.scala index f0c91edd3a..b23079e74a 100644 --- a/tests/src/test/scala/cats/tests/CokleisliTests.scala +++ b/tests/src/test/scala/cats/tests/CokleisliTests.scala @@ -19,6 +19,10 @@ class CokleisliTests extends SlowCatsSuite { def cokleisliEqE[F[_], A](implicit A: Arbitrary[F[A]], FA: Eq[A]): Eq[Cokleisli[F, A, A]] = Eq.by[Cokleisli[F, A, A], F[A] => A](_.run) + implicit val iso = MonoidalTests.Isomorphisms.covariant[Cokleisli[Option, Int, ?]] + checkAll("Cokleisli[Option, Int, Int]", MonoidalTests[Cokleisli[Option, Int, ?]].monoidal[Int, Int, Int]) + checkAll("Monoidal[Cokleisli[Option, Int, ?]", SerializableTests.serializable(Monoidal[Cokleisli[Option, Int, ?]])) + checkAll("Cokleisli[Option, Int, Int]", ApplicativeTests[Cokleisli[Option, Int, ?]].applicative[Int, Int, Int]) checkAll("Applicative[Cokleisli[Option, Int, ?]", SerializableTests.serializable(Applicative[Cokleisli[Option, Int, ?]])) diff --git a/tests/src/test/scala/cats/tests/ConstTests.scala b/tests/src/test/scala/cats/tests/ConstTests.scala index b4c1bf8742..1f08c6e34c 100644 --- a/tests/src/test/scala/cats/tests/ConstTests.scala +++ b/tests/src/test/scala/cats/tests/ConstTests.scala @@ -4,10 +4,15 @@ package tests import algebra.laws.{GroupLaws, OrderLaws} import cats.data.{Const, NonEmptyList} -import cats.laws.discipline.{ApplyTests, ApplicativeTests, SerializableTests, TraverseTests} +import cats.laws.discipline._ import cats.laws.discipline.arbitrary.{constArbitrary, oneAndArbitrary} class ConstTests extends CatsSuite { + + implicit val iso = MonoidalTests.Isomorphisms.covariant[Const[String, ?]] + checkAll("Const[String, Int]", MonoidalTests[Const[String, ?]].monoidal[Int, Int, Int]) + checkAll("Monoidal[Const[String, ?]]", SerializableTests.serializable(Monoidal[Const[String, ?]])) + checkAll("Const[String, Int]", ApplicativeTests[Const[String, ?]].applicative[Int, Int, Int]) checkAll("Applicative[Const[String, ?]]", SerializableTests.serializable(Applicative[Const[String, ?]])) diff --git a/tests/src/test/scala/cats/tests/EitherTests.scala b/tests/src/test/scala/cats/tests/EitherTests.scala index 092f1b8038..466e438ff8 100644 --- a/tests/src/test/scala/cats/tests/EitherTests.scala +++ b/tests/src/test/scala/cats/tests/EitherTests.scala @@ -1,10 +1,16 @@ package cats package tests -import cats.laws.discipline.{TraverseTests, MonadTests, SerializableTests} +import cats.laws.discipline.{TraverseTests, MonadTests, SerializableTests, MonoidalTests} +import cats.laws.discipline.eq._ import algebra.laws.OrderLaws class EitherTests extends CatsSuite { + + implicit val iso = MonoidalTests.Isomorphisms.covariant[Either[Int, ?]] + checkAll("Either[Int, Int]", MonoidalTests[Either[Int, ?]].monoidal[Int, Int, Int]) + checkAll("Monoidal[Either[Int, ?]]", SerializableTests.serializable(Monoidal[Either[Int, ?]])) + checkAll("Either[Int, Int]", MonadTests[Either[Int, ?]].monad[Int, Int, Int]) checkAll("Monad[Either[Int, ?]]", SerializableTests.serializable(Monad[Either[Int, ?]])) diff --git a/tests/src/test/scala/cats/tests/FuncTests.scala b/tests/src/test/scala/cats/tests/FuncTests.scala index 58cc648e0c..d627b70d1a 100644 --- a/tests/src/test/scala/cats/tests/FuncTests.scala +++ b/tests/src/test/scala/cats/tests/FuncTests.scala @@ -4,6 +4,7 @@ package tests import cats.data.{ Func, AppFunc, Const } import Func.{ appFunc, appFuncU } import cats.laws.discipline._ +import cats.laws.discipline.arbitrary._ import org.scalacheck.Arbitrary class FuncTests extends CatsSuite { @@ -13,6 +14,10 @@ class FuncTests extends CatsSuite { implicit def appFuncEq[F[_], A, B](implicit A: Arbitrary[A], FB: Eq[F[B]]): Eq[AppFunc[F, A, B]] = Eq.by[AppFunc[F, A, B], A => F[B]](_.run) + implicit val iso = MonoidalTests.Isomorphisms.covariant[Func[Option, Int, ?]] + checkAll("Func[Option, Int, Int]", MonoidalTests[Func[Option, Int, ?]].monoidal[Int, Int, Int]) + checkAll("Monoidal[Func[Option, Int, ?]]", SerializableTests.serializable(Monoidal[Func[Option, Int, ?]])) + { implicit val funcApp = Func.funcApplicative[Option, Int] checkAll("Func[Option, Int, Int]", ApplicativeTests[Func[Option, Int, ?]].applicative[Int, Int, Int]) diff --git a/tests/src/test/scala/cats/tests/FunctionTests.scala b/tests/src/test/scala/cats/tests/FunctionTests.scala index 7cdbd0255d..cc351859ef 100644 --- a/tests/src/test/scala/cats/tests/FunctionTests.scala +++ b/tests/src/test/scala/cats/tests/FunctionTests.scala @@ -7,9 +7,17 @@ import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary._ class FunctionTests extends CatsSuite { + + checkAll("Function0[Int]", MonoidalTests[Function0].monoidal[Int, Int, Int]) + checkAll("Monoidal[Function0]", SerializableTests.serializable(Monoidal[Function0])) + checkAll("Function0[Int]", BimonadTests[Function0].bimonad[Int, Int, Int]) checkAll("Bimonad[Function0]", SerializableTests.serializable(Comonad[Function0])) + implicit val iso = MonoidalTests.Isomorphisms.covariant[Function1[Int, ?]] + checkAll("Function1[Int, Int]", MonoidalTests[Function1[Int, ?]].monoidal[Int, Int, Int]) + checkAll("Monoidal[Function1[Int, ?]]", SerializableTests.serializable(Monoidal[Function1[Int, ?]])) + checkAll("Function1[Int, Int]", MonadReaderTests[Int => ?, Int].monadReader[Int, Int, Int]) checkAll("MonadReader[Int => ?, Int]", SerializableTests.serializable(MonadReader[Int => ?, Int])) diff --git a/tests/src/test/scala/cats/tests/IorTests.scala b/tests/src/test/scala/cats/tests/IorTests.scala index 2a5d06ca3f..cf99ca85cb 100644 --- a/tests/src/test/scala/cats/tests/IorTests.scala +++ b/tests/src/test/scala/cats/tests/IorTests.scala @@ -2,13 +2,18 @@ package cats package tests import cats.data.{Xor, Ior} -import cats.laws.discipline.{BifunctorTests, TraverseTests, MonadTests, SerializableTests} +import cats.laws.discipline.{BifunctorTests, TraverseTests, MonadTests, SerializableTests, MonoidalTests} import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq._ import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary._ class IorTests extends CatsSuite { + + implicit val iso = MonoidalTests.Isomorphisms.covariant[Ior[String, ?]] + checkAll("Ior[String, Int]", MonoidalTests[Ior[String, ?]].monoidal[Int, Int, Int]) + checkAll("Monoidal[String Ior ?]]", SerializableTests.serializable(Monoidal[String Ior ?])) + checkAll("Ior[String, Int]", MonadTests[String Ior ?].monad[Int, Int, Int]) checkAll("Monad[String Ior ?]]", SerializableTests.serializable(Monad[String Ior ?])) diff --git a/tests/src/test/scala/cats/tests/KleisliTests.scala b/tests/src/test/scala/cats/tests/KleisliTests.scala index d7e908b9ec..28f288c1c5 100644 --- a/tests/src/test/scala/cats/tests/KleisliTests.scala +++ b/tests/src/test/scala/cats/tests/KleisliTests.scala @@ -15,6 +15,10 @@ class KleisliTests extends CatsSuite { implicit def kleisliEq[F[_], A, B](implicit A: Arbitrary[A], FB: Eq[F[B]]): Eq[Kleisli[F, A, B]] = Eq.by[Kleisli[F, A, B], A => F[B]](_.run) + implicit val iso = MonoidalTests.Isomorphisms.covariant[Kleisli[Option, Int, ?]] + checkAll("Kleisli[Option, Int, Int]", MonoidalTests[Kleisli[Option, Int, ?]].monoidal[Int, Int, Int]) + checkAll("Monoidal[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Monoidal[Kleisli[Option, Int, ?]])) + { implicit val kleisliArrow = Kleisli.kleisliArrow[Option] checkAll("Kleisli[Option, Int, Int]", ArrowTests[Kleisli[Option, ?, ?]].arrow[Int, Int, Int, Int, Int, Int]) diff --git a/tests/src/test/scala/cats/tests/ListTests.scala b/tests/src/test/scala/cats/tests/ListTests.scala index caf6fc3f56..d01decdf29 100644 --- a/tests/src/test/scala/cats/tests/ListTests.scala +++ b/tests/src/test/scala/cats/tests/ListTests.scala @@ -2,10 +2,15 @@ package cats package tests import cats.data.NonEmptyList -import cats.laws.discipline.{TraverseTests, CoflatMapTests, MonadCombineTests, SerializableTests} +import cats.laws.discipline.{TraverseTests, CoflatMapTests, MonadCombineTests, SerializableTests, MonoidalTests} import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq._ class ListTests extends CatsSuite { + + checkAll("List[Int]", MonoidalTests[List].monoidal[Int, Int, Int]) + checkAll("Monoidal[List]", SerializableTests.serializable(Monoidal[List])) + checkAll("List[Int]", CoflatMapTests[List].coflatMap[Int, Int, Int]) checkAll("CoflatMap[List]", SerializableTests.serializable(CoflatMap[List])) diff --git a/tests/src/test/scala/cats/tests/MapTests.scala b/tests/src/test/scala/cats/tests/MapTests.scala index 6de167aa91..c89da547d9 100644 --- a/tests/src/test/scala/cats/tests/MapTests.scala +++ b/tests/src/test/scala/cats/tests/MapTests.scala @@ -1,9 +1,14 @@ package cats package tests -import cats.laws.discipline.{TraverseTests, FlatMapTests, SerializableTests} +import cats.laws.discipline.{TraverseTests, FlatMapTests, SerializableTests, MonoidalTests} +import cats.laws.discipline.eq._ class MapTests extends CatsSuite { + implicit val iso = MonoidalTests.Isomorphisms.covariant[Map[Int, ?]] + checkAll("Map[Int, Int]", MonoidalTests[Map[Int, ?]].monoidal[Int, Int, Int]) + checkAll("Monoidal[Map[Int, ?]]", SerializableTests.serializable(Monoidal[Map[Int, ?]])) + checkAll("Map[Int, Int]", FlatMapTests[Map[Int, ?]].flatMap[Int, Int, Int]) checkAll("FlatMap[Map[Int, ?]]", SerializableTests.serializable(FlatMap[Map[Int, ?]])) diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index 3a1a057942..4f49ca1c9b 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -4,9 +4,9 @@ package tests import algebra.laws.{GroupLaws, OrderLaws} import cats.data.{NonEmptyList, OneAnd} -import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests} +import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, MonoidalTests} import cats.laws.discipline.arbitrary.{evalArbitrary, oneAndArbitrary} - +import cats.laws.discipline.eq._ import scala.util.Random @@ -14,6 +14,13 @@ class OneAndTests extends CatsSuite { checkAll("OneAnd[Int, List]", OrderLaws[OneAnd[Int, List]].eqv) // Test instances that have more general constraints + { + implicit val monadCombine = ListWrapper.monadCombine + implicit val iso = MonoidalTests.Isomorphisms.covariant[OneAnd[?, ListWrapper]] + checkAll("OneAnd[Int, ListWrapper]", MonoidalTests[OneAnd[?, ListWrapper]].monoidal[Int, Int, Int]) + checkAll("Monoidal[OneAnd[A, ListWrapper]]", SerializableTests.serializable(Monoidal[OneAnd[?, ListWrapper]])) + } + { implicit val functor = ListWrapper.functor checkAll("OneAnd[Int, ListWrapper]", FunctorTests[OneAnd[?, ListWrapper]].functor[Int, Int, Int]) diff --git a/tests/src/test/scala/cats/tests/OptionTTests.scala b/tests/src/test/scala/cats/tests/OptionTTests.scala index a7e36a83af..ccac706273 100644 --- a/tests/src/test/scala/cats/tests/OptionTTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTTests.scala @@ -1,9 +1,10 @@ package cats.tests -import cats.{Id, Monad} +import cats.{Id, Monad, Monoidal} import cats.data.{OptionT, Xor} -import cats.laws.discipline.{MonadTests, SerializableTests} +import cats.laws.discipline.{MonadTests, SerializableTests, MonoidalTests} import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq._ import org.scalacheck.{Arbitrary, Gen} class OptionTTests extends CatsSuite { @@ -111,6 +112,10 @@ class OptionTTests extends CatsSuite { } } + implicit val iso = MonoidalTests.Isomorphisms.covariant[OptionT[List, ?]] + checkAll("OptionT[List, Int]", MonoidalTests[OptionT[List, ?]].monoidal[Int, Int, Int]) + checkAll("Monoidal[OptionT[List, ?]]", SerializableTests.serializable(Monoidal[OptionT[List, ?]])) + checkAll("OptionT[List, Int]", MonadTests[OptionT[List, ?]].monad[Int, Int, Int]) - checkAll("MonadOptionT[List, ?]]", SerializableTests.serializable(Monad[OptionT[List, ?]])) + checkAll("Monad[OptionT[List, ?]]", SerializableTests.serializable(Monad[OptionT[List, ?]])) } diff --git a/tests/src/test/scala/cats/tests/OptionTests.scala b/tests/src/test/scala/cats/tests/OptionTests.scala index 86eab0f910..656e831f89 100644 --- a/tests/src/test/scala/cats/tests/OptionTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTests.scala @@ -1,9 +1,13 @@ package cats package tests -import cats.laws.discipline.{TraverseTests, CoflatMapTests, MonadCombineTests, SerializableTests} +import cats.laws.discipline.{TraverseTests, CoflatMapTests, MonadCombineTests, SerializableTests, MonoidalTests} +import cats.laws.discipline.eq._ class OptionTests extends CatsSuite { + checkAll("Option[Int]", MonoidalTests[Option].monoidal[Int, Int, Int]) + checkAll("Monoidal[Option]", SerializableTests.serializable(Monoidal[Option])) + checkAll("Option[Int]", CoflatMapTests[Option].coflatMap[Int, Int, Int]) checkAll("CoflatMap[Option]", SerializableTests.serializable(CoflatMap[Option])) diff --git a/tests/src/test/scala/cats/tests/ProdTests.scala b/tests/src/test/scala/cats/tests/ProdTests.scala index 17ece854be..40bd777269 100644 --- a/tests/src/test/scala/cats/tests/ProdTests.scala +++ b/tests/src/test/scala/cats/tests/ProdTests.scala @@ -4,9 +4,14 @@ package tests import cats.data.Prod import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq._ import org.scalacheck.Arbitrary class ProdTests extends CatsSuite { + implicit val iso = MonoidalTests.Isomorphisms.covariant[Prod[Option, List, ?]] + checkAll("Prod[Option, List, Int]", MonoidalTests[Lambda[X => Prod[Option, List, X]]].monoidal[Int, Int, Int]) + checkAll("Monoidal[Prod[Option, List, Int]]", SerializableTests.serializable(Monoidal[Lambda[X => Prod[Option, List, X]]])) + checkAll("Prod[Option, List, Int]", AlternativeTests[Lambda[X => Prod[Option, List, X]]].alternative[Int, Int, Int]) checkAll("Alternative[Prod[Option, List, Int]]", SerializableTests.serializable(Alternative[Lambda[X => Prod[Option, List, X]]])) } diff --git a/tests/src/test/scala/cats/tests/StreamTests.scala b/tests/src/test/scala/cats/tests/StreamTests.scala index 40567af99f..fa1080f06b 100644 --- a/tests/src/test/scala/cats/tests/StreamTests.scala +++ b/tests/src/test/scala/cats/tests/StreamTests.scala @@ -1,9 +1,13 @@ package cats package tests -import cats.laws.discipline.{CoflatMapTests, MonadCombineTests, SerializableTests, TraverseTests} +import cats.laws.discipline.{CoflatMapTests, MonadCombineTests, SerializableTests, TraverseTests, MonoidalTests} +import cats.laws.discipline.eq.tuple3Eq class StreamTests extends CatsSuite { + checkAll("Stream[Int]", MonoidalTests[Stream].monoidal[Int, Int, Int]) + checkAll("Monoidal[Stream]", SerializableTests.serializable(Monoidal[Stream])) + checkAll("Stream[Int]", CoflatMapTests[Stream].coflatMap[Int, Int, Int]) checkAll("CoflatMap[Stream]", SerializableTests.serializable(CoflatMap[Stream])) diff --git a/tests/src/test/scala/cats/tests/StreamingTests.scala b/tests/src/test/scala/cats/tests/StreamingTests.scala index 4f3c506dc4..b09bbd63a0 100644 --- a/tests/src/test/scala/cats/tests/StreamingTests.scala +++ b/tests/src/test/scala/cats/tests/StreamingTests.scala @@ -5,9 +5,13 @@ import algebra.laws.OrderLaws import cats.data.Streaming import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.{TraverseTests, CoflatMapTests, MonadCombineTests, SerializableTests} +import cats.laws.discipline.{TraverseTests, CoflatMapTests, MonadCombineTests, SerializableTests, MonoidalTests} +import cats.laws.discipline.eq.tuple3Eq class StreamingTests extends CatsSuite { + checkAll("Streaming[Int]", MonoidalTests[Streaming].monoidal[Int, Int, Int]) + checkAll("Monoidal[Streaming]", SerializableTests.serializable(Monoidal[Streaming])) + checkAll("Streaming[Int]", CoflatMapTests[Streaming].coflatMap[Int, Int, Int]) checkAll("CoflatMap[Streaming]", SerializableTests.serializable(CoflatMap[Streaming])) diff --git a/tests/src/test/scala/cats/tests/ValidatedTests.scala b/tests/src/test/scala/cats/tests/ValidatedTests.scala index 1e3383201e..aa4995fd1c 100644 --- a/tests/src/test/scala/cats/tests/ValidatedTests.scala +++ b/tests/src/test/scala/cats/tests/ValidatedTests.scala @@ -3,15 +3,20 @@ package tests import cats.data.{NonEmptyList, Validated} import cats.data.Validated.{Valid, Invalid} -import cats.laws.discipline.{BifunctorTests, TraverseTests, ApplicativeTests, SerializableTests} +import cats.laws.discipline.{BifunctorTests, TraverseTests, ApplicativeTests, SerializableTests, MonoidalTests} import org.scalacheck.{Gen, Arbitrary} import org.scalacheck.Arbitrary._ import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq.tuple3Eq import algebra.laws.OrderLaws import scala.util.Try class ValidatedTests extends CatsSuite { + implicit val iso = MonoidalTests.Isomorphisms.covariant[Validated[String, ?]] + checkAll("Validated[String, Int]", MonoidalTests[Validated[String,?]].monoidal[Int, Int, Int]) + checkAll("Monoidal[Validated[String,?]]", SerializableTests.serializable(Monoidal[Validated[String,?]])) + checkAll("Validated[String, Int]", ApplicativeTests[Validated[String,?]].applicative[Int, Int, Int]) checkAll("Validated[?, ?]", BifunctorTests[Validated].bifunctor[Int, Int, Int, Int, Int, Int]) checkAll("Applicative[Validated[String,?]]", SerializableTests.serializable(Applicative[Validated[String,?]])) diff --git a/tests/src/test/scala/cats/tests/VectorTests.scala b/tests/src/test/scala/cats/tests/VectorTests.scala index 9db26b0684..3815b12369 100644 --- a/tests/src/test/scala/cats/tests/VectorTests.scala +++ b/tests/src/test/scala/cats/tests/VectorTests.scala @@ -1,9 +1,13 @@ package cats package tests -import cats.laws.discipline.{MonadCombineTests, SerializableTests, TraverseTests} +import cats.laws.discipline.{MonadCombineTests, SerializableTests, TraverseTests, MonoidalTests} +import cats.laws.discipline.eq.tuple3Eq class VectorTests extends CatsSuite { + checkAll("Vector[Int]", MonoidalTests[Vector].monoidal[Int, Int, Int]) + checkAll("Monoidal[Vector]", SerializableTests.serializable(Monoidal[Vector])) + checkAll("Vector[Int]", MonadCombineTests[Vector].monadCombine[Int, Int, Int]) checkAll("MonadCombine[Vector]", SerializableTests.serializable(MonadCombine[Vector])) diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index 369424c3a6..6a59ccd753 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -4,7 +4,8 @@ package tests import cats.data.Xor import cats.data.Xor._ import cats.laws.discipline.arbitrary.xorArbitrary -import cats.laws.discipline.{BifunctorTests, TraverseTests, MonadErrorTests, SerializableTests} +import cats.laws.discipline.{BifunctorTests, TraverseTests, MonadErrorTests, SerializableTests, MonoidalTests} +import cats.laws.discipline.eq.tuple3Eq import algebra.laws.{GroupLaws, OrderLaws} import org.scalacheck.{Arbitrary, Gen} import org.scalacheck.Arbitrary._ @@ -14,6 +15,10 @@ import scala.util.Try class XorTests extends CatsSuite { checkAll("Xor[String, Int]", GroupLaws[Xor[String, Int]].monoid) + implicit val iso = MonoidalTests.Isomorphisms.covariant[Xor[String, ?]] + checkAll("Xor[String, Int]", MonoidalTests[Xor[String, ?]].monoidal[Int, Int, Int]) + checkAll("Monoidal[Xor, ?]", SerializableTests.serializable(Monoidal[Xor[String, ?]])) + checkAll("Xor[String, Int]", MonadErrorTests[Xor[String, ?], String].monadError[Int, Int, Int]) checkAll("MonadError[Xor, String]", SerializableTests.serializable(MonadError[Xor[String, ?], String])) From 01393897197b79cc475a9655c39a77d8fb125efc Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Thu, 19 Nov 2015 19:11:19 +0100 Subject: [PATCH 4/6] add Const monoidal contravariant tests --- .../main/scala/cats/laws/discipline/MonoidalTests.scala | 9 +++++++++ tests/src/test/scala/cats/tests/ConstTests.scala | 7 ++++++- tests/src/test/scala/cats/tests/FunctionTests.scala | 6 ++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala b/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala index b480103438..20ecc59282 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala @@ -50,6 +50,15 @@ object MonoidalTests { def `(a, unit) ≅ a`[A](fs: (F[(A, Unit)], F[A]))(implicit EqFA: Eq[F[A]]) = F.map(fs._1)(_._1) ?== fs._2 } + implicit def contravariant[F[_]](implicit F: functor.Contravariant[F]): Isomorphisms[F] = + new Isomorphisms[F] { + def `((a, b), c) ≅ (a, (b, c))`[A, B, C](fs: (F[(A, (B, C))], F[((A, B), C)]))(implicit EqFABC: Eq[F[(A, B, C)]]) = + F.contramap[(A, (B, C)), (A, B, C)](fs._1) { case (a, b, c) => (a, (b, c)) } ?== F.contramap[((A, B), C), (A, B, C)](fs._2) { case (a, b, c) => ((a, b), c) } + def `(unit, a) ≅ a`[A](fs: (F[(Unit, A)], F[A]))(implicit EqFA: Eq[F[A]]) = + F.contramap(fs._1)((a: A) => ((), a)) ?== fs._2 + def `(a, unit) ≅ a`[A](fs: (F[(A, Unit)], F[A]))(implicit EqFA: Eq[F[A]]) = + F.contramap(fs._1)((a: A) => (a, ())) ?== fs._2 + } } } \ No newline at end of file diff --git a/tests/src/test/scala/cats/tests/ConstTests.scala b/tests/src/test/scala/cats/tests/ConstTests.scala index 3979c98360..811fd5cb4b 100644 --- a/tests/src/test/scala/cats/tests/ConstTests.scala +++ b/tests/src/test/scala/cats/tests/ConstTests.scala @@ -13,8 +13,8 @@ class ConstTests extends CatsSuite { { implicit val iso = MonoidalTests.Isomorphisms.covariant[Const[String, ?]] checkAll("Const[String, Int]", MonoidalTests[Const[String, ?]].monoidal[Int, Int, Int]) - checkAll("Monoidal[Const[String, ?]]", SerializableTests.serializable(Monoidal[Const[String, ?]])) } + checkAll("Monoidal[Const[String, ?]]", SerializableTests.serializable(Monoidal[Const[String, ?]])) checkAll("Const[String, Int]", ApplicativeTests[Const[String, ?]].applicative[Int, Int, Int]) checkAll("Applicative[Const[String, ?]]", SerializableTests.serializable(Applicative[Const[String, ?]])) @@ -42,4 +42,9 @@ class ConstTests extends CatsSuite { checkAll("Const[String, Int]", ContravariantTests[Const[String, ?]].contravariant[Int, Int, Int]) checkAll("Contravariant[Const[String, ?]]", SerializableTests.serializable(Contravariant[Const[String, ?]])) + + { + implicit val iso = MonoidalTests.Isomorphisms.contravariant[Const[String, ?]] + checkAll("Const[String, Int]", MonoidalTests[Const[String, ?]].monoidal[Int, Int, Int]) + } } diff --git a/tests/src/test/scala/cats/tests/FunctionTests.scala b/tests/src/test/scala/cats/tests/FunctionTests.scala index 65fb3e95b7..859d2e994d 100644 --- a/tests/src/test/scala/cats/tests/FunctionTests.scala +++ b/tests/src/test/scala/cats/tests/FunctionTests.scala @@ -17,8 +17,10 @@ class FunctionTests extends CatsSuite { checkAll("Function0[Int]", BimonadTests[Function0].bimonad[Int, Int, Int]) checkAll("Bimonad[Function0]", SerializableTests.serializable(Bimonad[Function0])) - implicit val iso = MonoidalTests.Isomorphisms.covariant[Function1[Int, ?]] - checkAll("Function1[Int, Int]", MonoidalTests[Function1[Int, ?]].monoidal[Int, Int, Int]) + { + implicit val iso = MonoidalTests.Isomorphisms.covariant[Function1[Int, ?]] + checkAll("Function1[Int, Int]", MonoidalTests[Function1[Int, ?]].monoidal[Int, Int, Int]) + } checkAll("Monoidal[Function1[Int, ?]]", SerializableTests.serializable(Monoidal[Function1[Int, ?]])) checkAll("Function1[Int, Int]", MonadReaderTests[Int => ?, Int].monadReader[Int, Int, Int]) From 7ddb6a7f16c78cba5194354ea8c0018d5f07f43a Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Sat, 21 Nov 2015 23:46:50 +0100 Subject: [PATCH 5/6] remove identity laws --- .../main/scala/cats/laws/MonoidalLaws.scala | 6 ----- .../cats/laws/discipline/MonoidalTests.scala | 22 ++++--------------- .../test/scala/cats/tests/ConstTests.scala | 2 +- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/laws/src/main/scala/cats/laws/MonoidalLaws.scala b/laws/src/main/scala/cats/laws/MonoidalLaws.scala index 474671a6e2..2d54922e79 100644 --- a/laws/src/main/scala/cats/laws/MonoidalLaws.scala +++ b/laws/src/main/scala/cats/laws/MonoidalLaws.scala @@ -8,12 +8,6 @@ trait MonoidalLaws[F[_]] { def associativity[A, B, C](fa: F[A], fb: F[B], fc: F[C]): (F[(A, (B, C))], F[((A, B), C)]) = (F.product(fa, F.product(fb, fc)), F.product(F.product(fa, fb), fc)) - def leftIdentity[A](funit: F[Unit], fa: F[A]): (F[(Unit, A)], F[A]) = - (F.product(funit, fa), fa) - - def rightIdentity[A](fa: F[A], funit: F[Unit]): (F[(A, Unit)], F[A]) = - (F.product(fa, funit), fa) - } object MonoidalLaws { diff --git a/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala b/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala index 20ecc59282..089d5e06af 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala @@ -15,16 +15,12 @@ trait MonoidalTests[F[_]] extends Laws { ArbFA: Arbitrary[F[A]], ArbFB: Arbitrary[F[B]], ArbFC: Arbitrary[F[C]], - ArbFUnit: Arbitrary[F[Unit]], - EqFA: Eq[F[A]], EqFABC: Eq[F[(A, B, C)]] ): RuleSet = { new DefaultRuleSet( name = "monoidal", parent = None, - "associativity" -> forAll((fa: F[A], fb: F[B], fc: F[C]) => iso.`((a, b), c) ≅ (a, (b, c))`(laws.associativity(fa, fb, fc))), - "left identity" -> forAll((fa: F[A], funit: F[Unit]) => iso.`(unit, a) ≅ a`(laws.leftIdentity(funit, fa))), - "right identity" -> forAll((fa: F[A], funit: F[Unit]) => iso.`(a, unit) ≅ a`(laws.rightIdentity(fa, funit))) + "monoidal associativity" -> forAll((fa: F[A], fb: F[B], fc: F[C]) => iso.associativity(laws.associativity(fa, fb, fc))) ) } } @@ -34,30 +30,20 @@ object MonoidalTests { new MonoidalTests[F] { val laws: MonoidalLaws[F] = MonoidalLaws[F] } trait Isomorphisms[F[_]] { - def `((a, b), c) ≅ (a, (b, c))`[A, B, C](fs: (F[(A, (B, C))], F[((A, B), C)]))(implicit EqFABC: Eq[F[(A, B, C)]]): Prop - def `(unit, a) ≅ a`[A](fs: (F[(Unit, A)], F[A]))(implicit EqFA: Eq[F[A]]): Prop - def `(a, unit) ≅ a`[A](fs: (F[(A, Unit)], F[A]))(implicit EqFA: Eq[F[A]]): Prop + def associativity[A, B, C](fs: (F[(A, (B, C))], F[((A, B), C)]))(implicit EqFABC: Eq[F[(A, B, C)]]): Prop } object Isomorphisms { import algebra.laws._ implicit def covariant[F[_]](implicit F: Functor[F]): Isomorphisms[F] = new Isomorphisms[F] { - def `((a, b), c) ≅ (a, (b, c))`[A, B, C](fs: (F[(A, (B, C))], F[((A, B), C)]))(implicit EqFABC: Eq[F[(A, B, C)]]) = + def associativity[A, B, C](fs: (F[(A, (B, C))], F[((A, B), C)]))(implicit EqFABC: Eq[F[(A, B, C)]]) = F.map(fs._1) { case (a, (b, c)) => (a, b, c) } ?== F.map(fs._2) { case ((a, b), c) => (a, b, c) } - def `(unit, a) ≅ a`[A](fs: (F[(Unit, A)], F[A]))(implicit EqFA: Eq[F[A]]) = - F.map(fs._1)(_._2) ?== fs._2 - def `(a, unit) ≅ a`[A](fs: (F[(A, Unit)], F[A]))(implicit EqFA: Eq[F[A]]) = - F.map(fs._1)(_._1) ?== fs._2 } implicit def contravariant[F[_]](implicit F: functor.Contravariant[F]): Isomorphisms[F] = new Isomorphisms[F] { - def `((a, b), c) ≅ (a, (b, c))`[A, B, C](fs: (F[(A, (B, C))], F[((A, B), C)]))(implicit EqFABC: Eq[F[(A, B, C)]]) = + def associativity[A, B, C](fs: (F[(A, (B, C))], F[((A, B), C)]))(implicit EqFABC: Eq[F[(A, B, C)]]) = F.contramap[(A, (B, C)), (A, B, C)](fs._1) { case (a, b, c) => (a, (b, c)) } ?== F.contramap[((A, B), C), (A, B, C)](fs._2) { case (a, b, c) => ((a, b), c) } - def `(unit, a) ≅ a`[A](fs: (F[(Unit, A)], F[A]))(implicit EqFA: Eq[F[A]]) = - F.contramap(fs._1)((a: A) => ((), a)) ?== fs._2 - def `(a, unit) ≅ a`[A](fs: (F[(A, Unit)], F[A]))(implicit EqFA: Eq[F[A]]) = - F.contramap(fs._1)((a: A) => (a, ())) ?== fs._2 } } diff --git a/tests/src/test/scala/cats/tests/ConstTests.scala b/tests/src/test/scala/cats/tests/ConstTests.scala index 811fd5cb4b..54a806ee5c 100644 --- a/tests/src/test/scala/cats/tests/ConstTests.scala +++ b/tests/src/test/scala/cats/tests/ConstTests.scala @@ -45,6 +45,6 @@ class ConstTests extends CatsSuite { { implicit val iso = MonoidalTests.Isomorphisms.contravariant[Const[String, ?]] - checkAll("Const[String, Int]", MonoidalTests[Const[String, ?]].monoidal[Int, Int, Int]) + checkAll("contravariant Const[String, Int]", MonoidalTests[Const[String, ?]].monoidal[Int, Int, Int]) } } From ecd307eee7dfe100e384a62c3a3363886d25aa8b Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Sun, 22 Nov 2015 01:01:08 +0100 Subject: [PATCH 6/6] fix tests --- .../cats/free/FreeApplicativeTests.scala | 5 ++- free/src/test/scala/cats/free/FreeTests.scala | 5 ++- .../test/scala/cats/tests/FutureTests.scala | 1 + .../test/scala/cats/tests/FutureTests.scala | 1 + laws/src/main/scala/cats/laws/ApplyLaws.scala | 2 +- .../main/scala/cats/laws/MonoidalLaws.scala | 2 +- .../laws/discipline/AlternativeTests.scala | 7 +++- .../laws/discipline/ApplicativeTests.scala | 7 +++- .../cats/laws/discipline/ApplyTests.scala | 19 ++++++---- .../cats/laws/discipline/BimonadTests.scala | 5 ++- .../cats/laws/discipline/FlatMapTests.scala | 5 ++- .../laws/discipline/MonadCombineTests.scala | 5 ++- .../laws/discipline/MonadErrorTests.scala | 5 ++- .../laws/discipline/MonadFilterTests.scala | 5 ++- .../laws/discipline/MonadReaderTests.scala | 5 ++- .../laws/discipline/MonadStateTests.scala | 5 ++- .../cats/laws/discipline/MonadTests.scala | 5 ++- .../cats/laws/discipline/MonoidalTests.scala | 12 +++---- .../test/scala/cats/state/StateTTests.scala | 3 +- .../scala/cats/tests/CokleisliTests.scala | 3 +- .../test/scala/cats/tests/ConstTests.scala | 12 +++---- .../test/scala/cats/tests/EitherTests.scala | 3 +- .../src/test/scala/cats/tests/EvalTests.scala | 8 +++-- .../src/test/scala/cats/tests/FuncTests.scala | 4 ++- .../test/scala/cats/tests/FunctionTests.scala | 6 ++-- tests/src/test/scala/cats/tests/IdTests.scala | 3 ++ .../src/test/scala/cats/tests/IorTests.scala | 3 +- .../test/scala/cats/tests/KleisliTests.scala | 3 +- .../src/test/scala/cats/tests/MapTests.scala | 3 +- .../test/scala/cats/tests/OneAndTests.scala | 5 ++- .../test/scala/cats/tests/OptionTTests.scala | 12 +++---- .../src/test/scala/cats/tests/ProdTests.scala | 2 +- .../scala/cats/tests/StreamingTTests.scala | 35 ++++++++++++------- .../scala/cats/tests/ValidatedTests.scala | 2 +- .../test/scala/cats/tests/WriterTTests.scala | 2 ++ .../src/test/scala/cats/tests/XorTTests.scala | 2 ++ .../src/test/scala/cats/tests/XorTests.scala | 7 ++-- 37 files changed, 141 insertions(+), 78 deletions(-) diff --git a/free/src/test/scala/cats/free/FreeApplicativeTests.scala b/free/src/test/scala/cats/free/FreeApplicativeTests.scala index e0c4f2419a..ca0e3fd77a 100644 --- a/free/src/test/scala/cats/free/FreeApplicativeTests.scala +++ b/free/src/test/scala/cats/free/FreeApplicativeTests.scala @@ -2,7 +2,8 @@ package cats package free import cats.arrow.NaturalTransformation -import cats.laws.discipline.{ApplicativeTests, SerializableTests} +import cats.laws.discipline.{MonoidalTests, ApplicativeTests, SerializableTests} +import cats.laws.discipline.eq.tuple3Eq import cats.tests.CatsSuite import cats.data.Const @@ -23,6 +24,8 @@ class FreeApplicativeTests extends CatsSuite { } } + implicit val iso = MonoidalTests.Isomorphisms.invariant[FreeApplicative[Option, ?]] + checkAll("FreeApplicative[Option, ?]", ApplicativeTests[FreeApplicative[Option, ?]].applicative[Int, Int, Int]) checkAll("Monad[FreeApplicative[Option, ?]]", SerializableTests.serializable(Applicative[FreeApplicative[Option, ?]])) diff --git a/free/src/test/scala/cats/free/FreeTests.scala b/free/src/test/scala/cats/free/FreeTests.scala index 57f26ef781..146111061d 100644 --- a/free/src/test/scala/cats/free/FreeTests.scala +++ b/free/src/test/scala/cats/free/FreeTests.scala @@ -3,7 +3,8 @@ package free import cats.arrow.NaturalTransformation import cats.tests.CatsSuite -import cats.laws.discipline.{MonadTests, SerializableTests} +import cats.laws.discipline.{MonoidalTests, MonadTests, SerializableTests} +import cats.laws.discipline.eq._ import org.scalacheck.{Arbitrary, Gen} class FreeTests extends CatsSuite { @@ -20,6 +21,8 @@ class FreeTests extends CatsSuite { SA.eqv(a.runM(identity), b.runM(identity)) } + implicit val iso = MonoidalTests.Isomorphisms.invariant[Free[Option, ?]] + checkAll("Free[Option, ?]", MonadTests[Free[Option, ?]].monad[Int, Int, Int]) checkAll("Monad[Free[Option, ?]]", SerializableTests.serializable(Monad[Free[Option, ?]])) diff --git a/js/src/test/scala/cats/tests/FutureTests.scala b/js/src/test/scala/cats/tests/FutureTests.scala index 19c649f774..aff4973654 100644 --- a/js/src/test/scala/cats/tests/FutureTests.scala +++ b/js/src/test/scala/cats/tests/FutureTests.scala @@ -4,6 +4,7 @@ package tests import cats.data.Xor import cats.laws.discipline._ +import cats.laws.discipline.eq.tuple3Eq import cats.js.std.Await import cats.js.std.future.{futureEq, futureComonad} import cats.tests.CatsSuite diff --git a/jvm/src/test/scala/cats/tests/FutureTests.scala b/jvm/src/test/scala/cats/tests/FutureTests.scala index 180077c2dc..a19ee09819 100644 --- a/jvm/src/test/scala/cats/tests/FutureTests.scala +++ b/jvm/src/test/scala/cats/tests/FutureTests.scala @@ -4,6 +4,7 @@ package tests import cats.data.Xor import cats.laws.discipline._ +import cats.laws.discipline.eq.tuple3Eq import cats.jvm.std.future.{futureEq, futureComonad} import cats.tests.CatsSuite diff --git a/laws/src/main/scala/cats/laws/ApplyLaws.scala b/laws/src/main/scala/cats/laws/ApplyLaws.scala index 8f71da8114..c6c2a76859 100644 --- a/laws/src/main/scala/cats/laws/ApplyLaws.scala +++ b/laws/src/main/scala/cats/laws/ApplyLaws.scala @@ -7,7 +7,7 @@ import cats.syntax.functor._ /** * Laws that must be obeyed by any `Apply`. */ -trait ApplyLaws[F[_]] extends FunctorLaws[F] { +trait ApplyLaws[F[_]] extends FunctorLaws[F] with MonoidalLaws[F] { implicit override def F: Apply[F] def applyComposition[A, B, C](fa: F[A], fab: F[A => B], fbc: F[B => C]): IsEq[F[C]] = { diff --git a/laws/src/main/scala/cats/laws/MonoidalLaws.scala b/laws/src/main/scala/cats/laws/MonoidalLaws.scala index 2d54922e79..402e1cd9bf 100644 --- a/laws/src/main/scala/cats/laws/MonoidalLaws.scala +++ b/laws/src/main/scala/cats/laws/MonoidalLaws.scala @@ -5,7 +5,7 @@ trait MonoidalLaws[F[_]] { implicit def F: Monoidal[F] - def associativity[A, B, C](fa: F[A], fb: F[B], fc: F[C]): (F[(A, (B, C))], F[((A, B), C)]) = + def monoidalAssociativity[A, B, C](fa: F[A], fb: F[B], fc: F[C]): (F[(A, (B, C))], F[((A, B), C)]) = (F.product(fa, F.product(fb, fc)), F.product(F.product(fa, fb), fc)) } diff --git a/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala b/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala index 8eacddbfdf..e2f35ab31c 100644 --- a/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala @@ -2,6 +2,7 @@ package cats package laws package discipline +import cats.laws.discipline.MonoidalTests.Isomorphisms import org.scalacheck.Arbitrary import org.scalacheck.Prop import Prop._ @@ -11,11 +12,15 @@ trait AlternativeTests[F[_]] extends ApplicativeTests[F] with MonoidKTests[F] { def alternative[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit ArbFA: Arbitrary[F[A]], + ArbFB: Arbitrary[F[B]], + ArbFC: Arbitrary[F[C]], ArbFAtoB: Arbitrary[F[A => B]], ArbFBtoC: Arbitrary[F[B => C]], EqFA: Eq[F[A]], EqFB: Eq[F[B]], - EqFC: Eq[F[C]] + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] ): RuleSet = { new RuleSet { val name: String = "alternative" diff --git a/laws/src/main/scala/cats/laws/discipline/ApplicativeTests.scala b/laws/src/main/scala/cats/laws/discipline/ApplicativeTests.scala index 71b3bd6c70..49117c8dea 100644 --- a/laws/src/main/scala/cats/laws/discipline/ApplicativeTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/ApplicativeTests.scala @@ -2,6 +2,7 @@ package cats package laws package discipline +import cats.laws.discipline.MonoidalTests.Isomorphisms import org.scalacheck.Arbitrary import org.scalacheck.Prop import Prop._ @@ -11,11 +12,15 @@ trait ApplicativeTests[F[_]] extends ApplyTests[F] { def applicative[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit ArbFA: Arbitrary[F[A]], + ArbFB: Arbitrary[F[B]], + ArbFC: Arbitrary[F[C]], ArbFAtoB: Arbitrary[F[A => B]], ArbFBtoC: Arbitrary[F[B => C]], EqFA: Eq[F[A]], EqFB: Eq[F[B]], - EqFC: Eq[F[C]] + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] ): RuleSet = { new DefaultRuleSet( name = "applicative", diff --git a/laws/src/main/scala/cats/laws/discipline/ApplyTests.scala b/laws/src/main/scala/cats/laws/discipline/ApplyTests.scala index 1c2ebf5dd3..d6ca9f8833 100644 --- a/laws/src/main/scala/cats/laws/discipline/ApplyTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/ApplyTests.scala @@ -2,24 +2,29 @@ package cats package laws package discipline +import cats.laws.discipline.MonoidalTests.Isomorphisms import org.scalacheck.Arbitrary import org.scalacheck.Prop import Prop._ -trait ApplyTests[F[_]] extends FunctorTests[F] { +trait ApplyTests[F[_]] extends FunctorTests[F] with MonoidalTests[F] { def laws: ApplyLaws[F] def apply[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit ArbFA: Arbitrary[F[A]], + ArbFB: Arbitrary[F[B]], + ArbFC: Arbitrary[F[C]], ArbFAtoB: Arbitrary[F[A => B]], ArbFBtoC: Arbitrary[F[B => C]], EqFA: Eq[F[A]], - EqFC: Eq[F[C]] - ): RuleSet = { - new DefaultRuleSet( - name = "apply", - parent = Some(functor[A, B, C]), - "apply composition" -> forAll(laws.applyComposition[A, B, C] _)) + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] + ): RuleSet = new RuleSet { + val name = "apply" + val parents = Seq(functor[A, B, C], monoidal[A, B, C]) + val bases = Seq.empty + val props = Seq("apply composition" -> forAll(laws.applyComposition[A, B, C] _)) } } diff --git a/laws/src/main/scala/cats/laws/discipline/BimonadTests.scala b/laws/src/main/scala/cats/laws/discipline/BimonadTests.scala index bbc949e09d..a5a7fee047 100644 --- a/laws/src/main/scala/cats/laws/discipline/BimonadTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/BimonadTests.scala @@ -2,6 +2,7 @@ package cats package laws package discipline +import cats.laws.discipline.MonoidalTests.Isomorphisms import org.scalacheck.Arbitrary import org.scalacheck.Prop import Prop._ @@ -20,7 +21,9 @@ trait BimonadTests[F[_]] extends MonadTests[F] with ComonadTests[F] { EqFFA: Eq[F[F[F[A]]]], EqFA: Eq[F[A]], EqFB: Eq[F[B]], - EqFC: Eq[F[C]] + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] ): RuleSet = { new RuleSet { def name: String = "bimonad" diff --git a/laws/src/main/scala/cats/laws/discipline/FlatMapTests.scala b/laws/src/main/scala/cats/laws/discipline/FlatMapTests.scala index 36b1479851..c4aebf3a56 100644 --- a/laws/src/main/scala/cats/laws/discipline/FlatMapTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/FlatMapTests.scala @@ -2,6 +2,7 @@ package cats package laws package discipline +import cats.laws.discipline.MonoidalTests.Isomorphisms import org.scalacheck.Arbitrary import org.scalacheck.Prop import Prop._ @@ -17,7 +18,9 @@ trait FlatMapTests[F[_]] extends ApplyTests[F] { ArbFBtoC: Arbitrary[F[B => C]], EqFA: Eq[F[A]], EqFB: Eq[F[B]], - EqFC: Eq[F[C]] + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] ): RuleSet = { new DefaultRuleSet( name = "flatMap", diff --git a/laws/src/main/scala/cats/laws/discipline/MonadCombineTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadCombineTests.scala index 55050e1d01..7f3cbea2ad 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonadCombineTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonadCombineTests.scala @@ -2,6 +2,7 @@ package cats package laws package discipline +import cats.laws.discipline.MonoidalTests.Isomorphisms import org.scalacheck.Arbitrary import org.scalacheck.Prop import Prop._ @@ -17,7 +18,9 @@ trait MonadCombineTests[F[_]] extends MonadFilterTests[F] with AlternativeTests[ ArbFBtoC: Arbitrary[F[B => C]], EqFA: Eq[F[A]], EqFB: Eq[F[B]], - EqFC: Eq[F[C]] + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] ): RuleSet = { new RuleSet { def name: String = "monadCombine" diff --git a/laws/src/main/scala/cats/laws/discipline/MonadErrorTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadErrorTests.scala index 91efe5467d..f43f7d0349 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonadErrorTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonadErrorTests.scala @@ -3,6 +3,7 @@ package laws package discipline import cats.data.{ Xor, XorT } +import cats.laws.discipline.MonoidalTests.Isomorphisms import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq.unitEq import org.scalacheck.{Arbitrary, Prop} @@ -24,7 +25,9 @@ trait MonadErrorTests[F[_], E] extends MonadTests[F] { EqE: Eq[E], EqFXorEU: Eq[F[E Xor Unit]], EqFXorEA: Eq[F[E Xor A]], - EqXorTFEA: Eq[XorT[F, E, A]] + EqXorTFEA: Eq[XorT[F, E, A]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] ): RuleSet = { new RuleSet { def name: String = "monadError" diff --git a/laws/src/main/scala/cats/laws/discipline/MonadFilterTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadFilterTests.scala index d9948843fd..a2910ae19e 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonadFilterTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonadFilterTests.scala @@ -2,6 +2,7 @@ package cats package laws package discipline +import cats.laws.discipline.MonoidalTests.Isomorphisms import org.scalacheck.Arbitrary import org.scalacheck.Prop import Prop._ @@ -17,7 +18,9 @@ trait MonadFilterTests[F[_]] extends MonadTests[F] { ArbFBtoC: Arbitrary[F[B => C]], EqFA: Eq[F[A]], EqFB: Eq[F[B]], - EqFC: Eq[F[C]] + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] ): RuleSet = { new DefaultRuleSet( name = "monadFilter", diff --git a/laws/src/main/scala/cats/laws/discipline/MonadReaderTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadReaderTests.scala index 627b019345..e3fcdd0fc2 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonadReaderTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonadReaderTests.scala @@ -2,6 +2,7 @@ package cats package laws package discipline +import cats.laws.discipline.MonoidalTests.Isomorphisms import org.scalacheck.{Arbitrary, Prop} import org.scalacheck.Prop.forAll @@ -18,7 +19,9 @@ trait MonadReaderTests[F[_], R] extends MonadTests[F] { EqFA: Eq[F[A]], EqFB: Eq[F[B]], EqFC: Eq[F[C]], - EqFR: Eq[F[R]] + EqFR: Eq[F[R]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] ): RuleSet = { new RuleSet { def name: String = "monadReader" diff --git a/laws/src/main/scala/cats/laws/discipline/MonadStateTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadStateTests.scala index 1998ecc31a..c9d5015c08 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonadStateTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonadStateTests.scala @@ -2,6 +2,7 @@ package cats package laws package discipline +import cats.laws.discipline.MonoidalTests.Isomorphisms import eq.unitEq import org.scalacheck.{Arbitrary, Prop} import org.scalacheck.Prop.forAll @@ -22,7 +23,9 @@ trait MonadStateTests[F[_], S] extends MonadTests[F] { EqFB: Eq[F[B]], EqFC: Eq[F[C]], EqFUnit: Eq[F[Unit]], - EqFS: Eq[F[S]] + EqFS: Eq[F[S]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] ): RuleSet = { new RuleSet { def name: String = "monadState" diff --git a/laws/src/main/scala/cats/laws/discipline/MonadTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadTests.scala index 3bf1b54788..b4a9903acf 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonadTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonadTests.scala @@ -2,6 +2,7 @@ package cats package laws package discipline +import cats.laws.discipline.MonoidalTests.Isomorphisms import org.scalacheck.Arbitrary import org.scalacheck.Prop import Prop._ @@ -17,7 +18,9 @@ trait MonadTests[F[_]] extends ApplicativeTests[F] with FlatMapTests[F] { ArbFBtoC: Arbitrary[F[B => C]], EqFA: Eq[F[A]], EqFB: Eq[F[B]], - EqFC: Eq[F[C]] + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] ): RuleSet = { new RuleSet { def name: String = "monad" diff --git a/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala b/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala index 089d5e06af..9c836c64f2 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonoidalTests.scala @@ -20,7 +20,7 @@ trait MonoidalTests[F[_]] extends Laws { new DefaultRuleSet( name = "monoidal", parent = None, - "monoidal associativity" -> forAll((fa: F[A], fb: F[B], fc: F[C]) => iso.associativity(laws.associativity(fa, fb, fc))) + "monoidal associativity" -> forAll((fa: F[A], fb: F[B], fc: F[C]) => iso.associativity(laws.monoidalAssociativity(fa, fb, fc))) ) } } @@ -35,15 +35,11 @@ object MonoidalTests { object Isomorphisms { import algebra.laws._ - implicit def covariant[F[_]](implicit F: Functor[F]): Isomorphisms[F] = + implicit def invariant[F[_]](implicit F: functor.Invariant[F]): Isomorphisms[F] = new Isomorphisms[F] { def associativity[A, B, C](fs: (F[(A, (B, C))], F[((A, B), C)]))(implicit EqFABC: Eq[F[(A, B, C)]]) = - F.map(fs._1) { case (a, (b, c)) => (a, b, c) } ?== F.map(fs._2) { case ((a, b), c) => (a, b, c) } - } - implicit def contravariant[F[_]](implicit F: functor.Contravariant[F]): Isomorphisms[F] = - new Isomorphisms[F] { - def associativity[A, B, C](fs: (F[(A, (B, C))], F[((A, B), C)]))(implicit EqFABC: Eq[F[(A, B, C)]]) = - F.contramap[(A, (B, C)), (A, B, C)](fs._1) { case (a, b, c) => (a, (b, c)) } ?== F.contramap[((A, B), C), (A, B, C)](fs._2) { case (a, b, c) => ((a, b), c) } + F.imap(fs._1) { case (a, (b, c)) => (a, b, c) } { case (a, b, c) => (a, (b, c)) } ?== + F.imap(fs._2) { case ((a, b), c) => (a, b, c) } { case (a, b, c) => ((a, b), c) } } } diff --git a/state/src/test/scala/cats/state/StateTTests.scala b/state/src/test/scala/cats/state/StateTTests.scala index 741ffa97fd..5dc9a8c37c 100644 --- a/state/src/test/scala/cats/state/StateTTests.scala +++ b/state/src/test/scala/cats/state/StateTTests.scala @@ -2,7 +2,7 @@ package cats package state import cats.tests.CatsSuite -import cats.laws.discipline.{MonadStateTests, MonoidKTests, SerializableTests} +import cats.laws.discipline.{MonoidalTests, MonadStateTests, MonoidKTests, SerializableTests} import cats.laws.discipline.eq._ import org.scalacheck.{Arbitrary, Gen} @@ -39,6 +39,7 @@ class StateTTests extends CatsSuite { } } + implicit val iso = MonoidalTests.Isomorphisms.invariant[StateT[Option, Int, ?]] checkAll("StateT[Option, Int, Int]", MonadStateTests[StateT[Option, Int, ?], Int].monadState[Int, Int, Int]) checkAll("MonadState[StateT[Option, ?, ?], Int]", SerializableTests.serializable(MonadState[StateT[Option, Int, ?], Int])) } diff --git a/tests/src/test/scala/cats/tests/CokleisliTests.scala b/tests/src/test/scala/cats/tests/CokleisliTests.scala index 428c06bc57..9baf71d421 100644 --- a/tests/src/test/scala/cats/tests/CokleisliTests.scala +++ b/tests/src/test/scala/cats/tests/CokleisliTests.scala @@ -18,7 +18,8 @@ class CokleisliTests extends SlowCatsSuite { def cokleisliEqE[F[_], A](implicit A: Arbitrary[F[A]], FA: Eq[A]): Eq[Cokleisli[F, A, A]] = Eq.by[Cokleisli[F, A, A], F[A] => A](_.run) - implicit val iso = MonoidalTests.Isomorphisms.covariant[Cokleisli[Option, Int, ?]] + implicit val iso = MonoidalTests.Isomorphisms.invariant[Cokleisli[Option, Int, ?]] + checkAll("Cokleisli[Option, Int, Int]", MonoidalTests[Cokleisli[Option, Int, ?]].monoidal[Int, Int, Int]) checkAll("Monoidal[Cokleisli[Option, Int, ?]", SerializableTests.serializable(Monoidal[Cokleisli[Option, Int, ?]])) diff --git a/tests/src/test/scala/cats/tests/ConstTests.scala b/tests/src/test/scala/cats/tests/ConstTests.scala index 54a806ee5c..1a73ce66d8 100644 --- a/tests/src/test/scala/cats/tests/ConstTests.scala +++ b/tests/src/test/scala/cats/tests/ConstTests.scala @@ -10,10 +10,9 @@ import cats.laws.discipline.arbitrary.{constArbitrary, oneAndArbitrary} class ConstTests extends CatsSuite { - { - implicit val iso = MonoidalTests.Isomorphisms.covariant[Const[String, ?]] - checkAll("Const[String, Int]", MonoidalTests[Const[String, ?]].monoidal[Int, Int, Int]) - } + implicit val iso = MonoidalTests.Isomorphisms.invariant[Const[String, ?]](Const.constTraverse) + + checkAll("Const[String, Int]", MonoidalTests[Const[String, ?]].monoidal[Int, Int, Int]) checkAll("Monoidal[Const[String, ?]]", SerializableTests.serializable(Monoidal[Const[String, ?]])) checkAll("Const[String, Int]", ApplicativeTests[Const[String, ?]].applicative[Int, Int, Int]) @@ -25,6 +24,7 @@ class ConstTests extends CatsSuite { // Get Apply[Const[C : Semigroup, ?]], not Applicative[Const[C : Monoid, ?]] { implicit def nonEmptyListSemigroup[A]: Semigroup[NonEmptyList[A]] = SemigroupK[NonEmptyList].algebra + implicit val iso = MonoidalTests.Isomorphisms.invariant[Const[NonEmptyList[String], ?]](Const.constContravariant) checkAll("Apply[Const[NonEmptyList[String], Int]]", ApplyTests[Const[NonEmptyList[String], ?]].apply[Int, Int, Int]) checkAll("Apply[Const[NonEmptyList[String], ?]]", SerializableTests.serializable(Apply[Const[NonEmptyList[String], ?]])) } @@ -43,8 +43,4 @@ class ConstTests extends CatsSuite { checkAll("Const[String, Int]", ContravariantTests[Const[String, ?]].contravariant[Int, Int, Int]) checkAll("Contravariant[Const[String, ?]]", SerializableTests.serializable(Contravariant[Const[String, ?]])) - { - implicit val iso = MonoidalTests.Isomorphisms.contravariant[Const[String, ?]] - checkAll("contravariant Const[String, Int]", MonoidalTests[Const[String, ?]].monoidal[Int, Int, Int]) - } } diff --git a/tests/src/test/scala/cats/tests/EitherTests.scala b/tests/src/test/scala/cats/tests/EitherTests.scala index 1eb7f3b5cb..5c4a015968 100644 --- a/tests/src/test/scala/cats/tests/EitherTests.scala +++ b/tests/src/test/scala/cats/tests/EitherTests.scala @@ -7,7 +7,8 @@ import algebra.laws.OrderLaws class EitherTests extends CatsSuite { - implicit val iso = MonoidalTests.Isomorphisms.covariant[Either[Int, ?]] + implicit val iso = MonoidalTests.Isomorphisms.invariant[Either[Int, ?]] + checkAll("Either[Int, Int]", MonoidalTests[Either[Int, ?]].monoidal[Int, Int, Int]) checkAll("Monoidal[Either[Int, ?]]", SerializableTests.serializable(Monoidal[Either[Int, ?]])) diff --git a/tests/src/test/scala/cats/tests/EvalTests.scala b/tests/src/test/scala/cats/tests/EvalTests.scala index 873ee34d23..e6035c1789 100644 --- a/tests/src/test/scala/cats/tests/EvalTests.scala +++ b/tests/src/test/scala/cats/tests/EvalTests.scala @@ -2,8 +2,9 @@ package cats package tests import scala.math.min -import cats.laws.discipline.{BimonadTests, SerializableTests} +import cats.laws.discipline.{MonoidalTests, BimonadTests, SerializableTests} import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq._ import algebra.laws.{GroupLaws, OrderLaws} class EvalTests extends CatsSuite { @@ -89,7 +90,10 @@ class EvalTests extends CatsSuite { } } - checkAll("Eval[Int]", BimonadTests[Eval].bimonad[Int, Int, Int]) + { + implicit val iso = MonoidalTests.Isomorphisms.invariant[Eval] + checkAll("Eval[Int]", BimonadTests[Eval].bimonad[Int, Int, Int]) + } checkAll("Bimonad[Eval]", SerializableTests.serializable(Bimonad[Eval])) checkAll("Eval[Int]", GroupLaws[Eval[Int]].group) diff --git a/tests/src/test/scala/cats/tests/FuncTests.scala b/tests/src/test/scala/cats/tests/FuncTests.scala index d627b70d1a..6565196582 100644 --- a/tests/src/test/scala/cats/tests/FuncTests.scala +++ b/tests/src/test/scala/cats/tests/FuncTests.scala @@ -14,7 +14,8 @@ class FuncTests extends CatsSuite { implicit def appFuncEq[F[_], A, B](implicit A: Arbitrary[A], FB: Eq[F[B]]): Eq[AppFunc[F, A, B]] = Eq.by[AppFunc[F, A, B], A => F[B]](_.run) - implicit val iso = MonoidalTests.Isomorphisms.covariant[Func[Option, Int, ?]] + implicit val iso = MonoidalTests.Isomorphisms.invariant[Func[Option, Int, ?]] + checkAll("Func[Option, Int, Int]", MonoidalTests[Func[Option, Int, ?]].monoidal[Int, Int, Int]) checkAll("Monoidal[Func[Option, Int, ?]]", SerializableTests.serializable(Monoidal[Func[Option, Int, ?]])) @@ -38,6 +39,7 @@ class FuncTests extends CatsSuite { { implicit val appFuncApp = AppFunc.appFuncApplicative[Option, Int] + implicit val iso = MonoidalTests.Isomorphisms.invariant[AppFunc[Option, Int, ?]] checkAll("AppFunc[Option, Int, Int]", ApplicativeTests[AppFunc[Option, Int, ?]].applicative[Int, Int, Int]) checkAll("Applicative[AppFunc[Option, Int, ?]]", SerializableTests.serializable(Applicative[AppFunc[Option, Int, ?]])) } diff --git a/tests/src/test/scala/cats/tests/FunctionTests.scala b/tests/src/test/scala/cats/tests/FunctionTests.scala index b47b32984d..0570cfaa8d 100644 --- a/tests/src/test/scala/cats/tests/FunctionTests.scala +++ b/tests/src/test/scala/cats/tests/FunctionTests.scala @@ -18,10 +18,8 @@ class FunctionTests extends CatsSuite { checkAll("Function0[Int]", BimonadTests[Function0].bimonad[Int, Int, Int]) checkAll("Bimonad[Function0]", SerializableTests.serializable(Bimonad[Function0])) - { - implicit val iso = MonoidalTests.Isomorphisms.covariant[Function1[Int, ?]] - checkAll("Function1[Int, Int]", MonoidalTests[Function1[Int, ?]].monoidal[Int, Int, Int]) - } + implicit val iso = MonoidalTests.Isomorphisms.invariant[Function1[Int, ?]] + checkAll("Function1[Int, Int]", MonoidalTests[Function1[Int, ?]].monoidal[Int, Int, Int]) checkAll("Monoidal[Function1[Int, ?]]", SerializableTests.serializable(Monoidal[Function1[Int, ?]])) checkAll("Function1[Int, Int]", MonadReaderTests[Int => ?, Int].monadReader[Int, Int, Int]) diff --git a/tests/src/test/scala/cats/tests/IdTests.scala b/tests/src/test/scala/cats/tests/IdTests.scala index b835f42858..572f6d6699 100644 --- a/tests/src/test/scala/cats/tests/IdTests.scala +++ b/tests/src/test/scala/cats/tests/IdTests.scala @@ -3,8 +3,11 @@ package tests import org.scalacheck.Prop.forAll import cats.laws.discipline._ +import cats.laws.discipline.eq.tuple3Eq class IdTests extends CatsSuite { + implicit val iso = MonoidalTests.Isomorphisms.invariant[Id] + checkAll("Id[Int]", BimonadTests[Id].bimonad[Int, Int, Int]) checkAll("Bimonad[Id]", SerializableTests.serializable(Bimonad[Id])) diff --git a/tests/src/test/scala/cats/tests/IorTests.scala b/tests/src/test/scala/cats/tests/IorTests.scala index f82edf9592..46ad2af724 100644 --- a/tests/src/test/scala/cats/tests/IorTests.scala +++ b/tests/src/test/scala/cats/tests/IorTests.scala @@ -10,7 +10,8 @@ import org.scalacheck.Arbitrary._ class IorTests extends CatsSuite { - implicit val iso = MonoidalTests.Isomorphisms.covariant[Ior[String, ?]] + implicit val iso = MonoidalTests.Isomorphisms.invariant[Ior[String, ?]] + checkAll("Ior[String, Int]", MonoidalTests[Ior[String, ?]].monoidal[Int, Int, Int]) checkAll("Monoidal[String Ior ?]]", SerializableTests.serializable(Monoidal[String Ior ?])) diff --git a/tests/src/test/scala/cats/tests/KleisliTests.scala b/tests/src/test/scala/cats/tests/KleisliTests.scala index 2cbd779a6a..6c86a28a0e 100644 --- a/tests/src/test/scala/cats/tests/KleisliTests.scala +++ b/tests/src/test/scala/cats/tests/KleisliTests.scala @@ -15,7 +15,8 @@ class KleisliTests extends CatsSuite { implicit def kleisliEq[F[_], A, B](implicit A: Arbitrary[A], FB: Eq[F[B]]): Eq[Kleisli[F, A, B]] = Eq.by[Kleisli[F, A, B], A => F[B]](_.run) - implicit val iso = MonoidalTests.Isomorphisms.covariant[Kleisli[Option, Int, ?]] + implicit val iso = MonoidalTests.Isomorphisms.invariant[Kleisli[Option, Int, ?]] + checkAll("Kleisli[Option, Int, Int]", MonoidalTests[Kleisli[Option, Int, ?]].monoidal[Int, Int, Int]) checkAll("Monoidal[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Monoidal[Kleisli[Option, Int, ?]])) diff --git a/tests/src/test/scala/cats/tests/MapTests.scala b/tests/src/test/scala/cats/tests/MapTests.scala index a80afd0107..ba6d77240b 100644 --- a/tests/src/test/scala/cats/tests/MapTests.scala +++ b/tests/src/test/scala/cats/tests/MapTests.scala @@ -5,7 +5,8 @@ import cats.laws.discipline.{TraverseTests, FlatMapTests, SerializableTests, Mon import cats.laws.discipline.eq._ class MapTests extends CatsSuite { - implicit val iso = MonoidalTests.Isomorphisms.covariant[Map[Int, ?]] + implicit val iso = MonoidalTests.Isomorphisms.invariant[Map[Int, ?]] + checkAll("Map[Int, Int]", MonoidalTests[Map[Int, ?]].monoidal[Int, Int, Int]) checkAll("Monoidal[Map[Int, ?]]", SerializableTests.serializable(Monoidal[Map[Int, ?]])) diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index e1e7a5c606..0b6f5c3ea0 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -13,10 +13,11 @@ import scala.util.Random class OneAndTests extends CatsSuite { checkAll("OneAnd[List, Int]", OrderLaws[OneAnd[List, Int]].eqv) + implicit val iso = MonoidalTests.Isomorphisms.invariant[OneAnd[ListWrapper, ?]](OneAnd.oneAndFunctor(ListWrapper.functor)) + // Test instances that have more general constraints { implicit val monadCombine = ListWrapper.monadCombine - implicit val iso = MonoidalTests.Isomorphisms.covariant[OneAnd[ListWrapper, ?]] checkAll("OneAnd[ListWrapper, Int]", MonoidalTests[OneAnd[ListWrapper, ?]].monoidal[Int, Int, Int]) checkAll("Monoidal[OneAnd[ListWrapper, A]]", SerializableTests.serializable(Monoidal[OneAnd[ListWrapper, ?]])) } @@ -48,6 +49,8 @@ class OneAndTests extends CatsSuite { implicitly[Comonad[NonEmptyList]] } + implicit val iso2 = MonoidalTests.Isomorphisms.invariant[OneAnd[List, ?]] + checkAll("NonEmptyList[Int]", MonadTests[NonEmptyList].monad[Int, Int, Int]) checkAll("Monad[NonEmptyList[A]]", SerializableTests.serializable(Monad[NonEmptyList])) diff --git a/tests/src/test/scala/cats/tests/OptionTTests.scala b/tests/src/test/scala/cats/tests/OptionTTests.scala index 6c35309456..a90ec73c1e 100644 --- a/tests/src/test/scala/cats/tests/OptionTTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTTests.scala @@ -112,14 +112,10 @@ class OptionTTests extends CatsSuite { } } - { - implicit val iso = MonoidalTests.Isomorphisms.covariant[OptionT[List, ?]] - checkAll("OptionT[List, Int]", MonoidalTests[OptionT[List, ?]].monoidal[Int, Int, Int]) - } - checkAll("Monoidal[OptionT[List, ?]]", SerializableTests.serializable(Monoidal[OptionT[List, ?]])) + implicit val iso = MonoidalTests.Isomorphisms.invariant[OptionT[List, ?]] - checkAll("OptionT[List, Int]", MonadTests[OptionT[List, ?]].monad[Int, Int, Int]) - checkAll("Monad[OptionT[List, ?]]", SerializableTests.serializable(Monad[OptionT[List, ?]])) + checkAll("OptionT[List, Int]", MonoidalTests[OptionT[List, ?]].monoidal[Int, Int, Int]) + checkAll("Monoidal[OptionT[List, ?]]", SerializableTests.serializable(Monoidal[OptionT[List, ?]])) test("liftF") { forAll { (xs: List[Int]) => @@ -145,7 +141,7 @@ class OptionTTests extends CatsSuite { } checkAll("OptionT[List, Int]", MonadCombineTests[OptionT[List, ?]].monad[Int, Int, Int]) - checkAll("MonadOptionT[List, ?]]", SerializableTests.serializable(Monad[OptionT[List, ?]])) + checkAll("Monad[OptionT[List, ?]]", SerializableTests.serializable(Monad[OptionT[List, ?]])) { implicit val F = ListWrapper.functor diff --git a/tests/src/test/scala/cats/tests/ProdTests.scala b/tests/src/test/scala/cats/tests/ProdTests.scala index 40bd777269..28d40f8a62 100644 --- a/tests/src/test/scala/cats/tests/ProdTests.scala +++ b/tests/src/test/scala/cats/tests/ProdTests.scala @@ -8,7 +8,7 @@ import cats.laws.discipline.eq._ import org.scalacheck.Arbitrary class ProdTests extends CatsSuite { - implicit val iso = MonoidalTests.Isomorphisms.covariant[Prod[Option, List, ?]] + implicit val iso = MonoidalTests.Isomorphisms.invariant[Prod[Option, List, ?]] checkAll("Prod[Option, List, Int]", MonoidalTests[Lambda[X => Prod[Option, List, X]]].monoidal[Int, Int, Int]) checkAll("Monoidal[Prod[Option, List, Int]]", SerializableTests.serializable(Monoidal[Lambda[X => Prod[Option, List, X]]])) diff --git a/tests/src/test/scala/cats/tests/StreamingTTests.scala b/tests/src/test/scala/cats/tests/StreamingTTests.scala index 61942b75bd..5110ccae11 100644 --- a/tests/src/test/scala/cats/tests/StreamingTTests.scala +++ b/tests/src/test/scala/cats/tests/StreamingTTests.scala @@ -4,26 +4,35 @@ package tests import algebra.laws.OrderLaws import cats.data.StreamingT -import cats.laws.discipline.{CoflatMapTests, MonadCombineTests, SerializableTests} +import cats.laws.discipline.{MonoidalTests, CoflatMapTests, MonadCombineTests, SerializableTests} import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq._ class StreamingTTests extends CatsSuite { - checkAll("StreamingT[Eval, ?]", MonadCombineTests[StreamingT[Eval, ?]].monad[Int, Int, Int]) - checkAll("StreamingT[Eval, ?]", CoflatMapTests[StreamingT[Eval, ?]].coflatMap[Int, Int, Int]) - checkAll("StreamingT[Eval, Int]", OrderLaws[StreamingT[Eval, Int]].order) - checkAll("Monad[StreamingT[Eval, ?]]", SerializableTests.serializable(Monad[StreamingT[Eval, ?]])) + { + implicit val iso = MonoidalTests.Isomorphisms.invariant[StreamingT[Eval, ?]] + checkAll("StreamingT[Eval, ?]", MonadCombineTests[StreamingT[Eval, ?]].monad[Int, Int, Int]) + checkAll("StreamingT[Eval, ?]", CoflatMapTests[StreamingT[Eval, ?]].coflatMap[Int, Int, Int]) + checkAll("StreamingT[Eval, Int]", OrderLaws[StreamingT[Eval, Int]].order) + checkAll("Monad[StreamingT[Eval, ?]]", SerializableTests.serializable(Monad[StreamingT[Eval, ?]])) + } - checkAll("StreamingT[Option, ?]", MonadCombineTests[StreamingT[Option, ?]].monad[Int, Int, Int]) - checkAll("StreamingT[Option, ?]", CoflatMapTests[StreamingT[Option, ?]].coflatMap[Int, Int, Int]) - checkAll("StreamingT[Option, Int]", OrderLaws[StreamingT[Option, Int]].order) - checkAll("Monad[StreamingT[Option, ?]]", SerializableTests.serializable(Monad[StreamingT[Option, ?]])) + { + implicit val iso = MonoidalTests.Isomorphisms.invariant[StreamingT[Option, ?]] + checkAll("StreamingT[Option, ?]", MonadCombineTests[StreamingT[Option, ?]].monad[Int, Int, Int]) + checkAll("StreamingT[Option, ?]", CoflatMapTests[StreamingT[Option, ?]].coflatMap[Int, Int, Int]) + checkAll("StreamingT[Option, Int]", OrderLaws[StreamingT[Option, Int]].order) + checkAll("Monad[StreamingT[Option, ?]]", SerializableTests.serializable(Monad[StreamingT[Option, ?]])) + } - checkAll("StreamingT[List, ?]", MonadCombineTests[StreamingT[List, ?]].monad[Int, Int, Int]) - checkAll("StreamingT[List, ?]", CoflatMapTests[StreamingT[List, ?]].coflatMap[Int, Int, Int]) - checkAll("StreamingT[List, Int]", OrderLaws[StreamingT[List, Int]].order) - checkAll("Monad[StreamingT[List, ?]]", SerializableTests.serializable(Monad[StreamingT[List, ?]])) + { + implicit val iso = MonoidalTests.Isomorphisms.invariant[StreamingT[List, ?]] + checkAll("StreamingT[List, ?]", MonadCombineTests[StreamingT[List, ?]].monad[Int, Int, Int]) + checkAll("StreamingT[List, ?]", CoflatMapTests[StreamingT[List, ?]].coflatMap[Int, Int, Int]) + checkAll("StreamingT[List, Int]", OrderLaws[StreamingT[List, Int]].order) + checkAll("Monad[StreamingT[List, ?]]", SerializableTests.serializable(Monad[StreamingT[List, ?]])) + } test("uncons with Id consistent with List headOption/tail") { forAll { (s: StreamingT[Id, Int]) => diff --git a/tests/src/test/scala/cats/tests/ValidatedTests.scala b/tests/src/test/scala/cats/tests/ValidatedTests.scala index 12f55ba179..915c2f6bca 100644 --- a/tests/src/test/scala/cats/tests/ValidatedTests.scala +++ b/tests/src/test/scala/cats/tests/ValidatedTests.scala @@ -13,7 +13,7 @@ import algebra.laws.OrderLaws import scala.util.Try class ValidatedTests extends CatsSuite { - implicit val iso = MonoidalTests.Isomorphisms.covariant[Validated[String, ?]] + implicit val iso = MonoidalTests.Isomorphisms.invariant[Validated[String, ?]] checkAll("Validated[String, Int]", MonoidalTests[Validated[String,?]].monoidal[Int, Int, Int]) checkAll("Monoidal[Validated[String,?]]", SerializableTests.serializable(Monoidal[Validated[String,?]])) diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index 9bc21198f4..89362ee35d 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -62,6 +62,8 @@ class WriterTTests extends CatsSuite { checkAll("Bifunctor[WriterT[ListWrapper, ?, ?]]", SerializableTests.serializable(Bifunctor[WriterT[ListWrapper, ?, ?]])) } + implicit val iso = MonoidalTests.Isomorphisms.invariant[WriterT[ListWrapper, ListWrapper[Int], ?]](WriterT.writerTFunctor(ListWrapper.functor)) + // We have varying instances available depending on `F` and `L`. // We also battle some inference issues with `Id`. // Below we go through some gymnastics in order to test both the implicit diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index de5287af4d..924b4db926 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -5,11 +5,13 @@ import cats.functor.Bifunctor import cats.data.{Xor, XorT} import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq.tuple3Eq import algebra.laws.OrderLaws class XorTTests extends CatsSuite { implicit val eq0 = XorT.xorTEq[List, String, String Xor Int] implicit val eq1 = XorT.xorTEq[XorT[List, String, ?], String, Int](eq0) + implicit val iso = MonoidalTests.Isomorphisms.invariant[XorT[List, String, ?]] checkAll("XorT[List, String, Int]", MonadErrorTests[XorT[List, String, ?], String].monadError[Int, Int, Int]) checkAll("MonadError[XorT[List, ?, ?]]", SerializableTests.serializable(MonadError[XorT[List, String, ?], String])) checkAll("XorT[List, String, Int]", MonoidKTests[XorT[List, String, ?]].monoidK[Int]) diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index b59d154844..b5ce071a04 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -15,10 +15,9 @@ import scala.util.Try class XorTests extends CatsSuite { checkAll("Xor[String, Int]", GroupLaws[Xor[String, Int]].monoid) - { - implicit val iso = MonoidalTests.Isomorphisms.covariant[Xor[String, ?]] - checkAll("Xor[String, Int]", MonoidalTests[Xor[String, ?]].monoidal[Int, Int, Int]) - } + implicit val iso = MonoidalTests.Isomorphisms.invariant[Xor[String, ?]] + + checkAll("Xor[String, Int]", MonoidalTests[Xor[String, ?]].monoidal[Int, Int, Int]) checkAll("Monoidal[Xor, ?]", SerializableTests.serializable(Monoidal[Xor[String, ?]])) implicit val eq0 = XorT.xorTEq[Xor[String, ?], String, Int]