diff --git a/core/src/main/scala/cats/evidence/Is.scala b/core/src/main/scala/cats/evidence/Is.scala index d9b2a9c1f1..632f96a3e2 100644 --- a/core/src/main/scala/cats/evidence/Is.scala +++ b/core/src/main/scala/cats/evidence/Is.scala @@ -1,6 +1,7 @@ package cats.evidence import cats.Id +import cats.arrow._ /** * A value of `A Is B` is proof that the types `A` and `B` are the same. More @@ -65,7 +66,19 @@ abstract class Is[A, B] extends Serializable { substitute[A =:= ?](implicitly[A =:= A]) } -object Is { +sealed abstract class IsInstances { + import Is._ + + /** + * The category instance on Leibniz categories. + */ + implicit val leibniz: Category[Is] = new Category[Is] { + def id[A]: A Is A = refl[A] + def compose[A, B, C](bc: B Is C, ab: A Is B): A Is C = bc.compose(ab) + } +} + +object Is extends IsInstances { /** * In truth, "all values of `A Is B` are `refl`". `reflAny` is that diff --git a/tests/src/test/scala/cats/tests/IsSuite.scala b/tests/src/test/scala/cats/tests/IsSuite.scala index 6d2ef5050a..26f5bb827e 100644 --- a/tests/src/test/scala/cats/tests/IsSuite.scala +++ b/tests/src/test/scala/cats/tests/IsSuite.scala @@ -1,9 +1,25 @@ package cats package tests +import cats.arrow._ +import cats.kernel.laws.discipline.SerializableTests +import cats.laws.discipline.CategoryTests +import org.scalacheck.{Arbitrary, Gen} + class IsSuite extends CatsSuite { import evidence._ + implicit def arbIs[A, B](implicit ev: A Is B): Arbitrary[A Is B] = Arbitrary(Gen.const(ev)) + implicit def eqIs[A, B]: Eq[A Is B] = Eq.fromUniversalEquals + + trait Top { + def foo: String = this.getClass.getName + } + case class Bottom() extends Top + + checkAll("Is[Bottom, Bottom]", CategoryTests[Is].category[Bottom, Bottom, Bottom, Bottom]) + checkAll("Category[Is]", SerializableTests.serializable(Category[Is])) + test("syntax") { trait Bar