Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Contravariant instance for Eq and test Show instance. #1211

Merged
merged 3 commits into from
Jul 19, 2016
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
5 changes: 5 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,11 @@ object arbitrary extends ArbitraryInstances0 {
implicit def catsLawsArbitraryForFn0[A: Arbitrary]: Arbitrary[() => A] =
Arbitrary(getArbitrary[A].map(() => _))

implicit def catsLawsArbitraryForEq[A: Arbitrary]: Arbitrary[Eq[A]] =
Arbitrary(Gen.oneOf(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems a bit weak. You could do something like a.hashCode == b.hashCode. I think that is a lawful Eq that has a better chance of showing some bad behavior (although, if that is the case, Eq.on is broken anyway, and the test should be there).

new Eq[A] { def eqv(x: A, y: A) = true },
new Eq[A] { def eqv(x: A, y: A) = false }))

implicit def catsLawsArbitraryForPartialOrder[A: Arbitrary]: Arbitrary[PartialOrder[A]] =
Arbitrary(Gen.oneOf(
PartialOrder.from[A]((_: A, _: A) => Double.NaN),
Expand Down
16 changes: 16 additions & 0 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)], booleanEq: Eq[Boolean]): Eq[Eq[A]] = new Eq[Eq[A]] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really think we need the Eq[Boolean] here. I think we assume that primitives are okay to compare directly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, I adapted catsLawsEqForOrder where an Eq[Int] is used.

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) => booleanEq.eqv(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 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]))
}