From 5380d00df0ec6814402ecc1c1dfd30f9939d4c82 Mon Sep 17 00:00:00 2001 From: Luis Angel Vicente Sanchez Date: Sat, 14 Feb 2015 15:01:55 +0000 Subject: [PATCH 1/9] Add Semigroup and Monoid laws (address #196). --- .../src/main/scala/cats/laws/MonoidLaws.scala | 20 ++++++++++ .../main/scala/cats/laws/SemigroupLaws.scala | 17 ++++++++ .../cats/laws/discipline/MonoidTests.scala | 33 +++++++++++++++ .../cats/laws/discipline/SemigroupTests.scala | 40 +++++++++++++++++++ 4 files changed, 110 insertions(+) create mode 100644 laws/src/main/scala/cats/laws/MonoidLaws.scala create mode 100644 laws/src/main/scala/cats/laws/SemigroupLaws.scala create mode 100644 laws/src/main/scala/cats/laws/discipline/MonoidTests.scala create mode 100644 laws/src/main/scala/cats/laws/discipline/SemigroupTests.scala diff --git a/laws/src/main/scala/cats/laws/MonoidLaws.scala b/laws/src/main/scala/cats/laws/MonoidLaws.scala new file mode 100644 index 0000000000..f0d8901b2c --- /dev/null +++ b/laws/src/main/scala/cats/laws/MonoidLaws.scala @@ -0,0 +1,20 @@ +package cats +package laws + +/** + * Laws that must be obeyed by any [[cats.Monoid]]. + */ +trait MonoidLaws[A] extends SemigroupLaws[A] { + override implicit def A: Monoid[A] + + def leftIdentity(a: A): (A, A) = + A.combine(A.empty, a) -> a + + def rightIdentity(a: A): (A, A) = + A.combine(a, A.empty) -> a +} + +object MonoidLaws { + def apply[A](implicit ev: Monoid[A]): MonoidLaws[A] = + new MonoidLaws[A] { def A = ev } +} diff --git a/laws/src/main/scala/cats/laws/SemigroupLaws.scala b/laws/src/main/scala/cats/laws/SemigroupLaws.scala new file mode 100644 index 0000000000..32f5ec8a56 --- /dev/null +++ b/laws/src/main/scala/cats/laws/SemigroupLaws.scala @@ -0,0 +1,17 @@ +package cats +package laws + +/** + * Laws that must be obeyed by any [[cats.Semigroup]]. + */ +trait SemigroupLaws[A] { + implicit def A: Semigroup[A] + + def associative(a: A, b: A, c: A): (A, A) = + A.combine(A.combine(a, b), c) -> A.combine(a, A.combine(b, c)) +} + +object SemigroupLaws { + def apply[A](implicit ev: Semigroup[A]): SemigroupLaws[A] = + new SemigroupLaws[A] { def A = ev } +} diff --git a/laws/src/main/scala/cats/laws/discipline/MonoidTests.scala b/laws/src/main/scala/cats/laws/discipline/MonoidTests.scala new file mode 100644 index 0000000000..3410c15660 --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/MonoidTests.scala @@ -0,0 +1,33 @@ +package cats +package laws +package discipline + +import algebra.laws._ +import org.scalacheck.Prop._ +import org.scalacheck.{Arbitrary, Prop} +import org.typelevel.discipline.Laws + +object MonoidTests { + def apply[A: Arbitrary](implicit eqa: Eq[A]): MonoidTests[A] = + new MonoidTests[A] { + def EqA = eqa + def ArbA = implicitly[Arbitrary[A]] + } +} + +trait MonoidTests[A] extends SemigroupTests[A] { + def identity(implicit A: Monoid[A]) = { + val laws = MonoidLaws[A] + new SemigroupProperties( + name = "monoid", + parents = Seq(associative), + "left identity" -> forAll { (a: A) => + val (lhs, rhs) = laws.leftIdentity(a) + lhs ?== rhs + }, + "right identity" -> forAll { (a: A) => + val (lhs, rhs) = laws.rightIdentity(a) + lhs ?== rhs + }) + } +} diff --git a/laws/src/main/scala/cats/laws/discipline/SemigroupTests.scala b/laws/src/main/scala/cats/laws/discipline/SemigroupTests.scala new file mode 100644 index 0000000000..ce8001654a --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/SemigroupTests.scala @@ -0,0 +1,40 @@ +package cats +package laws +package discipline + +import algebra.laws._ +import org.scalacheck.Prop._ +import org.scalacheck.{Arbitrary, Prop} +import org.typelevel.discipline.Laws + +object SemigroupTests { + def apply[A: Arbitrary](implicit eqa: Eq[A]): SemigroupTests[A] = + new SemigroupTests[A] { + def EqA = eqa + def ArbA = implicitly[Arbitrary[A]] + } +} + +trait SemigroupTests[A] extends Laws { + implicit def EqA: Eq[A] + implicit def ArbA: Arbitrary[A] + + def associative(implicit A: Semigroup[A]) = { + val laws = SemigroupLaws[A] + new SemigroupProperties( + name = "semigroup", + parents = Nil, + "associative" -> forAll { (a: A, b: A, c: A) => + val (lhs, rhs) = laws.associative(a, b, c) + lhs ?== rhs + }) + } + + class SemigroupProperties( + val name: String, + val parents: Seq[SemigroupProperties], + val props: (String, Prop)* + ) extends RuleSet { + val bases = Nil + } +} From 2eff8843600075c227e41134c59d342ea63571ac Mon Sep 17 00:00:00 2001 From: Luis Angel Vicente Sanchez Date: Sat, 14 Feb 2015 16:36:24 +0000 Subject: [PATCH 2/9] Replace Semigroup/Monoid laws with SemigroupK/MonoidK laws. --- .../main/scala/cats/laws/MonoidKLaws.scala | 20 +++++++++ .../src/main/scala/cats/laws/MonoidLaws.scala | 20 --------- .../main/scala/cats/laws/SemigroupKLaws.scala | 17 ++++++++ .../main/scala/cats/laws/SemigroupLaws.scala | 17 -------- .../cats/laws/discipline/ArbitraryK.scala | 6 +++ .../cats/laws/discipline/MonoidKTests.scala | 33 ++++++++++++++ .../cats/laws/discipline/MonoidTests.scala | 33 -------------- .../laws/discipline/SemigroupKTests.scala | 43 +++++++++++++++++++ .../cats/laws/discipline/SemigroupTests.scala | 40 ----------------- 9 files changed, 119 insertions(+), 110 deletions(-) create mode 100644 laws/src/main/scala/cats/laws/MonoidKLaws.scala delete mode 100644 laws/src/main/scala/cats/laws/MonoidLaws.scala create mode 100644 laws/src/main/scala/cats/laws/SemigroupKLaws.scala delete mode 100644 laws/src/main/scala/cats/laws/SemigroupLaws.scala create mode 100644 laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala delete mode 100644 laws/src/main/scala/cats/laws/discipline/MonoidTests.scala create mode 100644 laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala delete mode 100644 laws/src/main/scala/cats/laws/discipline/SemigroupTests.scala diff --git a/laws/src/main/scala/cats/laws/MonoidKLaws.scala b/laws/src/main/scala/cats/laws/MonoidKLaws.scala new file mode 100644 index 0000000000..3eaae8025b --- /dev/null +++ b/laws/src/main/scala/cats/laws/MonoidKLaws.scala @@ -0,0 +1,20 @@ +package cats +package laws + +/** + * Laws that must be obeyed by any [[cats.MonoidK]]. + */ +trait MonoidKLaws[F[_]] extends SemigroupKLaws[F] { + override implicit def F: MonoidK[F] + + def leftIdentity[A](a: F[A]): (F[A], F[A]) = + F.combine(F.empty, a) -> a + + def rightIdentity[A](a: F[A]): (F[A], F[A]) = + F.combine(a, F.empty) -> a +} + +object MonoidKLaws { + def apply[F[_]](implicit ev: MonoidK[F]): MonoidKLaws[F] = + new MonoidKLaws[F] { def F = ev } +} diff --git a/laws/src/main/scala/cats/laws/MonoidLaws.scala b/laws/src/main/scala/cats/laws/MonoidLaws.scala deleted file mode 100644 index f0d8901b2c..0000000000 --- a/laws/src/main/scala/cats/laws/MonoidLaws.scala +++ /dev/null @@ -1,20 +0,0 @@ -package cats -package laws - -/** - * Laws that must be obeyed by any [[cats.Monoid]]. - */ -trait MonoidLaws[A] extends SemigroupLaws[A] { - override implicit def A: Monoid[A] - - def leftIdentity(a: A): (A, A) = - A.combine(A.empty, a) -> a - - def rightIdentity(a: A): (A, A) = - A.combine(a, A.empty) -> a -} - -object MonoidLaws { - def apply[A](implicit ev: Monoid[A]): MonoidLaws[A] = - new MonoidLaws[A] { def A = ev } -} diff --git a/laws/src/main/scala/cats/laws/SemigroupKLaws.scala b/laws/src/main/scala/cats/laws/SemigroupKLaws.scala new file mode 100644 index 0000000000..e08ce05ec3 --- /dev/null +++ b/laws/src/main/scala/cats/laws/SemigroupKLaws.scala @@ -0,0 +1,17 @@ +package cats +package laws + +/** + * Laws that must be obeyed by any [[cats.SemigroupK]]. + */ +trait SemigroupKLaws[F[_]] { + implicit def F: SemigroupK[F] + + def associative[A](a: F[A], b: F[A], c: F[A]): (F[A], F[A]) = + F.combine(F.combine(a, b), c) -> F.combine(a, F.combine(b, c)) +} + +object SemigroupKLaws { + def apply[F[_]](implicit ev: SemigroupK[F]): SemigroupKLaws[F] = + new SemigroupKLaws[F] { def F = ev } +} diff --git a/laws/src/main/scala/cats/laws/SemigroupLaws.scala b/laws/src/main/scala/cats/laws/SemigroupLaws.scala deleted file mode 100644 index 32f5ec8a56..0000000000 --- a/laws/src/main/scala/cats/laws/SemigroupLaws.scala +++ /dev/null @@ -1,17 +0,0 @@ -package cats -package laws - -/** - * Laws that must be obeyed by any [[cats.Semigroup]]. - */ -trait SemigroupLaws[A] { - implicit def A: Semigroup[A] - - def associative(a: A, b: A, c: A): (A, A) = - A.combine(A.combine(a, b), c) -> A.combine(a, A.combine(b, c)) -} - -object SemigroupLaws { - def apply[A](implicit ev: Semigroup[A]): SemigroupLaws[A] = - new SemigroupLaws[A] { def A = ev } -} diff --git a/laws/src/main/scala/cats/laws/discipline/ArbitraryK.scala b/laws/src/main/scala/cats/laws/discipline/ArbitraryK.scala index 6c5b699020..8a343ec555 100644 --- a/laws/src/main/scala/cats/laws/discipline/ArbitraryK.scala +++ b/laws/src/main/scala/cats/laws/discipline/ArbitraryK.scala @@ -41,4 +41,10 @@ object ArbitraryK { implicit def cokleisliA[F[_], A]: ArbitraryK[Cokleisli[F, A, ?]] = new ArbitraryK[Cokleisli[F, A, ?]]{ def synthesize[B: Arbitrary]: Arbitrary[Cokleisli[F, A, B]] = implicitly } + + implicit val stream: ArbitraryK[Stream] = + new ArbitraryK[Stream] { def synthesize[A: Arbitrary]: Arbitrary[Stream[A]] = implicitly } + + implicit val vector: ArbitraryK[Vector] = + new ArbitraryK[Vector] { def synthesize[A: Arbitrary]: Arbitrary[Vector[A]] = implicitly } } diff --git a/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala b/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala new file mode 100644 index 0000000000..50a360500d --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala @@ -0,0 +1,33 @@ +package cats +package laws +package discipline + +import algebra.laws._ +import org.scalacheck.Prop._ +import org.scalacheck.Arbitrary + +object MonoidKTests { + def apply[F[_]: ArbitraryK, A: Arbitrary](implicit eqfa: Eq[F[A]]): MonoidKTests[F, A] = + new MonoidKTests[F, A] { + def EqFA = eqfa + def ArbA = implicitly[Arbitrary[A]] + def ArbF = implicitly[ArbitraryK[F]] + } +} + +trait MonoidKTests[F[_], A] extends SemigroupKTests[F, A] { + def identity(implicit A: MonoidK[F]) = { + val laws = MonoidKLaws[F] + new SemigroupKProperties( + name = "monoidK", + parents = Seq(associative), + "left identity" -> forAll { (a: F[A]) => + val (lhs, rhs) = laws.leftIdentity(a) + lhs ?== rhs + }, + "right identity" -> forAll { (a: F[A]) => + val (lhs, rhs) = laws.rightIdentity(a) + lhs ?== rhs + }) + } +} diff --git a/laws/src/main/scala/cats/laws/discipline/MonoidTests.scala b/laws/src/main/scala/cats/laws/discipline/MonoidTests.scala deleted file mode 100644 index 3410c15660..0000000000 --- a/laws/src/main/scala/cats/laws/discipline/MonoidTests.scala +++ /dev/null @@ -1,33 +0,0 @@ -package cats -package laws -package discipline - -import algebra.laws._ -import org.scalacheck.Prop._ -import org.scalacheck.{Arbitrary, Prop} -import org.typelevel.discipline.Laws - -object MonoidTests { - def apply[A: Arbitrary](implicit eqa: Eq[A]): MonoidTests[A] = - new MonoidTests[A] { - def EqA = eqa - def ArbA = implicitly[Arbitrary[A]] - } -} - -trait MonoidTests[A] extends SemigroupTests[A] { - def identity(implicit A: Monoid[A]) = { - val laws = MonoidLaws[A] - new SemigroupProperties( - name = "monoid", - parents = Seq(associative), - "left identity" -> forAll { (a: A) => - val (lhs, rhs) = laws.leftIdentity(a) - lhs ?== rhs - }, - "right identity" -> forAll { (a: A) => - val (lhs, rhs) = laws.rightIdentity(a) - lhs ?== rhs - }) - } -} diff --git a/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala b/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala new file mode 100644 index 0000000000..4c17d33b42 --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala @@ -0,0 +1,43 @@ +package cats +package laws +package discipline + +import algebra.laws._ +import org.scalacheck.Prop._ +import org.scalacheck.{Arbitrary, Prop} +import org.typelevel.discipline.Laws + +object SemigroupKTests { + def apply[F[_]: ArbitraryK, A: Arbitrary](implicit eqfa: Eq[F[A]]): SemigroupKTests[F, A] = + new SemigroupKTests[F, A] { + def EqFA = eqfa + def ArbA = implicitly[Arbitrary[A]] + def ArbF = implicitly[ArbitraryK[F]] + } +} + +trait SemigroupKTests[F[_], A] extends Laws { + implicit def EqFA: Eq[F[A]] + implicit def ArbA: Arbitrary[A] + implicit def ArbF: ArbitraryK[F] + implicit def ArbFA: Arbitrary[F[A]] = ArbF.synthesize[A](ArbA) + + def associative(implicit F: SemigroupK[F]) = { + val laws = SemigroupKLaws[F] + new SemigroupKProperties( + name = "semigroupK", + parents = Nil, + "associative" -> forAll { (a: F[A], b: F[A], c: F[A]) => + val (lhs, rhs) = laws.associative(a, b, c) + lhs ?== rhs + }) + } + + class SemigroupKProperties( + val name: String, + val parents: Seq[SemigroupKProperties], + val props: (String, Prop)* + ) extends RuleSet { + val bases = Nil + } +} diff --git a/laws/src/main/scala/cats/laws/discipline/SemigroupTests.scala b/laws/src/main/scala/cats/laws/discipline/SemigroupTests.scala deleted file mode 100644 index ce8001654a..0000000000 --- a/laws/src/main/scala/cats/laws/discipline/SemigroupTests.scala +++ /dev/null @@ -1,40 +0,0 @@ -package cats -package laws -package discipline - -import algebra.laws._ -import org.scalacheck.Prop._ -import org.scalacheck.{Arbitrary, Prop} -import org.typelevel.discipline.Laws - -object SemigroupTests { - def apply[A: Arbitrary](implicit eqa: Eq[A]): SemigroupTests[A] = - new SemigroupTests[A] { - def EqA = eqa - def ArbA = implicitly[Arbitrary[A]] - } -} - -trait SemigroupTests[A] extends Laws { - implicit def EqA: Eq[A] - implicit def ArbA: Arbitrary[A] - - def associative(implicit A: Semigroup[A]) = { - val laws = SemigroupLaws[A] - new SemigroupProperties( - name = "semigroup", - parents = Nil, - "associative" -> forAll { (a: A, b: A, c: A) => - val (lhs, rhs) = laws.associative(a, b, c) - lhs ?== rhs - }) - } - - class SemigroupProperties( - val name: String, - val parents: Seq[SemigroupProperties], - val props: (String, Prop)* - ) extends RuleSet { - val bases = Nil - } -} From 5bf91338f1f07d1c84de46c11fcc121213d2a450 Mon Sep 17 00:00:00 2001 From: Luis Angel Vicente Sanchez Date: Sat, 14 Feb 2015 16:38:13 +0000 Subject: [PATCH 3/9] Add Eq instances for Vector and Stream (needed for tests). --- std/src/main/scala/cats/std/stream.scala | 22 +++++++++++++++++++++- std/src/main/scala/cats/std/vector.scala | 20 +++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/std/src/main/scala/cats/std/stream.scala b/std/src/main/scala/cats/std/stream.scala index 1e12c37811..0e11569df9 100644 --- a/std/src/main/scala/cats/std/stream.scala +++ b/std/src/main/scala/cats/std/stream.scala @@ -1,7 +1,7 @@ package cats package std -import scala.annotation.tailrec +import scala.collection.immutable.Stream.Empty trait StreamInstances { implicit val streamInstance: Traverse[Stream] with MonadCombine[Stream] with CoFlatMap[Stream] = @@ -50,4 +50,24 @@ trait StreamInstances { G.map(gsb)(_.force) } } + + // TODO: eventually use algebra's instances (which will deal with + // implicit priority between Eq/PartialOrder/Order). + + implicit def eqStream[A](implicit ev: Eq[A]): Eq[Stream[A]] = + new Eq[Stream[A]] { + def eqv(x: Stream[A], y: Stream[A]): Boolean = { + def loop(xs: Stream[A], ys: Stream[A]): Boolean = + xs match { + case Empty => ys.isEmpty + case a #:: xs => + ys match { + case Empty => false + case b #:: ys => if (ev.neqv(a, b)) false else loop(xs, ys) + } + } + loop(x, y) + } + } + } diff --git a/std/src/main/scala/cats/std/vector.scala b/std/src/main/scala/cats/std/vector.scala index 097e810758..557d049897 100644 --- a/std/src/main/scala/cats/std/vector.scala +++ b/std/src/main/scala/cats/std/vector.scala @@ -1,7 +1,6 @@ package cats package std -import scala.annotation.tailrec import scala.collection.immutable.VectorBuilder trait VectorInstances { @@ -39,4 +38,23 @@ trait VectorInstances { G.map(gbb)(_.result) } } + + // TODO: eventually use algebra's instances (which will deal with + // implicit priority between Eq/PartialOrder/Order). + + implicit def eqVector[A](implicit ev: Eq[A]): Eq[Vector[A]] = + new Eq[Vector[A]] { + def eqv(x: Vector[A], y: Vector[A]): Boolean = { + def loop(xs: Vector[A], ys: Vector[A]): Boolean = + xs match { + case Seq() => ys.isEmpty + case a +: xs => + ys match { + case Seq() => false + case b +: ys => if (ev.neqv(a, b)) false else loop(xs, ys) + } + } + loop(x, y) + } + } } From 0f72c99e8ab1a7681d0dafce0d2938db1a83240b Mon Sep 17 00:00:00 2001 From: Luis Angel Vicente Sanchez Date: Sat, 14 Feb 2015 16:38:59 +0000 Subject: [PATCH 4/9] Add for all existent SemigroupK/MonoidK instances. --- tests/src/test/scala/cats/tests/StdTests.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/src/test/scala/cats/tests/StdTests.scala b/tests/src/test/scala/cats/tests/StdTests.scala index 94a7854815..81bcb161ad 100644 --- a/tests/src/test/scala/cats/tests/StdTests.scala +++ b/tests/src/test/scala/cats/tests/StdTests.scala @@ -1,8 +1,6 @@ package cats.tests -import algebra.laws._ -import cats.laws.discipline.FunctorTests$ -import cats.laws.discipline.{ComonadTests, FunctorTests} +import cats.laws.discipline.{MonoidKTests, ComonadTests, FunctorTests} import org.typelevel.discipline.scalatest.Discipline import org.scalatest.FunSuite @@ -13,11 +11,17 @@ import algebra.std.string._ import cats.std.function._ import cats.std.list._ import cats.std.option._ +import cats.std.stream._ +import cats.std.vector._ class StdTests extends FunSuite with Discipline { checkAll("Function0[Int]", FunctorTests[Function0, Int].applicative[Int, Int]) checkAll("Function0[Int]", ComonadTests[Function0, Int, Int].comonad[Int]) checkAll("Option[Int]", FunctorTests[Option, Int].applicative[Int, Int]) checkAll("Option[String]", FunctorTests[Option, String].applicative[Int, Int]) + checkAll("Option[String]", MonoidKTests[Option, String].identity) checkAll("List[Int]", FunctorTests[List, Int].applicative[Int, Int]) + checkAll("List[Int]", MonoidKTests[List, Int].identity) + checkAll("Stream[Int]", MonoidKTests[Stream, Int].identity) + checkAll("Vector[Int]", MonoidKTests[Vector, Int].identity) } From c8ab0d4b61ba5cb043923a0d1334bc839dd500b3 Mon Sep 17 00:00:00 2001 From: Luis Angel Vicente Sanchez Date: Tue, 17 Feb 2015 17:33:00 +0000 Subject: [PATCH 5/9] Update SemigroupK/MonoidK laws and tests to use IsEq. --- laws/src/main/scala/cats/laws/MonoidKLaws.scala | 8 ++++---- laws/src/main/scala/cats/laws/SemigroupKLaws.scala | 4 ++-- .../scala/cats/laws/discipline/MonoidKTests.scala | 11 +++-------- .../scala/cats/laws/discipline/SemigroupKTests.scala | 6 ++---- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/laws/src/main/scala/cats/laws/MonoidKLaws.scala b/laws/src/main/scala/cats/laws/MonoidKLaws.scala index 3eaae8025b..495e9d1016 100644 --- a/laws/src/main/scala/cats/laws/MonoidKLaws.scala +++ b/laws/src/main/scala/cats/laws/MonoidKLaws.scala @@ -7,11 +7,11 @@ package laws trait MonoidKLaws[F[_]] extends SemigroupKLaws[F] { override implicit def F: MonoidK[F] - def leftIdentity[A](a: F[A]): (F[A], F[A]) = - F.combine(F.empty, a) -> a + def leftIdentity[A](a: F[A]): IsEq[F[A]] = + F.combine(F.empty, a) <-> a - def rightIdentity[A](a: F[A]): (F[A], F[A]) = - F.combine(a, F.empty) -> a + def rightIdentity[A](a: F[A]): IsEq[F[A]] = + F.combine(a, F.empty) <-> a } object MonoidKLaws { diff --git a/laws/src/main/scala/cats/laws/SemigroupKLaws.scala b/laws/src/main/scala/cats/laws/SemigroupKLaws.scala index e08ce05ec3..5cadd329d3 100644 --- a/laws/src/main/scala/cats/laws/SemigroupKLaws.scala +++ b/laws/src/main/scala/cats/laws/SemigroupKLaws.scala @@ -7,8 +7,8 @@ package laws trait SemigroupKLaws[F[_]] { implicit def F: SemigroupK[F] - def associative[A](a: F[A], b: F[A], c: F[A]): (F[A], F[A]) = - F.combine(F.combine(a, b), c) -> F.combine(a, F.combine(b, c)) + def associative[A](a: F[A], b: F[A], c: F[A]): IsEq[F[A]] = + F.combine(F.combine(a, b), c) <-> F.combine(a, F.combine(b, c)) } object SemigroupKLaws { diff --git a/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala b/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala index 50a360500d..f55ae41f92 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala @@ -21,13 +21,8 @@ trait MonoidKTests[F[_], A] extends SemigroupKTests[F, A] { new SemigroupKProperties( name = "monoidK", parents = Seq(associative), - "left identity" -> forAll { (a: F[A]) => - val (lhs, rhs) = laws.leftIdentity(a) - lhs ?== rhs - }, - "right identity" -> forAll { (a: F[A]) => - val (lhs, rhs) = laws.rightIdentity(a) - lhs ?== rhs - }) + "left identity" -> forAll(laws.leftIdentity[A] _), + "right identity" -> forAll(laws.rightIdentity[A] _) + ) } } diff --git a/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala b/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala index 4c17d33b42..745b83f64f 100644 --- a/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala @@ -27,10 +27,8 @@ trait SemigroupKTests[F[_], A] extends Laws { new SemigroupKProperties( name = "semigroupK", parents = Nil, - "associative" -> forAll { (a: F[A], b: F[A], c: F[A]) => - val (lhs, rhs) = laws.associative(a, b, c) - lhs ?== rhs - }) + "associative" -> forAll(laws.associative[A] _) + ) } class SemigroupKProperties( From 15dccd4f4b70c66247f80d6955506f1d82fe4d6a Mon Sep 17 00:00:00 2001 From: Luis Angel Vicente Sanchez Date: Wed, 18 Feb 2015 13:06:19 +0000 Subject: [PATCH 6/9] * modify SemigroupKTests and MonoidKTests to follow implicit usage conventions. --- .../src/main/scala/cats/laws/discipline/MonoidKTests.scala | 7 +++---- .../main/scala/cats/laws/discipline/SemigroupKTests.scala | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala b/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala index f55ae41f92..49d6e565c5 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala @@ -2,16 +2,15 @@ package cats package laws package discipline -import algebra.laws._ import org.scalacheck.Prop._ import org.scalacheck.Arbitrary object MonoidKTests { - def apply[F[_]: ArbitraryK, A: Arbitrary](implicit eqfa: Eq[F[A]]): MonoidKTests[F, A] = + def apply[F[_]: ArbitraryK, A: Arbitrary](implicit eqfa: Eq[F[A]], arba: Arbitrary[A], arbf: ArbitraryK[F]): MonoidKTests[F, A] = new MonoidKTests[F, A] { def EqFA = eqfa - def ArbA = implicitly[Arbitrary[A]] - def ArbF = implicitly[ArbitraryK[F]] + def ArbA = arba + def ArbF = arbf } } diff --git a/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala b/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala index 745b83f64f..ea6ca7ebdb 100644 --- a/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala @@ -2,17 +2,16 @@ package cats package laws package discipline -import algebra.laws._ import org.scalacheck.Prop._ import org.scalacheck.{Arbitrary, Prop} import org.typelevel.discipline.Laws object SemigroupKTests { - def apply[F[_]: ArbitraryK, A: Arbitrary](implicit eqfa: Eq[F[A]]): SemigroupKTests[F, A] = + def apply[F[_]: ArbitraryK, A: Arbitrary](implicit eqfa: Eq[F[A]], arba: Arbitrary[A], arbf: ArbitraryK[F]): SemigroupKTests[F, A] = new SemigroupKTests[F, A] { def EqFA = eqfa - def ArbA = implicitly[Arbitrary[A]] - def ArbF = implicitly[ArbitraryK[F]] + def ArbA = arba + def ArbF = arbf } } From 233c309744b3ef3b78a56d013c31d543721e5537 Mon Sep 17 00:00:00 2001 From: Luis Angel Vicente Sanchez Date: Thu, 19 Feb 2015 20:45:53 +0000 Subject: [PATCH 7/9] Make SemigroupKTests/MonoidKTests consistent with other tests. --- .../cats/laws/discipline/MonoidKTests.scala | 37 +++++++------- .../laws/discipline/SemigroupKTests.scala | 48 ++++++++----------- .../src/test/scala/cats/tests/StdTests.scala | 6 +-- 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala b/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala index 49d6e565c5..b616197af9 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala @@ -5,23 +5,28 @@ package discipline import org.scalacheck.Prop._ import org.scalacheck.Arbitrary -object MonoidKTests { - def apply[F[_]: ArbitraryK, A: Arbitrary](implicit eqfa: Eq[F[A]], arba: Arbitrary[A], arbf: ArbitraryK[F]): MonoidKTests[F, A] = - new MonoidKTests[F, A] { - def EqFA = eqfa - def ArbA = arba - def ArbF = arbf +trait MonoidKTests[F[_]] extends SemigroupKTests[F] { + def laws: MonoidKLaws[F] + + def identity[A: Arbitrary](implicit + ArbF: ArbitraryK[F], + EqFA: Eq[F[A]] + ): RuleSet = { + implicit def ArbFA: Arbitrary[F[A]] = ArbF.synthesize[A] + + new RuleSet { + val name = "monoidK" + val bases = Nil + val parents = Seq(associative[A]) + val props = Seq( + "left identity" -> forAll(laws.leftIdentity[A] _), + "right identity" -> forAll(laws.rightIdentity[A] _) + ) } + } } -trait MonoidKTests[F[_], A] extends SemigroupKTests[F, A] { - def identity(implicit A: MonoidK[F]) = { - val laws = MonoidKLaws[F] - new SemigroupKProperties( - name = "monoidK", - parents = Seq(associative), - "left identity" -> forAll(laws.leftIdentity[A] _), - "right identity" -> forAll(laws.rightIdentity[A] _) - ) - } +object MonoidKTests { + def apply[F[_] : MonoidK]: MonoidKTests[F] = + new MonoidKTests[F] { def laws = MonoidKLaws[F] } } diff --git a/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala b/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala index ea6ca7ebdb..3d46667199 100644 --- a/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala @@ -3,38 +3,30 @@ package laws package discipline import org.scalacheck.Prop._ -import org.scalacheck.{Arbitrary, Prop} +import org.scalacheck.Arbitrary import org.typelevel.discipline.Laws -object SemigroupKTests { - def apply[F[_]: ArbitraryK, A: Arbitrary](implicit eqfa: Eq[F[A]], arba: Arbitrary[A], arbf: ArbitraryK[F]): SemigroupKTests[F, A] = - new SemigroupKTests[F, A] { - def EqFA = eqfa - def ArbA = arba - def ArbF = arbf - } -} +trait SemigroupKTests[F[_]] extends Laws { + def laws: SemigroupKLaws[F] -trait SemigroupKTests[F[_], A] extends Laws { - implicit def EqFA: Eq[F[A]] - implicit def ArbA: Arbitrary[A] - implicit def ArbF: ArbitraryK[F] - implicit def ArbFA: Arbitrary[F[A]] = ArbF.synthesize[A](ArbA) + def associative[A: Arbitrary](implicit + ArbF: ArbitraryK[F], + EqFA: Eq[F[A]] + ): RuleSet = { + implicit def ArbFA: Arbitrary[F[A]] = ArbF.synthesize[A] - def associative(implicit F: SemigroupK[F]) = { - val laws = SemigroupKLaws[F] - new SemigroupKProperties( - name = "semigroupK", - parents = Nil, - "associative" -> forAll(laws.associative[A] _) - ) + new RuleSet { + val name = "semigroupK" + val bases = Nil + val parents = Nil + val props = Seq( + "associative" -> forAll(laws.associative[A] _) + ) + } } +} - class SemigroupKProperties( - val name: String, - val parents: Seq[SemigroupKProperties], - val props: (String, Prop)* - ) extends RuleSet { - val bases = Nil - } +object SemigroupKTests { + def apply[F[_]: SemigroupK]: SemigroupKTests[F] = + new SemigroupKTests[F] { def laws = SemigroupKLaws[F] } } diff --git a/tests/src/test/scala/cats/tests/StdTests.scala b/tests/src/test/scala/cats/tests/StdTests.scala index 4563a9da8f..0b77a8f03c 100644 --- a/tests/src/test/scala/cats/tests/StdTests.scala +++ b/tests/src/test/scala/cats/tests/StdTests.scala @@ -21,7 +21,7 @@ class StdTests extends FunSuite with Discipline { checkAll("Option[Int]", MonadTests[Option].monad[Int, Int, Int]) checkAll("Option[String]", MonadTests[Option].monad[String, Int, Int]) checkAll("List[Int]", MonadTests[List].monad[Int, Int, Int]) - checkAll("List[Int]", MonoidKTests[List, Int].identity) - checkAll("Stream[Int]", MonoidKTests[Stream, Int].identity) - checkAll("Vector[Int]", MonoidKTests[Vector, Int].identity) + checkAll("List[Int]", MonoidKTests[List].identity[Int]) + checkAll("Stream[Int]", MonoidKTests[Stream].identity[Int]) + checkAll("Vector[Int]", MonoidKTests[Vector].identity[Int]) } From 2fc7e7198beeedaf271afd76a8a7e1bee4c9d985 Mon Sep 17 00:00:00 2001 From: Luis Angel Vicente Sanchez Date: Fri, 20 Feb 2015 00:03:13 +0000 Subject: [PATCH 8/9] Rename MonoidKTests/SemigroupKTests methods. --- laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala | 4 ++-- .../main/scala/cats/laws/discipline/SemigroupKTests.scala | 2 +- tests/src/test/scala/cats/tests/StdTests.scala | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala b/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala index b616197af9..6300549d77 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonoidKTests.scala @@ -8,7 +8,7 @@ import org.scalacheck.Arbitrary trait MonoidKTests[F[_]] extends SemigroupKTests[F] { def laws: MonoidKLaws[F] - def identity[A: Arbitrary](implicit + def monoidK[A: Arbitrary](implicit ArbF: ArbitraryK[F], EqFA: Eq[F[A]] ): RuleSet = { @@ -17,7 +17,7 @@ trait MonoidKTests[F[_]] extends SemigroupKTests[F] { new RuleSet { val name = "monoidK" val bases = Nil - val parents = Seq(associative[A]) + val parents = Seq(semigroupK[A]) val props = Seq( "left identity" -> forAll(laws.leftIdentity[A] _), "right identity" -> forAll(laws.rightIdentity[A] _) diff --git a/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala b/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala index 3d46667199..0e14007e46 100644 --- a/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/SemigroupKTests.scala @@ -9,7 +9,7 @@ import org.typelevel.discipline.Laws trait SemigroupKTests[F[_]] extends Laws { def laws: SemigroupKLaws[F] - def associative[A: Arbitrary](implicit + def semigroupK[A: Arbitrary](implicit ArbF: ArbitraryK[F], EqFA: Eq[F[A]] ): RuleSet = { diff --git a/tests/src/test/scala/cats/tests/StdTests.scala b/tests/src/test/scala/cats/tests/StdTests.scala index 96b6008a96..fbeaab20d1 100644 --- a/tests/src/test/scala/cats/tests/StdTests.scala +++ b/tests/src/test/scala/cats/tests/StdTests.scala @@ -20,7 +20,7 @@ class StdTests extends FunSuite with Discipline { checkAll("Option[Int]", MonadFilterTests[Option].monadFilter[Int, Int, Int]) checkAll("Option[String]", MonadFilterTests[Option].monadFilter[String, Int, Int]) checkAll("List[Int]", MonadFilterTests[List].monadFilter[Int, Int, Int]) - checkAll("List[Int]", MonoidKTests[List].identity[Int]) - checkAll("Stream[Int]", MonoidKTests[Stream].identity[Int]) - checkAll("Vector[Int]", MonoidKTests[Vector].identity[Int]) + checkAll("List[Int]", MonoidKTests[List].monoidK[Int]) + checkAll("Stream[Int]", MonoidKTests[Stream].monoidK[Int]) + checkAll("Vector[Int]", MonoidKTests[Vector].monoidK[Int]) } From 0a3fb6151d1af905251901b8ae7b38752f5694e4 Mon Sep 17 00:00:00 2001 From: Luis Angel Vicente Sanchez Date: Fri, 20 Feb 2015 00:09:42 +0000 Subject: [PATCH 9/9] Add MonoidK law check for Option instance. --- tests/src/test/scala/cats/tests/StdTests.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/src/test/scala/cats/tests/StdTests.scala b/tests/src/test/scala/cats/tests/StdTests.scala index fbeaab20d1..31eba33089 100644 --- a/tests/src/test/scala/cats/tests/StdTests.scala +++ b/tests/src/test/scala/cats/tests/StdTests.scala @@ -19,6 +19,7 @@ class StdTests extends FunSuite with Discipline { checkAll("Function0[Int]", MonadTests[Function0].monad[Int, Int, Int]) checkAll("Option[Int]", MonadFilterTests[Option].monadFilter[Int, Int, Int]) checkAll("Option[String]", MonadFilterTests[Option].monadFilter[String, Int, Int]) + checkAll("Option[Int]", MonoidKTests[Option].monoidK[Int]) checkAll("List[Int]", MonadFilterTests[List].monadFilter[Int, Int, Int]) checkAll("List[Int]", MonoidKTests[List].monoidK[Int]) checkAll("Stream[Int]", MonoidKTests[Stream].monoidK[Int])