Skip to content

Commit

Permalink
Add dual categories. (#2217)
Browse files Browse the repository at this point in the history
  • Loading branch information
sellout authored and kailuowang committed May 30, 2018
1 parent f4722ac commit 3993dba
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 0 deletions.
50 changes: 50 additions & 0 deletions core/src/main/scala/cats/data/Op.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package cats
package data

import cats.arrow._

/**
* The dual category of some other category, `Arr`.
*/
final case class Op[Arr[_, _], A, B](run: Arr[B, A]) {
def compose[Z](op: Op[Arr, Z, A])(implicit Arr: Compose[Arr]): Op[Arr, Z, B] =
Op(Arr.compose(op.run, run))

def eqv(op: Op[Arr, A, B])(implicit Arr: Eq[Arr[B, A]]): Boolean =
Arr.eqv(run, op.run)
}

object Op extends OpInstances

private[data] sealed abstract class OpInstances extends OpInstances0 {
implicit def catsDataCategoryForOp[Arr[_, _]](implicit ArrC: Category[Arr]): Category[Op[Arr, ?, ?]] =
new OpCategory[Arr] { def Arr: Category[Arr] = ArrC }

implicit def catsKernelEqForOp[Arr[_, _], A, B](implicit ArrEq: Eq[Arr[B, A]]): Eq[Op[Arr, A, B]] =
new OpEq[Arr, A, B] { def Arr: Eq[Arr[B, A]] = ArrEq }
}

private[data] sealed abstract class OpInstances0 {
implicit def catsDataComposeForOp[Arr[_, _]](implicit ArrC: Compose[Arr]): Compose[Op[Arr, ?, ?]] =
new OpCompose[Arr] { def Arr: Compose[Arr] = ArrC }
}

private[data] trait OpCategory[Arr[_, _]] extends Category[Op[Arr, ?, ?]] with OpCompose[Arr] {
implicit def Arr: Category[Arr]

override def id[A]: Op[Arr, A, A] = Op(Arr.id)
}

private[data] trait OpCompose[Arr[_, _]] extends Compose[Op[Arr, ?, ?]] {
implicit def Arr: Compose[Arr]

def compose[A, B, C](f: Op[Arr, B, C], g: Op[Arr, A, B]): Op[Arr, A, C] =
f.compose(g)
}

private[data] trait OpEq[Arr[_, _], A, B] extends Eq[Op[Arr, A, B]] {
implicit def Arr: Eq[Arr[B, A]]

def eqv(f: Op[Arr, A, B], g: Op[Arr, A, B]): Boolean =
f.eqv(g)
}
6 changes: 6 additions & 0 deletions laws/src/main/scala/cats/laws/discipline/Arbitrary.scala
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,12 @@ object arbitrary extends ArbitraryInstances0 {
implicit def catsLawArbitraryForCokleisliId[A: Arbitrary: Cogen, B: Arbitrary]: Arbitrary[Cokleisli[Id, A, B]] =
catsLawsArbitraryForCokleisli[Id, A, B]

implicit def catsLawsArbitraryForOp[Arr[_, _], A, B](implicit Arr: Arbitrary[Arr[B, A]]): Arbitrary[Op[Arr, A, B]] =
Arbitrary(Arr.arbitrary.map(Op(_)))

implicit def catsLawsCogenForOp[Arr[_, _], A, B](implicit Arr: Cogen[Arr[B, A]]): Cogen[Op[Arr, A, B]] =
Arr.contramap(_.run)

implicit def catsLawsArbitraryForIRWST[F[_]: Applicative, E, L, SA, SB, A](implicit
F: Arbitrary[(E, SA) => F[(L, SB, A)]]): Arbitrary[IndexedReaderWriterStateT[F, E, L, SA, SB, A]] =
Arbitrary(F.arbitrary.map(IndexedReaderWriterStateT(_)))
Expand Down
37 changes: 37 additions & 0 deletions tests/src/test/scala/cats/tests/OpSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package cats
package tests

import cats.arrow._
import cats.data.{Kleisli, Op}
import cats.laws.discipline._
import cats.laws.discipline.arbitrary._
import cats.laws.discipline.eq._
import cats.kernel.laws.discipline.EqTests

class OpSuite extends CatsSuite {
{
implicit val catsKernelEqForOp = Op.catsKernelEqForOp[Function1, Char, Int]
checkAll("Op[Function1, Char, Int]", EqTests[Op[Function1, Char, Int]].eqv)
checkAll("Eq[Op[Function1, Char, Int]]", SerializableTests.serializable(Eq[Op[Function1, Char, Int]]))
}

{
implicit val catsDataCategoryForOp = Op.catsDataCategoryForOp[Function1]
checkAll("Op[Function1, Char, Int]", CategoryTests[Op[Function1, ?, ?]].category[Char, Int, Char, Int])
checkAll("Category[Op[Function1, ?, ?]]", SerializableTests.serializable(Category[Op[Function1, ?, ?]]))
}

/**
* Testing that implicit resolution works. If it compiles, the "test" passes.
*/
object ImplicitResolution {
// Arr is Function1
Category[Op[Function1, ?, ?]]
Compose[Op[Function1, ?, ?]]
Eq[Op[Function1, Char, Int]]

// Arr is Kleisli[Option, ?, ?]
Category[Op[Kleisli[Option, ?, ?], ?, ?]]
Compose[Op[Kleisli[Option, ?, ?], ?, ?]]
}
}

0 comments on commit 3993dba

Please sign in to comment.