Using discipline to test Category laws
val dottyVersion = "0.20.0-RC1" | |
lazy val root = project | |
.in(file(".")) | |
.settings( | |
name := "discipline-example", | |
version := "0.1.0", | |
libraryDependencies ++= Seq( | |
"com.novocode" % "junit-interface" % "0.11" % "test", | |
("org.typelevel" % "cats-core_2.13" % "2.0.0").withDottyCompat(scalaVersion.value), | |
("org.typelevel" % "cats-laws_2.13" % "2.0.0").withDottyCompat(scalaVersion.value), | |
("org.scalacheck" % "scalacheck_2.13" % "1.14.2").withDottyCompat(scalaVersion.value), | |
"org.typelevel" % "discipline-core_2.13" % "1.0.1", | |
"com.github.alexarchambault" % "scalacheck-shapeless_1.14_2.13" % "1.2.3" | |
), | |
scalaVersion := dottyVersion | |
) |
// look at the accompanying sbt file for the required libraries | |
import cats.Eq | |
import org.scalacheck.{Arbitrary, Cogen, Prop} | |
import Prop._ | |
import org.typelevel.discipline.Laws | |
import discipline._ | |
trait Category[Hom[_, _]] | |
type ⤳[A, B] = Hom[A, B] | |
def id[A]: A ⤳ A | |
def [A, B, C] (g: B ⤳ C) ◦ (f: A ⤳ B): A ⤳ C | |
class CategoryLaws[⤳[_, _]](given C: Category[⤳]) | |
def associativity[A, B, C, D]( | |
f: A ⤳ B, | |
g: B ⤳ C, | |
h: C ⤳ D | |
) = | |
h ◦ (g ◦ f) <-> (h ◦ g) ◦ f | |
def identityRight[A, B](f: A ⤳ B) = f ◦ C.id[A] <-> f | |
def identityLeft[A, B](f: A ⤳ B) = (C.id[B] ◦ f) <-> f | |
type Scala = Function | |
given Scala: Category[Scala] | |
def id[A]: A => A = identity | |
def [A, B, C] (g: B => C) ◦ (f: A => B) = g compose f | |
// ----------------------------------------- | |
// This is how we use the laws defined above | |
// ----------------------------------------- | |
class CategoryTests[Hom[_, _]](given Category[Hom]) extends Laws { | |
def laws = CategoryLaws[Hom] | |
def category[A, B, C, D](given | |
arbAB: Arbitrary[Hom[A, B]], | |
arbBC: Arbitrary[Hom[B, C]], | |
arbCD: Arbitrary[Hom[C, D]], | |
eqAB: Eq[Hom[A, B]], | |
eqAD: Eq[Hom[A, D]] | |
): RuleSet = | |
new DefaultRuleSet( | |
name = "category", | |
parent = None, | |
"right identity" -> forAll(laws.identityRight[A, B] _), | |
"left identity" -> forAll(laws.identityLeft[A, B] _), | |
"associativity" -> forAll(laws.associativity[A, B, C, D] _) | |
) | |
} | |
object DisciplineExamples extends App { | |
import cats.implicits._ | |
import cats.laws.discipline.DeprecatedEqInstances.catsLawsEqForFn1 | |
CategoryTests[Scala].category[Int, Int, Int, Int].all.check() | |
} | |
// Example run: | |
// [info] Running categories.simple.DisciplineExamples | |
// + category.associativity: OK, passed 100 tests. | |
// + category.left identity: OK, passed 100 tests. | |
// + category.right identity: OK, passed 100 tests. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment