Skip to content

Instantly share code, notes, and snippets.

@jpablo
Last active November 16, 2020 14:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jpablo/d49041fd0d470a043940a4ba3a0a8e73 to your computer and use it in GitHub Desktop.
Save jpablo/d49041fd0d470a043940a4ba3a0a8e73 to your computer and use it in GitHub Desktop.
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