Skip to content

Commit

Permalink
Merge pull request #246 from fthomas/topic/strong-laws
Browse files Browse the repository at this point in the history
Add StrongLaws and StrongTests
  • Loading branch information
ceedubs committed Mar 6, 2015
2 parents 2adf0e5 + 24dc3c3 commit 2bf9a7e
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 4 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ lazy val core = project.dependsOn(macros)
sourceGenerators in Compile <+= (sourceManaged in Compile).map(Boilerplate.gen)
)

lazy val laws = project.dependsOn(macros, core, data)
lazy val laws = project.dependsOn(macros, core, data, std)
.settings(moduleName := "cats-laws")
.settings(catsSettings: _*)
.settings(
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/functor/Strong.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package cats
package functor

/**
* Must obey the laws defined in [[laws.StrongLaws]].
*/
trait Strong[F[_, _]] extends Profunctor[F] {
def first[A, B, C](fa: F[A, B]): F[(A, C), (B, C)]
def second[A, B, C](fa: F[A, B]): F[(C, A), (C, B)]
Expand Down
24 changes: 24 additions & 0 deletions laws/src/main/scala/cats/laws/StrongLaws.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cats.laws

import cats.functor.Strong
import cats.syntax.profunctor._
import cats.syntax.strong._
import cats.std.function._

/**
* Laws that must be obeyed by any [[cats.functor.Strong]].
*/
trait StrongLaws[F[_, _]] extends ProfunctorLaws[F] {
implicit override def F: Strong[F]

def strongFirstDistributivity[A0, A1, B1, B2, C](fab: F[A1, B1], f: A0 => A1, g: B1 => B2): IsEq[F[(A0, C), (B2, C)]] =
fab.dimap(f)(g).first[C] <-> fab.first[C].dimap(f.first[C])(g.first[C])

def strongSecondDistributivity[A0, A1, B1, B2, C](fab: F[A1, B1], f: A0 => A1, g: B1 => B2): IsEq[F[(C, A0), (C, B2)]] =
fab.dimap(f)(g).second[C] <-> fab.second[C].dimap(f.second[C])(g.second[C])
}

object StrongLaws {
def apply[F[_, _]](implicit ev: Strong[F]): StrongLaws[F] =
new StrongLaws[F] { def F = ev }
}
2 changes: 1 addition & 1 deletion laws/src/main/scala/cats/laws/discipline/SplitTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ trait SplitTests[F[_, _]] extends ComposeTests[F] {
ArbFDE: Arbitrary[F[D, E]],
ArbFEG: Arbitrary[F[E, G]],
EqFAD: Eq[F[A, D]],
EqFADCG: Eq[F[(A, D),(C, G)]]
EqFADCG: Eq[F[(A, D), (C, G)]]
): RuleSet =
new RuleSet {
def name = "split"
Expand Down
35 changes: 35 additions & 0 deletions laws/src/main/scala/cats/laws/discipline/StrongTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package cats.laws
package discipline

import cats.Eq
import cats.functor.Strong
import org.scalacheck.Arbitrary
import org.scalacheck.Prop._

trait StrongTests[F[_, _]] extends ProfunctorTests[F] {
def laws: StrongLaws[F]

def strong[A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, G: Arbitrary](implicit
ArbFAB: Arbitrary[F[A, B]],
ArbFBC: Arbitrary[F[B, C]],
ArbFCD: Arbitrary[F[C, D]],
EqFAB: Eq[F[A, B]],
EqFAG: Eq[F[A, G]],
EqFAEDE: Eq[F[(A, E), (D, E)]],
EqFEAED: Eq[F[(E, A), (E, D)]]
): RuleSet =
new RuleSet {
def name = "strong"
def bases = Nil
def parents = Seq(profunctor[A, B, C, D, E, G])
def props = Seq(
"strong first distributivity" -> forAll(laws.strongFirstDistributivity[A, B, C, D, E] _),
"strong second distributivity" -> forAll(laws.strongSecondDistributivity[A, B, C, D, E] _)
)
}
}

object StrongTests {
def apply[F[_, _]: Strong]: StrongTests[F] =
new StrongTests[F] { def laws = StrongLaws[F] }
}
2 changes: 1 addition & 1 deletion tests/src/test/scala/cats/tests/FunctionTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ class FunctionTests extends CatsSuite {
checkAll("Function0[Int]", MonadTests[Function0].monad[Int, Int, Int])
checkAll("Function1[Int, Int]", CategoryTests[Function1].category[Int, Int, Int, Int])
checkAll("Function1[Int, Int]", SplitTests[Function1].split[Int, Int, Int, Int, Int, Int])
checkAll("Function1[Int, Int]", ProfunctorTests[Function1].profunctor[Int, Int, Int, Int, Int, Int])
checkAll("Function1[Int, Int]", StrongTests[Function1].strong[Int, Int, Int, Int, Int, Int])
}
2 changes: 1 addition & 1 deletion tests/src/test/scala/cats/tests/KleisliTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ class KleisliTests extends CatsSuite {
Eq.by[Kleisli[F, A, B], A => F[B]](_.run)

checkAll("Kleisli[Option, Int, Int]", ApplicativeTests[Kleisli[Option, Int, ?]].applicative[Int, Int, Int])
checkAll("Kleisli[Option, Int, Int]", ProfunctorTests[Kleisli[Option, ?, ?]].profunctor[Int, Int, Int, Int, Int, Int])
checkAll("Kleisli[Option, Int, Int]", StrongTests[Kleisli[Option, ?, ?]].strong[Int, Int, Int, Int, Int, Int])
}

0 comments on commit 2bf9a7e

Please sign in to comment.