Skip to content

Commit

Permalink
Fix InvariantMonoidal tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ceedubs committed Feb 17, 2019
1 parent 0151836 commit c468414
Show file tree
Hide file tree
Showing 9 changed files with 553 additions and 207 deletions.
32 changes: 17 additions & 15 deletions free/src/test/scala/cats/free/FreeInvariantMonoidalSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package tests

import cats.arrow.FunctionK
import cats.free.FreeInvariantMonoidal
import cats.laws.discipline.{InvariantMonoidalTests, SerializableTests}
import cats.laws.discipline.{InvariantMonoidalTests, MiniInt, SerializableTests}
import cats.laws.discipline.arbitrary._
import cats.laws.discipline.SemigroupalTests.Isomorphisms
import org.scalacheck.{Arbitrary, Gen}
import cats.tests.CsvCodecInvariantMonoidalSuite._
import cats.tests.BinCodecInvariantMonoidalSuite._

class FreeInvariantMonoidalSuite extends CatsSuite {
implicit def freeInvariantMonoidalArbitrary[F[_], A](implicit F: Arbitrary[F[A]],
Expand All @@ -25,24 +26,25 @@ class FreeInvariantMonoidalSuite extends CatsSuite {
}
}

implicit val isoFreeCsvCodec = Isomorphisms.invariant[FreeInvariantMonoidal[CsvCodec, ?]]
implicit val isoFreeBinCodec = Isomorphisms.invariant[FreeInvariantMonoidal[BinCodec, ?]]

checkAll("FreeInvariantMonoidal[CsvCodec, ?]",
InvariantMonoidalTests[FreeInvariantMonoidal[CsvCodec, ?]].invariantMonoidal[Int, Int, Int])
checkAll("InvariantMonoidal[FreeInvariantMonoidal[CsvCodec, ?]]",
SerializableTests.serializable(InvariantMonoidal[FreeInvariantMonoidal[CsvCodec, ?]]))
checkAll("FreeInvariantMonoidal[BinCodec, ?]",
InvariantMonoidalTests[FreeInvariantMonoidal[BinCodec, ?]].invariantMonoidal[MiniInt, Boolean, Boolean])
checkAll("InvariantMonoidal[FreeInvariantMonoidal[BinCodec, ?]]",
SerializableTests.serializable(InvariantMonoidal[FreeInvariantMonoidal[BinCodec, ?]]))

test("FreeInvariantMonoidal#fold") {
val n = 2
val i1 = numericSystemCodec(8)
val i2 = InvariantMonoidal[CsvCodec].point(n)
val iExpr = i1.product(i2.imap(_ * 2)(_ / 2))
forAll { i1: BinCodec[MiniInt] =>
val n = MiniInt.unsafeFromInt(2)
val i2 = InvariantMonoidal[BinCodec].point(n)
val iExpr = i1.product(i2.imap(_ * n)(_ / n))

val f1 = FreeInvariantMonoidal.lift[CsvCodec, Int](i1)
val f2 = FreeInvariantMonoidal.pure[CsvCodec, Int](n)
val fExpr = f1.product(f2.imap(_ * 2)(_ / 2))
val f1 = FreeInvariantMonoidal.lift[BinCodec, MiniInt](i1)
val f2 = FreeInvariantMonoidal.pure[BinCodec, MiniInt](n)
val fExpr = f1.product(f2.imap(_ * n)(_ / n))

fExpr.fold should ===(iExpr)
fExpr.fold should ===(iExpr)
}
}

implicit val idIsInvariantMonoidal: InvariantMonoidal[Id] = new InvariantMonoidal[Id] {
Expand Down
2 changes: 1 addition & 1 deletion laws/src/main/scala/cats/laws/discipline/Arbitrary.scala
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ object arbitrary extends ArbitraryInstances0 {
Cogen[List[A]].contramap(_.toList)

implicit val catsLawsCogenForMiniInt: Cogen[MiniInt] =
Cogen[Int].contramap(_.asInt)
Cogen[Int].contramap(_.toInt)

implicit val catsLawsArbitraryForMiniInt: Arbitrary[MiniInt] =
Arbitrary(Gen.oneOf(MiniInt.allValues))
Expand Down
130 changes: 101 additions & 29 deletions laws/src/main/scala/cats/laws/discipline/Eq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,57 +15,65 @@ import cats.kernel._
import cats.syntax.eq._
import org.scalacheck.Arbitrary

object eq extends DisciplineEqInstances {
object eq extends DisciplineBinCompatEqInstances {

implicit def catsLawsEqForFn1Exhaustive[A, B](implicit A: ExhaustiveCheck[A], B: Eq[B]): Eq[A => B] =
Eq.instance((f, g) => A.allValues.forall(a => B.eqv(f(a), g(a))))

implicit def catsLawsEqForFn2Exhaustive[A, B, C](implicit A: ExhaustiveCheck[A],
B: ExhaustiveCheck[B],
C: Eq[C]): Eq[(A, B) => C] =
Eq.by((_: (A, B) => C).tupled)(catsLawsEqForFn1Exhaustive)
implicit def catsLawsEqForFn2BinCompat[A, B, C](implicit ev: Eq[((A, B)) => C]): Eq[(A, B) => C] =
Eq.by((_: (A, B) => C).tupled)

implicit def catsLawsEqForAndThenExhaustive[A, B](implicit A: ExhaustiveCheck[A], B: Eq[B]): Eq[AndThen[A, B]] =
Eq.instance(catsLawsEqForFn1Exhaustive[A, B].eqv(_, _))
implicit def catsLawsEqForAndThenBinCompat[A, B](implicit eqAB: Eq[A => B]): Eq[AndThen[A, B]] =
Eq.by[AndThen[A, B], A => B](identity)

implicit def catsLawsEqForShowExhaustive[A: ExhaustiveCheck]: Eq[Show[A]] =
implicit def catsLawsEqForShowBinCompat[A](implicit ev: Eq[A => String]): Eq[Show[A]] =
Eq.by[Show[A], A => String](showA => a => showA.show(a))

implicit def catsLawsEqForEqExhaustive[A: ExhaustiveCheck]: Eq[Eq[A]] =
implicit def catsLawsEqForEqBinCompt[A](implicit ev: Eq[(A, A) => Boolean]): Eq[Eq[A]] =
Eq.by[Eq[A], (A, A) => Boolean](e => (a1, a2) => e.eqv(a1, a2))

implicit def catsLawsEqForEquivExhaustive[A: ExhaustiveCheck]: Eq[Equiv[A]] =
implicit def catsLawsEqForEquivBinCompat[A](implicit ev: Eq[(A, A) => Boolean]): Eq[Equiv[A]] =
Eq.by[Equiv[A], (A, A) => Boolean](e => (a1, a2) => e.equiv(a1, a2))

implicit def catsLawsEqForPartialOrderExhaustive[A: ExhaustiveCheck]: Eq[PartialOrder[A]] = {
import cats.instances.option._

implicit def catsLawsEqForPartialOrderBinCompat[A](implicit ev: Eq[(A, A) => Option[Int]]): Eq[PartialOrder[A]] =
Eq.by[PartialOrder[A], (A, A) => Option[Int]](o => (a1, a2) => o.tryCompare(a1, a2))
}

implicit def catsLawsEqForPartialOrderingExhaustive[A: ExhaustiveCheck]: Eq[PartialOrdering[A]] = {
import cats.instances.option._

Eq.by[PartialOrdering[A], (A, A) => Option[Int]](
(o: PartialOrdering[A]) => (a1, a2) => o.tryCompare(a1, a2)
)
}
implicit def catsLawsEqForPartialOrderingBinCompat[A](
implicit ev: Eq[(A, A) => Option[Int]]
): Eq[PartialOrdering[A]] =
Eq.by[PartialOrdering[A], (A, A) => Option[Int]]((o: PartialOrdering[A]) => (a1, a2) => o.tryCompare(a1, a2))

implicit def catsLawsEqForOrderExhaustive[A: ExhaustiveCheck]: Eq[Order[A]] =
implicit def catsLawsEqForOrderBinCompat[A](implicit ev: Eq[(A, A) => Int]): Eq[Order[A]] =
Eq.by[Order[A], (A, A) => Int](o => (a1, a2) => o.compare(a1, a2))

implicit def catsLawsEqForOrderingExhaustive[A: ExhaustiveCheck]: Eq[Ordering[A]] =
implicit def catsLawsEqForOrderingBinCompat[A](implicit ev: Eq[(A, A) => Int]): Eq[Ordering[A]] =
Eq.by[Ordering[A], (A, A) => Int](o => (a1, a2) => o.compare(a1, a2))

implicit def catsLawsEqForHashExhaustive[A: ExhaustiveCheck]: Eq[Hash[A]] =
implicit def catsLawsEqForHashBinCompat[A](implicit ev: Eq[A => Int]): Eq[Hash[A]] =
Eq.by[Hash[A], A => Int](h => a => h.hash(a))

implicit def catsLawsEqForSemigroupExhaustive[A: ExhaustiveCheck: Eq]: Eq[Semigroup[A]] =
implicit def catsLawsEqForSemigroupBinCompat[A](implicit ev: Eq[(A, A) => A]): Eq[Semigroup[A]] =
Eq.by[Semigroup[A], (A, A) => A](s => (a1, a2) => s.combine(a1, a2))

implicit def catsLawsEqForCommutativeSemigroupExhaustive[A: ExhaustiveCheck: Eq]: Eq[CommutativeSemigroup[A]] =
Eq.by[CommutativeSemigroup[A], (A, A) => (A, Boolean)](
s => (x, y) => (s.combine(x, y), s.combine(x, y) === s.combine(y, x))
implicit def catsLawsEqForCommutativeSemigroupBinCompat[A](implicit eqA: Eq[A],
ev: Eq[(A, A) => (A, A)]): Eq[CommutativeSemigroup[A]] =
Eq.by[CommutativeSemigroup[A], (A, A) => (A, A)](s => (x, y) => (s.combine(x, y), s.combine(y, x)))

implicit def catsLawsEqForBandBinCompat[A](implicit ev: Eq[(A, A) => (A, A)]): Eq[Band[A]] =
Eq.by[Band[A], (A, A) => (A, A)](
f => (x, y) => (f.combine(x, y), f.combine(f.combine(x, y), y))
)

implicit def catsLawsEqForGroupBinCompat[A](implicit ev1: Eq[(A, A) => (A, Boolean)], eqA: Eq[A]): Eq[Group[A]] =
Eq.by[Group[A], (A, A) => (A, Boolean)](
f =>
(x, y) =>
(
f.combine(x, y),
f.combine(f.inverse(x), x) === f.empty && f.combine(x, f.inverse(x)) === f.empty &&
f.combine(f.inverse(y), y) === f.empty && f.combine(y, f.inverse(y)) === f.empty &&
f.inverse(f.empty) == f.empty
)
)

implicit def catsLawsEqForMonoid[A](implicit eqSA: Eq[Semigroup[A]], eqA: Eq[A]): Eq[Monoid[A]] = new Eq[Monoid[A]] {
Expand Down Expand Up @@ -103,12 +111,16 @@ object eq extends DisciplineEqInstances {
* they haven't been removed, but they should be considered to be deprecated, and we put them in a
* lower implicit scope priority.
*/
private[discipline] trait DisciplineEqInstances {
private[discipline] trait DisciplineBinCompatEqInstances {

/**
* Create an approximation of Eq[A => B] by generating random values for A
* and comparing the application of the two functions.
*/
@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForFn1Exhaustive instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForFn1[A, B](implicit A: Arbitrary[A], B: Eq[B]): Eq[A => B] = new Eq[A => B] {
val sampleCnt: Int = if (Platform.isJvm) 50 else 30

Expand All @@ -125,14 +137,26 @@ private[discipline] trait DisciplineEqInstances {
* Create an approximation of Eq[(A, B) => C] by generating random values for A and B
* and comparing the application of the two functions.
*/
@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForFn2BinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForFn2[A, B, C](implicit A: Arbitrary[A], B: Arbitrary[B], C: Eq[C]): Eq[(A, B) => C] =
Eq.by((_: (A, B) => C).tupled)(catsLawsEqForFn1)

/** `Eq[AndThen]` instance, built by piggybacking on [[catsLawsEqForFn1]]. */
@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForAndThenBinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForAndThen[A, B](implicit A: Arbitrary[A], B: Eq[B]): Eq[AndThen[A, B]] =
Eq.instance(catsLawsEqForFn1[A, B].eqv(_, _))

/** Create an approximation of Eq[Show[A]] by using catsLawsEqForFn1[A, String] */
@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForShowBinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForShow[A: Arbitrary]: Eq[Show[A]] =
Eq.by[Show[A], A => String] { showInstance => (a: A) =>
showInstance.show(a)
Expand All @@ -142,6 +166,10 @@ private[discipline] trait DisciplineEqInstances {
* Create an approximate Eq instance for some type A, by comparing
* the behavior of `f(x, b)` and `f(y, b)` across many `b` samples.
*/
@deprecated(
"This method is problematic and will most likely be removed in a future version of Cats. You may want to use an approach based on catsLawsEqForFn1Exhaustive instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
def sampledEq[A, B: Arbitrary, C: Eq](samples: Int)(f: (A, B) => C): Eq[A] =
new Eq[A] {
val gen = Arbitrary.arbitrary[B]
Expand All @@ -155,30 +183,58 @@ private[discipline] trait DisciplineEqInstances {
}
}

@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForEqBinCompt instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForEq[A](implicit arbA: Arbitrary[(A, A)]): Eq[Eq[A]] =
sampledEq[Eq[A], (A, A), Boolean](100) { case (e, (l, r)) => e.eqv(l, r) }

@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForEquivBinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForEquiv[A](implicit arbA: Arbitrary[(A, A)]): Eq[Equiv[A]] =
sampledEq[Equiv[A], (A, A), Boolean](100) { case (e, (l, r)) => e.equiv(l, r) }

@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForPartialOrderBinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForPartialOrder[A](implicit arbA: Arbitrary[(A, A)],
optIntEq: Eq[Option[Int]]): Eq[PartialOrder[A]] =
sampledEq[PartialOrder[A], (A, A), Option[Int]](100) { case (p, (l, r)) => p.tryCompare(l, r) }

@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForPartialOrderingBinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForPartialOrdering[A](implicit arbA: Arbitrary[(A, A)],
optIntEq: Eq[Option[Int]]): Eq[PartialOrdering[A]] =
sampledEq[PartialOrdering[A], (A, A), Option[Int]](100) { case (p, (l, r)) => p.tryCompare(l, r) }

@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForOrderBinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForOrder[A](implicit arbA: Arbitrary[(A, A)]): Eq[Order[A]] =
sampledEq[Order[A], (A, A), Int](100) { case (p, (l, r)) => p.compare(l, r) }

@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForOrderingBinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForOrdering[A](implicit arbA: Arbitrary[(A, A)]): Eq[Ordering[A]] =
sampledEq[Ordering[A], (A, A), Int](100) { case (p, (l, r)) => p.compare(l, r) }

/**
* Creates an approximation of Eq[Hash[A]] by generating 100 values for A
* and comparing the application of the two hash functions.
*/
@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForHashBinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForHash[A](implicit arbA: Arbitrary[A]): Eq[Hash[A]] = new Eq[Hash[A]] {
def eqv(f: Hash[A], g: Hash[A]): Boolean = {
val samples = List.fill(100)(arbA.arbitrary.sample).collect {
Expand All @@ -195,11 +251,19 @@ private[discipline] trait DisciplineEqInstances {
* Create an approximation of Eq[Semigroup[A]] by generating values for A
* and comparing the application of the two combine functions.
*/
@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForSemigroupBinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForSemigroup[A](implicit arbAA: Arbitrary[(A, A)], eqA: Eq[A]): Eq[Semigroup[A]] = {
val instance: Eq[((A, A)) => A] = catsLawsEqForFn1[(A, A), A]
Eq.by[Semigroup[A], ((A, A)) => A](f => Function.tupled((x, y) => f.combine(x, y)))(instance)
}

@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForCommutativeSemigroupBinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForCommutativeSemigroup[A](implicit arbAA: Arbitrary[(A, A)],
eqA: Eq[A]): Eq[CommutativeSemigroup[A]] = {
implicit val eqABool: Eq[(A, Boolean)] = Eq.instance {
Expand All @@ -211,13 +275,21 @@ private[discipline] trait DisciplineEqInstances {
)(catsLawsEqForFn1[(A, A), (A, Boolean)])
}

@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForBandBinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForBand[A](implicit arbAA: Arbitrary[(A, A)],
eqSA: Eq[Semigroup[A]],
eqA: Eq[A]): Eq[Band[A]] =
Eq.by[Band[A], ((A, A)) => Boolean](
f => Function.tupled((x, y) => f.combine(x, y) === f.combine(f.combine(x, y), y))
)(catsLawsEqForFn1[(A, A), Boolean])

@deprecated(
"This instance is problematic and will most likely be removed in a future version of Cats. Use catsLawsEqForGroupBinCompat instead. See https://github.com/typelevel/cats/pull/2577 for more information.",
"1.7"
)
implicit def catsLawsEqForGroup[A](implicit arbAA: Arbitrary[(A, A)],
eqMA: Eq[Monoid[A]],
eqA: Eq[A]): Eq[Group[A]] = {
Expand Down
11 changes: 11 additions & 0 deletions laws/src/main/scala/cats/laws/discipline/ExhaustiveCheck.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ object ExhaustiveCheck {
implicit val catsLawsExhaustiveCheckForBoolean: ExhaustiveCheck[Boolean] =
instance(Stream(false, true))

implicit val catsLawsExhaustiveCheckForSetBoolean: ExhaustiveCheck[Set[Boolean]] =
forSet[Boolean]

/**
* Warning: the domain of (A, B) is the cross-product of the domain of `A` and the domain of `B`.
*/
Expand Down Expand Up @@ -48,4 +51,12 @@ object ExhaustiveCheck {

implicit def catsLawsExhaustiveCheckForOption[A](implicit A: ExhaustiveCheck[A]): ExhaustiveCheck[Option[A]] =
instance(Stream.cons(None, A.allValues.map(Some(_))))

/**
* Creates an `ExhaustiveCheck[Set[A]]` given an `ExhaustiveCheck[A]` by computing the powerset of
* values. Note that if there are `n` elements in the domain of `A` there will be `2^n` elements
* in the domain of `Set[A]`, so use this only on small domains.
*/
def forSet[A](implicit A: ExhaustiveCheck[A]): ExhaustiveCheck[Set[A]] =
instance(A.allValues.toSet.subsets.toStream)
}
Loading

0 comments on commit c468414

Please sign in to comment.