-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
More instances for prod #1402
More instances for prod #1402
Changes from 5 commits
64c74fa
78f4c60
c2c7e97
95aed3a
d648c88
2032ce0
f8462cd
4e9c489
000a583
405dd93
867b2a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package cats | |
package data | ||
|
||
import cats.functor.Contravariant | ||
import cats.syntax.cartesian._ | ||
|
||
/** | ||
* [[Prod]] is a product to two independent functor values. | ||
|
@@ -52,7 +53,7 @@ private[data] sealed abstract class ProdInstances3 extends ProdInstances4 { | |
} | ||
} | ||
|
||
private[data] sealed abstract class ProdInstances4 { | ||
private[data] sealed abstract class ProdInstances4 extends ProdInstances5 { | ||
implicit def catsDataFunctorForProd[F[_], G[_]](implicit FF: Functor[F], GG: Functor[G]): Functor[λ[α => Prod[F, G, α]]] = new ProdFunctor[F, G] { | ||
def F: Functor[F] = FF | ||
def G: Functor[G] = GG | ||
|
@@ -63,6 +64,45 @@ private[data] sealed abstract class ProdInstances4 { | |
} | ||
} | ||
|
||
private[data] sealed abstract class ProdInstances5 extends ProdInstances6 { | ||
implicit def catsDataMonadForProd[F[_], G[_]](implicit FM: Monad[F], GM: Monad[G]): Monad[λ[α => Prod[F, G, α]]] = new ProdMonad[F, G] { | ||
def F: Monad[F] = FM | ||
def G: Monad[G] = GM | ||
} | ||
} | ||
|
||
private[data] sealed abstract class ProdInstances6 extends ProdInstances7 { | ||
implicit def catsDataFoldableForProd[F[_], G[_]](implicit FF: Foldable[F], GF: Foldable[G]): Foldable[λ[α => Prod[F, G, α]]] = new ProdFoldable[F, G] { | ||
def F: Foldable[F] = FF | ||
def G: Foldable[G] = GF | ||
} | ||
} | ||
|
||
private[data] sealed abstract class ProdInstances7 extends ProdInstances8 { | ||
implicit def catsDataTraverseForProd[F[_], G[_]](implicit FF: Traverse[F], GF: Traverse[G]): Traverse[λ[α => Prod[F, G, α]]] = new ProdTraverse[F, G] { | ||
def F: Traverse[F] = FF | ||
def G: Traverse[G] = GF | ||
} | ||
} | ||
|
||
private[data] sealed abstract class ProdInstances8 extends ProdInstances9 { | ||
implicit def catsDataMonadCombineForProd[F[_], G[_]](implicit FF: MonadCombine[F], GF: MonadCombine[G]): MonadCombine[λ[α => Prod[F, G, α]]] = new ProdMonadCombine[F, G] { | ||
def F: MonadCombine[F] = FF | ||
def G: MonadCombine[G] = GF | ||
} | ||
} | ||
|
||
private[data] sealed abstract class ProdInstances9 { | ||
implicit def catsDataOrderForProd[F[_], G[_], A](implicit FF: Order[F[A]], GF: Order[G[A]]): Order[Prod[F, G, A]] = new ProdOrder[F, G, A] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can probably be moved up to one sub class down from the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure! |
||
def F: Order[F[A]] = FF | ||
def G: Order[G[A]] = GF | ||
} | ||
implicit def catsDataShowForProd[F[_], G[_], A](implicit FF: Show[F[A]], GF: Show[G[A]]): Show[Prod[F, G, A]] = new ProdShow[F, G, A] { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one can probably be moved up to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes! |
||
def F: Show[F[A]] = FF | ||
def G: Show[G[A]] = GF | ||
} | ||
} | ||
|
||
sealed trait ProdFunctor[F[_], G[_]] extends Functor[λ[α => Prod[F, G, α]]] { | ||
def F: Functor[F] | ||
def G: Functor[G] | ||
|
@@ -78,7 +118,7 @@ sealed trait ProdContravariant[F[_], G[_]] extends Contravariant[λ[α => Prod[F | |
sealed trait ProdApply[F[_], G[_]] extends Apply[λ[α => Prod[F, G, α]]] with ProdFunctor[F, G] { | ||
def F: Apply[F] | ||
def G: Apply[G] | ||
def ap[A, B](f: Prod[F, G, A => B])(fa: Prod[F, G, A]): Prod[F, G, B] = | ||
override def ap[A, B](f: Prod[F, G, A => B])(fa: Prod[F, G, A]): Prod[F, G, B] = | ||
Prod(F.ap(f.first)(fa.first), G.ap(f.second)(fa.second)) | ||
override def product[A, B](fa: Prod[F, G, A], fb: Prod[F, G, B]): Prod[F, G, (A, B)] = | ||
Prod(F.product(fa.first, fb.first), G.product(fa.second, fb.second)) | ||
|
@@ -109,3 +149,56 @@ sealed trait ProdAlternative[F[_], G[_]] extends Alternative[λ[α => Prod[F, G, | |
def F: Alternative[F] | ||
def G: Alternative[G] | ||
} | ||
|
||
sealed trait ProdMonad[F[_], G[_]] extends Monad[λ[α => Prod[F, G, α]]] with ProdApplicative[F, G] { | ||
def F: Monad[F] | ||
def G: Monad[G] | ||
override def pure[A](a: A): Prod[F, G, A] = | ||
Prod(F.pure(a), G.pure(a)) | ||
|
||
override def flatMap[A, B](p: Prod[F, G, A])(f: A => Prod[F, G, B]): Prod[F, G, B] = | ||
Prod(F.flatMap(p.first)(f(_).first), G.flatMap(p.second)(f(_).second)) | ||
|
||
def tailRecM[A, B](a: A)(f: A => Prod[F, G, Either[A, B]]): Prod[F, G, B] = | ||
Prod(F.tailRecM(a)(f(_).first), G.tailRecM(a)(f(_).second)) | ||
} | ||
|
||
sealed trait ProdFoldable[F[_], G[_]] extends Foldable[λ[α => Prod[F, G, α]]] { | ||
def F: Foldable[F] | ||
def G: Foldable[G] | ||
|
||
override def foldLeft[A, B](fa: Prod[F, G, A], b: B)(f: (B, A) => B): B = | ||
G.foldLeft(fa.second, F.foldLeft(fa.first, b)(f))(f) | ||
|
||
override def foldRight[A, B](fa: Prod[F, G, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = | ||
F.foldRight(fa.first, G.foldRight(fa.second, lb)(f))(f) | ||
} | ||
|
||
sealed trait ProdTraverse[F[_], G[_]] extends Traverse[λ[α => Prod[F, G, α]]] with ProdFoldable[F, G] { | ||
def F: Traverse[F] | ||
def G: Traverse[G] | ||
|
||
override def traverse[H[_]: Applicative, A, B](fa: Prod[F, G, A])(f: A => H[B]): H[Prod[F, G, B]] = | ||
(F.traverse(fa.first)(f) |@| G.traverse(fa.second)(f)).map(Prod(_, _)) | ||
} | ||
|
||
sealed trait ProdMonadCombine[F[_], G[_]] extends MonadCombine[λ[α => Prod[F, G, α]]] | ||
with ProdMonad[F, G] with ProdAlternative[F, G] { | ||
def F: MonadCombine[F] | ||
def G: MonadCombine[G] | ||
} | ||
|
||
sealed trait ProdShow[F[_], G[_], A] extends Show[Prod[F, G, A]] { | ||
def F: Show[F[A]] | ||
def G: Show[G[A]] | ||
|
||
def show(prod: Prod[F, G, A]): String = s"Prod(${F.show(prod.first)}, ${G.show(prod.second)})" | ||
} | ||
|
||
sealed trait ProdOrder[F[_], G[_], A] extends Order[Prod[F, G, A]] { | ||
def F: Order[F[A]] | ||
def G: Order[G[A]] | ||
|
||
def compare(x: Prod[F, G, A], y: Prod[F, G, A]): Int = | ||
Array(F.compare(x.first, y.first), G.compare(x.second, y.second)).find(_ != 0).getOrElse(0) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,8 @@ class ProdTests extends CatsSuite { | |
checkAll("Prod[Show, Order, Int]", ContravariantTests[λ[α => Prod[Show, Order, α]]].contravariant[Int, Int, Int]) | ||
checkAll("Contravariant[Prod[Show, Order, Int]]", SerializableTests.serializable(Contravariant[λ[α => Prod[Show, Order, α]]])) | ||
|
||
checkAll("Show[Prod[Option, Option, Int]]", SerializableTests.serializable(Show[Prod[Option, Option, Int]])) | ||
|
||
{ | ||
implicit val monoidK = ListWrapper.monoidK | ||
checkAll("Prod[ListWrapper, ListWrapper, ?]", MonoidKTests[Prod[ListWrapper, ListWrapper, ?]].monoidK[Int]) | ||
|
@@ -42,4 +44,50 @@ class ProdTests extends CatsSuite { | |
checkAll("Prod[ListWrapper, ListWrapper, ?]", FunctorTests[Prod[ListWrapper, ListWrapper, ?]].functor[Int, Int, Int]) | ||
checkAll("Functor[Prod[ListWrapper, ListWrapper, ?]]", SerializableTests.serializable(Functor[Prod[ListWrapper, ListWrapper, ?]])) | ||
} | ||
|
||
{ | ||
implicit val monad = ListWrapper.monad | ||
implicit val iso = CartesianTests.Isomorphisms.invariant[Prod[ListWrapper, ListWrapper, ?]] | ||
checkAll("Prod[ListWrapper, ListWrapper, ?]", MonadTests[Prod[ListWrapper, ListWrapper, ?]].monad[Int, Int, Int]) | ||
checkAll("Monad[Prod[ListWrapper, ListWrapper, ?]]", SerializableTests.serializable(Monad[Prod[ListWrapper, ListWrapper, ?]])) | ||
} | ||
|
||
{ | ||
implicit val foldable = ListWrapper.foldable | ||
checkAll("Prod[ListWrapper, ListWrapper, ?]", FoldableTests[Prod[ListWrapper, ListWrapper, ?]].foldable[Int, Int]) | ||
checkAll("Foldable[Prod[ListWrapper, ListWrapper, ?]]", SerializableTests.serializable(Foldable[Prod[ListWrapper, ListWrapper, ?]])) | ||
} | ||
|
||
{ | ||
implicit val traverse = ListWrapper.traverse | ||
checkAll("Prod[ListWrapper, ListWrapper, ?]", TraverseTests[Prod[ListWrapper, ListWrapper, ?]].traverse[Int, Int, Int, Int, Option, Option]) | ||
checkAll("Traverse[Prod[ListWrapper, ListWrapper, ?]]", SerializableTests.serializable(Traverse[Prod[ListWrapper, ListWrapper, ?]])) | ||
} | ||
|
||
{ | ||
implicit val monadCombine = ListWrapper.monadCombine | ||
implicit val iso = CartesianTests.Isomorphisms.invariant[Prod[ListWrapper, ListWrapper, ?]] | ||
checkAll("Prod[ListWrapper, ListWrapper, ?]", MonadCombineTests[Prod[ListWrapper, ListWrapper, ?]].monadCombine[Int, Int, Int]) | ||
checkAll("MonadCombine[Prod[ListWrapper, ListWrapper, ?]]", SerializableTests.serializable(MonadCombine[Prod[ListWrapper, ListWrapper, ?]])) | ||
} | ||
|
||
test("order") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure! |
||
forAll { t: Prod[Id, Id, Int] => | ||
val u: Prod[Id, Id, Int] = Prod(t.second, t.first) | ||
val Prod(t1, t2) = t | ||
val Prod(u1, u2) = u | ||
|
||
Order[Prod[Id, Id, Int]].compare(t, u) should === (Order[(Int, Int)].compare((t1, t2), (u1, u2))) | ||
} | ||
} | ||
|
||
test("show") { | ||
forAll { (l1: Option[Int], l2: Option[Int]) => | ||
val showOptionInt = implicitly[Show[Option[Int]]] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpicking, but this can just be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, you're right :) |
||
val prod = Prod(l1, l2) | ||
|
||
Show[Prod[Option, Option, Int]].show(prod) should === (s"Prod(${showOptionInt.show(l1)}, ${showOptionInt.show(l2)})") | ||
} | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this particular case, to ensure the more specific instance having the highest priority, we basically want to ensure that instances of typeclasses who are lower in the inheritance tree are also lower in the instance trait inheritance tree. For example,
Monad
inheritsFunctor
and thus is more "specific" thanFunctor
, so it should be in an instance trait with higher priority (0 - 3) than whereFunctor
is (4).So, we need to rearrange the instances to that order.
MonadCombine
<:Altenative
MonadCombine
<:Monad
<:Applicative
<:Apply
<:Functor
Traverse
<:Foldable
Traverse
<:Functor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing that out, I didn't think on that!
I've done the changes in 000a583