-
-
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
Add InvariantSemigroupal and ability to turn Monoidals to Monoids #2088
Changes from 9 commits
ec1f604
2edcb85
495cc74
d8b496d
19dc0ff
38b46e6
f4b7490
f9df1c3
5e17eed
8e2f7ab
3bfaf86
b7f36f2
94b0d2e
5443046
1a10366
dfbcf66
7880ada
3e3982f
95f58ff
4b73af8
30391e8
18f353d
436e444
ff0a355
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 |
---|---|---|
|
@@ -8,7 +8,7 @@ import simulacrum.typeclass | |
* Must obey the laws defined in cats.laws.ApplyLaws. | ||
*/ | ||
@typeclass(excludeParents = List("ApplyArityFunctions")) | ||
trait Apply[F[_]] extends Functor[F] with Semigroupal[F] with ApplyArityFunctions[F] { self => | ||
trait Apply[F[_]] extends Functor[F] with InvariantSemigroupal[F] with ApplyArityFunctions[F] { self => | ||
|
||
/** | ||
* Given a value and a function in the Apply context, applies the | ||
|
@@ -79,11 +79,14 @@ trait Apply[F[_]] extends Functor[F] with Semigroupal[F] with ApplyArityFunction | |
val F = self | ||
val G = Apply[G] | ||
} | ||
|
||
override def semigroup[A](implicit A: Semigroup[A]): Semigroup[F[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. same question about customization. |
||
new ApplySemigroup[F, A](this, A) | ||
} | ||
|
||
object Apply { | ||
def semigroup[F[_], A](implicit f: Apply[F], sg: Semigroup[A]): Semigroup[F[A]] = | ||
new ApplySemigroup[F, A](f, sg) | ||
f.semigroup | ||
} | ||
|
||
private[cats] class ApplySemigroup[F[_], A](f: Apply[F], sg: Semigroup[A]) extends Semigroup[F[A]] { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -136,6 +136,18 @@ private[cats] trait ComposedSemigroupal[F[_], G[_]] extends ContravariantSemigro | |
} | ||
} | ||
|
||
private[cats] trait ComposedInvariantApplySemigroupal[F[_], G[_]] extends InvariantSemigroupal[λ[α => F[G[α]]]] with ComposedInvariantCovariant[F, G] { outer => | ||
def F: InvariantSemigroupal[F] | ||
def G: Apply[G] | ||
|
||
def product[A, B](fa: F[G[A]], fb: F[G[B]]): F[G[(A, B)]] = | ||
F.imap(F.product(fa, fb)) { case (ga, gb) => | ||
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. do you plan to add tests for this instance? |
||
G.map2(ga, gb)(_ -> _) | ||
} { g: G[(A, B)] => | ||
(G.map(g)(_._1), G.map(g)(_._2)) | ||
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 line is not tested, we might need to test an instance that is not a covariant. |
||
} | ||
} | ||
|
||
private[cats] trait ComposedCovariantContravariant[F[_], G[_]] extends Contravariant[λ[α => F[G[α]]]] { outer => | ||
def F: Functor[F] | ||
def G: Contravariant[G] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,12 +11,24 @@ import simulacrum.typeclass | |
* Based on ekmett's contravariant library: | ||
* https://hackage.haskell.org/package/contravariant-1.4/docs/Data-Functor-Contravariant-Divisible.html | ||
*/ | ||
@typeclass trait ContravariantMonoidal[F[_]] extends ContravariantSemigroupal[F] { self => | ||
@typeclass trait ContravariantMonoidal[F[_]] extends ContravariantSemigroupal[F] with InvariantMonoidal[F] { | ||
/** | ||
* `unit` produces an instance of `F` for any type `A` | ||
* that is trivial with respect to `contramap2` along | ||
* the diagonal | ||
*/ | ||
def unit[A]: F[A] | ||
|
||
override def pure[A](a: A): F[A] = unit | ||
|
||
override def monoid[A](implicit A: Monoid[A]): Monoid[F[A]] = | ||
ContravariantMonoidal.monoid(this) | ||
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 seems to be breaking the previous pattern: we are calling the object from the trait, but in the other cases we call it the opposite direction. 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, this is because we can define a |
||
} | ||
object ContravariantMonoidal extends SemigroupalArityFunctions { | ||
def monoid[F[_], A](implicit f: ContravariantMonoidal[F]): Monoid[F[A]] = | ||
new ContravariantMonoidalMonoid[F, A](f) | ||
} | ||
|
||
private[cats] class ContravariantMonoidalMonoid[F[_], A](f: ContravariantMonoidal[F]) extends ContravariantSemigroupalSemigroup[F, A](f) with Monoid[F[A]] { | ||
def empty: F[A] = f.unit | ||
} | ||
object ContravariantMonoidal extends SemigroupalArityFunctions |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package cats | ||
|
||
import simulacrum.typeclass | ||
|
||
/** | ||
* [[InvariantSemigroupal]] is nothing more than something both invariant | ||
* and Semigroupal. It comes up enough to be useful, and composes well | ||
*/ | ||
@typeclass trait InvariantSemigroupal[F[_]] extends Semigroupal[F] with Invariant[F] { self => | ||
|
||
def composeApply[G[_]: Apply]: InvariantSemigroupal[λ[α => F[G[α]]]] = | ||
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. Any plan to test cover this one as well? 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. Has this been addressed? 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! |
||
new ComposedInvariantApplySemigroupal[F, G] { | ||
def F = self | ||
def G = Apply[G] | ||
} | ||
|
||
/** | ||
* Gives a `Semigroup` instance if A itself has a `Semigroup` instance. | ||
*/ | ||
def semigroup[A](implicit A: Semigroup[A]): Semigroup[F[A]] = | ||
new InvariantSemigroupalSemigroup[F, A](this, A) | ||
|
||
} | ||
|
||
object InvariantSemigroupal extends SemigroupalArityFunctions | ||
|
||
private[cats] class InvariantSemigroupalSemigroup[F[_], A](f: InvariantSemigroupal[F], sg: Semigroup[A]) extends Semigroup[F[A]] { | ||
def combine(a: F[A], b: F[A]): F[A] = | ||
InvariantSemigroupal.imap2(a, b)(sg.combine)(a => (a, a))(f, f) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,23 @@ | ||
package cats.instances | ||
|
||
import cats.kernel._ | ||
import cats.InvariantMonoidal | ||
import cats.{InvariantMonoidal, Monoid, InvariantSemigroupal} | ||
|
||
trait InvariantMonoidalInstances { | ||
|
||
implicit def catsSemigroupalForMonoid: InvariantSemigroupal[Monoid] = new InvariantSemigroupal[Monoid] { | ||
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. I am curious why not move these two to the companion of 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. Because they're in kernel :/ 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. oh Duh |
||
def product[A, B](fa: Monoid[A], fb: Monoid[B]): Monoid[(A, B)] = new Monoid[(A, B)] { | ||
val empty = fa.empty -> fb.empty | ||
def combine(x: (A, B), y: (A, B)): (A, B) = fa.combine(x._1, y._1) -> fb.combine(x._2, y._2) | ||
} | ||
|
||
def imap[A, B](fa: Monoid[A])(f: A => B)(g: B => A): Monoid[B] = new Monoid[B] { | ||
def empty: B = f(fa.empty) | ||
|
||
def combine(x: B, y: B): B = f(fa.combine(g(x), g(y))) | ||
} | ||
} | ||
|
||
implicit val catsInvariantMonoidalSemigroup: InvariantMonoidal[Semigroup] = new InvariantMonoidal[Semigroup] { | ||
def product[A, B](fa: Semigroup[A], fb: Semigroup[B]): Semigroup[(A, B)] = new Semigroup[(A, B)] { | ||
def combine(x: (A, B), y: (A, B)): (A, B) = fa.combine(x._1, y._1) -> fb.combine(x._2, y._2) | ||
|
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.
every time we add a method to a trait, we break binary compatibility. Is there any reason to have this here vs the
object
? Can you really customize this based on theF[_]
?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.
Hmm, maybe you are right. We had the same discussion with regards to parallel, but I think in this case it might not be as customizable.