Skip to content

Commit

Permalink
Merge pull request #1211 from peterneyens/contravariant-instances
Browse files Browse the repository at this point in the history
Add Contravariant instance for Eq and test Show instance.
  • Loading branch information
ceedubs authored Jul 19, 2016
2 parents b684260 + e7957d2 commit 585959e
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 3 deletions.
17 changes: 16 additions & 1 deletion core/src/main/scala/cats/functor/Contravariant.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,22 @@ import simulacrum.typeclass
}
}

object Contravariant {
object Contravariant extends KernelContravariantInstances

/**
* Convariant instances for types that are housed in cats.kernel and therefore
* can't have instances for this type class in their companion objects.
*/
private[functor] sealed trait KernelContravariantInstances {
implicit val catsFunctorContravariantForEq: Contravariant[Eq] =
new Contravariant[Eq] {
/** Derive a `Eq` for `B` given a `Eq[A]` and a function `B => A`.
*
* Note: resulting instances are law-abiding only when the functions used are injective (represent a one-to-one mapping)
*/
def contramap[A, B](fa: Eq[A])(f: B => A): Eq[B] = fa.on(f)
}

implicit val catsFunctorContravariantForPartialOrder: Contravariant[PartialOrder] =
new Contravariant[PartialOrder] {
/** Derive a `PartialOrder` for `B` given a `PartialOrder[A]` and a function `B => A`.
Expand Down
3 changes: 3 additions & 0 deletions laws/src/main/scala/cats/laws/discipline/Arbitrary.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ object arbitrary extends ArbitraryInstances0 {
implicit def catsLawsArbitraryForFn0[A: Arbitrary]: Arbitrary[() => A] =
Arbitrary(getArbitrary[A].map(() => _))

implicit def catsLawsArbitraryForEq[A: Arbitrary]: Arbitrary[Eq[A]] =
Arbitrary(new Eq[A] { def eqv(x: A, y: A) = x.hashCode == y.hashCode })

implicit def catsLawsArbitraryForPartialOrder[A: Arbitrary]: Arbitrary[PartialOrder[A]] =
Arbitrary(Gen.oneOf(
PartialOrder.from[A]((_: A, _: A) => Double.NaN),
Expand Down
20 changes: 18 additions & 2 deletions laws/src/main/scala/cats/laws/discipline/Eq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ object eq {
}
}

/**
* Create an approximation of Eq[Eq[A]] by generating 100 values for A
* and comparing the application of the two eqv functions
*/
implicit def catsLawsEqForEq[A](implicit arbA: Arbitrary[(A, A)]): Eq[Eq[A]] = new Eq[Eq[A]] {
def eqv(f: Eq[A], g: Eq[A]): Boolean = {
val samples = List.fill(100)(arbA.arbitrary.sample).collect {
case Some(a) => a
case None => sys.error("Could not generate arbitrary values to compare two Eq[A]")
}
samples.forall {
case (l, r) => f.eqv(l, r) == g.eqv(l, r)
}
}
}

/**
* Create an approximation of Eq[PartialOrder[A]] by generating 100 values for A
* and comparing the application of the two compare functions
Expand All @@ -51,14 +67,14 @@ object eq {
* Create an approximation of Eq[Order[A]] by generating 100 values for A
* and comparing the application of the two compare functions
*/
implicit def catsLawsEqForOrder[A](implicit arbA: Arbitrary[(A, A)], intEq: Eq[Int]): Eq[Order[A]] = new Eq[Order[A]] {
implicit def catsLawsEqForOrder[A](implicit arbA: Arbitrary[(A, A)]): Eq[Order[A]] = new Eq[Order[A]] {
def eqv(f: Order[A], g: Order[A]): Boolean = {
val samples = List.fill(100)(arbA.arbitrary.sample).collect {
case Some(a) => a
case None => sys.error("Could not generate arbitrary values to compare two Order[A]")
}
samples.forall {
case (l, r) => intEq.eqv(f.compare(l, r), g.compare(l, r))
case (l, r) => f.compare(l, r) == g.compare(l, r)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import cats.laws.discipline.{ContravariantTests, SerializableTests}
import cats.laws.discipline.eq._

class KernelContravariantTests extends CatsSuite {
checkAll("Contravariant[Eq]", ContravariantTests[Eq].contravariant[Int, Int, Int])
checkAll("Contravariant[Eq]", SerializableTests.serializable(Contravariant[Eq]))

checkAll("Contravariant[PartialOrder]", ContravariantTests[PartialOrder].contravariant[Int, Int, Int])
checkAll("Contravariant[PartialOrder]", SerializableTests.serializable(Contravariant[PartialOrder]))

Expand Down
12 changes: 12 additions & 0 deletions tests/src/test/scala/cats/tests/ShowTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cats
package tests

import cats.functor.Contravariant
import cats.laws.discipline.arbitrary._
import cats.laws.discipline.{ContravariantTests, SerializableTests}
import cats.laws.discipline.eq._

class ShowTests extends CatsSuite {
checkAll("Contravariant[Show]", ContravariantTests[Show].contravariant[Int, Int, Int])
checkAll("Contravariant[Show]", SerializableTests.serializable(Contravariant[Show]))
}

0 comments on commit 585959e

Please sign in to comment.