Skip to content

Commit

Permalink
Get the relevant bits of algebra.laws ported over.
Browse files Browse the repository at this point in the history
There are a few things here:

 1. Many of algebra's interesting laws apply to rings/lattices.
 2. We introduce a (laws-only) dependency on catalysts-platform.
 3. Unlike Cats, we'll run the tests in kernel-laws' tests.
  • Loading branch information
non committed Apr 25, 2016
1 parent 38a3dc8 commit 157710f
Show file tree
Hide file tree
Showing 8 changed files with 456 additions and 5 deletions.
14 changes: 9 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,11 @@ lazy val scalacheckVersion = "1.12.5"

lazy val disciplineDependencies = Seq(
libraryDependencies += "org.scalacheck" %%% "scalacheck" % scalacheckVersion,
libraryDependencies += "org.typelevel" %%% "discipline" % "0.4"
)
libraryDependencies += "org.typelevel" %%% "discipline" % "0.4")

lazy val testingDependencies = Seq(
libraryDependencies += "org.typelevel" %%% "catalysts-platform" % "0.0.2",
libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.0-M7" % "test")

/**
* Remove 2.10 projects from doc generation, as the macros used in the projects
Expand Down Expand Up @@ -190,8 +193,11 @@ lazy val kernelLaws = crossProject.crossType(CrossType.Pure)
.settings(buildSettings: _*)
.settings(publishSettings: _*)
.settings(scoverageSettings: _*)
.settings(disciplineDependencies: _*)
.settings(testingDependencies: _*)
.jsSettings(commonJsSettings:_*)
.jvmSettings(commonJvmSettings:_*)
.dependsOn(kernel)

lazy val kernelLawsJVM = kernelLaws.jvm
lazy val kernelLawsJS = kernelLaws.js
Expand Down Expand Up @@ -228,9 +234,7 @@ lazy val tests = crossProject.crossType(CrossType.Pure)
.settings(catsSettings:_*)
.settings(disciplineDependencies:_*)
.settings(noPublishSettings:_*)
.settings(libraryDependencies ++= Seq(
"org.scalatest" %%% "scalatest" % "3.0.0-M7" % "test",
"org.typelevel" %%% "catalysts-platform" % "0.0.2" % "test"))
.settings(testingDependencies: _*)
.jsSettings(commonJsSettings:_*)
.jvmSettings(commonJvmSettings:_*)

Expand Down
27 changes: 27 additions & 0 deletions kernel-laws/src/main/scala/cats/kernel/laws/BaseLaws.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package cats.kernel.laws

import cats.kernel._

import org.typelevel.discipline.Laws

import org.scalacheck.{Arbitrary, Prop}

object BaseLaws {
def apply[A : Eq : Arbitrary] = new BaseLaws[A] {
def Equ = Eq[A]
def Arb = implicitly[Arbitrary[A]]
}
}

trait BaseLaws[A] extends Laws {

implicit def Equ: Eq[A]
implicit def Arb: Arbitrary[A]

class BaseRuleSet(
val name: String,
val parent: Option[RuleSet],
val bases: Seq[(String, Laws#RuleSet)],
val props: (String, Prop)*
) extends RuleSet with HasOneParent
}
12 changes: 12 additions & 0 deletions kernel-laws/src/main/scala/cats/kernel/laws/CheckSupport.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// package cats.kernel.laws
//
// /**
// * This object contains Arbitrary instances for types defined in
// * cats.kernel.std, as well as anything else we'd like to import to assist
// * in running ScalaCheck tests.
// *
// * (Since cats.kernel-std has no dependencies, its types can't define
// * Arbitrary instances in companions.)
// */
// object CheckSupport {
// }
142 changes: 142 additions & 0 deletions kernel-laws/src/main/scala/cats/kernel/laws/GroupLaws.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package cats.kernel.laws

import cats.kernel._

import org.typelevel.discipline.Laws

import org.scalacheck.{Arbitrary, Prop}
import org.scalacheck.Prop._

object GroupLaws {
def apply[A : Eq : Arbitrary] = new GroupLaws[A] {
def Equ = Eq[A]
def Arb = implicitly[Arbitrary[A]]
}
}

trait GroupLaws[A] extends Laws {

implicit def Equ: Eq[A]
implicit def Arb: Arbitrary[A]

// groups

def semigroup(implicit A: Semigroup[A]) = new GroupProperties(
name = "semigroup",
parents = Nil,
Rules.serializable(A),
Rules.associativity(A.combine),
Rules.repeat1("combineN")(A.combineN),
Rules.repeat2("combineN", "|+|")(A.combineN)(A.combine)
)

def band(implicit A: Band[A]) = new GroupProperties(
name = "band",
parents = List(semigroup),
Rules.idempotence(A.combine),
"isIdempotent" -> Semigroup.isIdempotent[A]
)

def commutativeSemigroup(implicit A: CommutativeSemigroup[A]) = new GroupProperties(
name = "commutative semigroup",
parents = List(semigroup),
Rules.commutative(A.combine)
)

def semilattice(implicit A: Semilattice[A]) = new GroupProperties(
name = "semilattice",
parents = List(band, commutativeSemigroup)
)

def monoid(implicit A: Monoid[A]) = new GroupProperties(
name = "monoid",
parents = List(semigroup),
Rules.leftIdentity(A.empty)(A.combine),
Rules.rightIdentity(A.empty)(A.combine),
Rules.repeat0("combineN", "id", A.empty)(A.combineN),
Rules.collect0("combineAll", "id", A.empty)(A.combineAll),
Rules.isId("isEmpty", A.empty)(A.isEmpty)
)

def commutativeMonoid(implicit A: CommutativeMonoid[A]) = new GroupProperties(
name = "commutative monoid",
parents = List(monoid, commutativeSemigroup)
)

def boundedSemilattice(implicit A: BoundedSemilattice[A]) = new GroupProperties(
name = "boundedSemilattice",
parents = List(commutativeMonoid, semilattice)
)

def group(implicit A: Group[A]) = new GroupProperties(
name = "group",
parents = List(monoid),
Rules.leftInverse(A.empty)(A.combine)(A.inverse),
Rules.rightInverse(A.empty)(A.combine)(A.inverse),
Rules.consistentInverse("remove")(A.remove)(A.combine)(A.inverse)
)

def commutativeGroup(implicit A: CommutativeGroup[A]) = new GroupProperties(
name = "commutative group",
parents = List(group, commutativeMonoid)
)

// // additive groups
//
// def additiveSemigroup(implicit A: AdditiveSemigroup[A]) = new AdditiveProperties(
// base = semigroup(A.additive),
// parents = Nil,
// Rules.serializable(A),
// Rules.repeat1("sumN")(A.sumN),
// Rules.repeat2("sumN", "+")(A.sumN)(A.plus)
// )
//
// def additiveCommutativeSemigroup(implicit A: AdditiveCommutativeSemigroup[A]) = new AdditiveProperties(
// base = commutativeSemigroup(A.additive),
// parents = List(additiveSemigroup)
// )
//
// def additiveMonoid(implicit A: AdditiveMonoid[A]) = new AdditiveProperties(
// base = monoid(A.additive),
// parents = List(additiveSemigroup),
// Rules.repeat0("sumN", "zero", A.zero)(A.sumN),
// Rules.collect0("sum", "zero", A.zero)(A.sum)
// )
//
// def additiveCommutativeMonoid(implicit A: AdditiveCommutativeMonoid[A]) = new AdditiveProperties(
// base = commutativeMonoid(A.additive),
// parents = List(additiveMonoid)
// )
//
// def additiveGroup(implicit A: AdditiveGroup[A]) = new AdditiveProperties(
// base = group(A.additive),
// parents = List(additiveMonoid),
// Rules.consistentInverse("subtract")(A.minus)(A.plus)(A.negate)
// )
//
// def additiveCommutativeGroup(implicit A: AdditiveCommutativeGroup[A]) = new AdditiveProperties(
// base = commutativeGroup(A.additive),
// parents = List(additiveGroup)
// )


// property classes

class GroupProperties(
val name: String,
val parents: Seq[GroupProperties],
val props: (String, Prop)*
) extends RuleSet {
val bases = Nil
}

// class AdditiveProperties(
// val base: GroupProperties,
// val parents: Seq[AdditiveProperties],
// val props: (String, Prop)*
// ) extends RuleSet {
// val name = base.name
// val bases = List("base" -> base)
// }

}
14 changes: 14 additions & 0 deletions kernel-laws/src/main/scala/cats/kernel/laws/IsSerializable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cats.kernel.laws

import catalysts.Platform

import scala.util.DynamicVariable

/**
* Object with a dynamic variable that allows users to skip the
* serialization tests for certain instances.
*/
private[laws] object IsSerializable {
val runTests = new DynamicVariable[Boolean](true)
def apply(): Boolean = (!Platform.isJs) && runTests.value
}
79 changes: 79 additions & 0 deletions kernel-laws/src/main/scala/cats/kernel/laws/OrderLaws.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package cats.kernel
package laws

import org.typelevel.discipline.Laws

import org.scalacheck.{Arbitrary, Prop}
import org.scalacheck.Prop._

import cats.kernel.std.boolean._

object OrderLaws {
def apply[A: Eq: Arbitrary] = new OrderLaws[A] {
def Equ = Eq[A]
def Arb = implicitly[Arbitrary[A]]
}
}

trait OrderLaws[A] extends Laws {

implicit def Equ: Eq[A]
implicit def Arb: Arbitrary[A]

def eqv = new OrderProperties(
name = "eq",
parent = None,
Rules.serializable(Equ),
"reflexitivity-eq" -> forAll { (x: A) =>
x ?== x
},
"symmetry-eq" -> forAll { (x: A, y: A) =>
Equ.eqv(x, y) ?== Equ.eqv(y, x)
},
"antisymmetry-eq" -> forAll { (x: A, y: A, f: A => A) =>
!Equ.eqv(x, y) ?|| Equ.eqv(f(x), f(y))
},

This comment has been minimized.

Copy link
@TomasMikula

TomasMikula Sep 6, 2016

Contributor

Should this be called the substitution property rather than antisymmetry? Citing from the Wikipedia page on Equality:

The substitution property states:

  • For any quantities a and b and any expression F(x), if a = b, then F(a) = F(b) (if both sides make sense, i.e. are well-formed).

/cc @non @johnynek @rklaehn

"transitivity-eq" -> forAll { (x: A, y: A, z: A) =>
!(Equ.eqv(x, y) && Equ.eqv(y, z)) ?|| Equ.eqv(x, z)
}
)

def partialOrder(implicit A: PartialOrder[A]) = new OrderProperties(
name = "partialOrder",
parent = Some(eqv),
Rules.serializable(A),
"reflexitivity" -> forAll { (x: A) =>
x ?<= x
},
"antisymmetry" -> forAll { (x: A, y: A) =>
!(A.lteqv(x, y) && A.lteqv(y, x)) ?|| A.eqv(x, y)
},
"transitivity" -> forAll { (x: A, y: A, z: A) =>
!(A.lteqv(x, y) && A.lteqv(y, z)) ?|| A.lteqv(x, z)
},
"gteqv" -> forAll { (x: A, y: A) =>
A.lteqv(x, y) ?== A.gteqv(y, x)
},
"lt" -> forAll { (x: A, y: A) =>
A.lt(x, y) ?== (A.lteqv(x, y) && A.neqv(x, y))
},
"gt" -> forAll { (x: A, y: A) =>
A.lt(x, y) ?== A.gt(y, x)
}
)

def order(implicit A: Order[A]) = new OrderProperties(
name = "order",
parent = Some(partialOrder),
"totality" -> forAll { (x: A, y: A) =>
A.lteqv(x, y) ?|| A.lteqv(y, x)
}
)

class OrderProperties(
name: String,
parent: Option[RuleSet],
props: (String, Prop)*
) extends DefaultRuleSet(name, parent, props: _*)

}
Loading

0 comments on commit 157710f

Please sign in to comment.