Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SplitLaws, SplitTests, and SplitSyntax #232

Merged
merged 1 commit into from
Feb 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/src/main/scala/cats/syntax/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ trait AllSyntax
with ProfunctorSyntax
with SemigroupSyntax
with ShowSyntax
with SplitSyntax
with StrongSyntax
1 change: 1 addition & 0 deletions core/src/main/scala/cats/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ package object syntax {
object profunctor extends ProfunctorSyntax
object semigroup extends SemigroupSyntax
object show extends ShowSyntax
object split extends SplitSyntax
object strong extends StrongSyntax
}
14 changes: 14 additions & 0 deletions core/src/main/scala/cats/syntax/split.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cats
package syntax

import cats.arrow.Split

trait SplitSyntax {
// TODO: use simulacrum instances eventually
implicit def splitSyntax[F[_, _]: Split, A, B](fab: F[A, B]): SplitOps[F, A, B] =
new SplitOps[F, A, B](fab)
}

class SplitOps[F[_, _], A, B](fab: F[A, B])(implicit F: Split[F]) {
def split[C, D](fcd: F[C, D]): F[(A, C), (B, D)] = F.split(fab, fcd)
}
21 changes: 21 additions & 0 deletions laws/src/main/scala/cats/laws/SplitLaws.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cats.laws

import cats.arrow.Split
import cats.syntax.compose._
import cats.syntax.split._

/**
* Laws that must be obeyed by any [[cats.arrow.Split]].
*/
trait SplitLaws[F[_, _]] extends ComposeLaws[F] {
implicit override def F: Split[F]

def splitInterchange[A1, A2, A3, B1, B2, B3](f1: F[A1, A2], f2: F[A2, A3],
g1: F[B1, B2], g2: F[B2, B3]): IsEq[F[(A1, B1), (A3, B3)]] =
((f1 split g1) andThen (f2 split g2)) <-> ((f1 andThen f2) split (g1 andThen g2))
}

object SplitLaws {
def apply[F[_, _]](implicit ev: Split[F]): SplitLaws[F] =
new SplitLaws[F] { def F = ev }
}
7 changes: 7 additions & 0 deletions laws/src/main/scala/cats/laws/discipline/Eq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,11 @@ object eq {
samples.forall(s => B.eqv(f(s), g(s)) )
}
}

implicit def tuple2Eq[A, B](implicit A: Eq[A], B: Eq[B]): Eq[(A, B)] =
new Eq[(A, B)] {
def eqv(x: (A, B), y: (A, B)): Boolean =
A.eqv(x._1, y._1) && B.eqv(x._2, y._2)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be defined in the std module instead of in laws?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see the issue is that laws currently doesn't depend on std does it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That instance is required in tests but defined in laws because the Eq[A => B] instance was already there. I think it should ultimately come from algebra.std. Guess I need to open a PR there.

I'd like to not hold up this PR by waiting until this instance appears in algebra.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it's smart to define the instances we need here, and then remove them when algebra adds them.

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

import cats.Eq
import cats.arrow.Split
import org.scalacheck.Arbitrary
import org.scalacheck.Prop._

trait SplitTests[F[_, _]] extends ComposeTests[F] {
def laws: SplitLaws[F]

def split[A, B, C, D, E, G](implicit
ArbFAB: Arbitrary[F[A, B]],
ArbFBC: Arbitrary[F[B, C]],
ArbFCD: Arbitrary[F[C, D]],
ArbFDE: Arbitrary[F[D, E]],
ArbFEG: Arbitrary[F[E, G]],
EqFAD: Eq[F[A, D]],
EqFADCG: Eq[F[(A, D),(C, G)]]
): RuleSet =
new RuleSet {
def name = "split"
def bases = Nil
def parents = Seq(compose[A, B, C, D])
def props = Seq(
"split interchange" -> forAll(laws.splitInterchange[A, B, C, D, E, G] _)
)
}
}

object SplitTests {
def apply[F[_, _]: Split]: SplitTests[F] =
new SplitTests[F] { def laws = SplitLaws[F] }
}
1 change: 1 addition & 0 deletions tests/src/test/scala/cats/tests/FunctionTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ class FunctionTests extends CatsSuite {
checkAll("Function0[Int]", ComonadTests[Function0].comonad[Int, Int, Int])
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])
}