-
-
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 Invariant instances for kernel type classes #1914
Conversation
5ab0e76
to
ac4c94f
Compare
close and reopen to trigger build again (somehow failed on codecov) |
Codecov Report
@@ Coverage Diff @@
## master #1914 +/- ##
==========================================
+ Coverage 96.08% 96.21% +0.13%
==========================================
Files 273 272 -1
Lines 4541 4627 +86
Branches 119 124 +5
==========================================
+ Hits 4363 4452 +89
+ Misses 178 175 -3
Continue to review full report at Codecov.
|
ac4c94f
to
f3752f1
Compare
Should mention that this is an API breaking change, as imports from |
I probably missed something but I had the impression that we want to include all these instances into the companion objects of |
Oh, I may have misunderstood then. The only con, I can think about is that these won't get imported when importing all instances which might be confusing to some? |
If you put them in the companion, they never need to be imported right? |
They don't? 🤔 Maybe I'm missing something here, but what would happen if I want to do something like: val semiB: Semigroup[B] = Semigroup[A].imap(...) I'd need to import the implicit instance from somewhere, right? |
@LukaJCB for your example, it should work without import if the demo, the following code compiles trait TCParent[F[_]]
trait TCChild[F[_]] extends TCParent[F]
trait A[T]
object TCParent {
implicit val tcA: TCChild[A] = null
}
object Test {
implicit class TCParentOps[F[_], T](f: F[T])(implicit tcF: TCParent[F]) {
def e1: String = "bah"
}
implicit class TCOps[F[_], T](f: F[T])(implicit tcF: TCChild[F]) {
def e: String = "bah"
}
val a: A[String] = ???
a.e
a.e1
}
|
Ah okay, I think I've found the issue, we currently have tests, that check whether |
ah didn't realize about the |
2ab053d
to
f3752f1
Compare
we could have a fallback possibly that looks in the other places. So if Not really sure that's a great idea. |
implicit val catsInvariantMonoidalMonoid: InvariantMonoidal[Monoid] = new InvariantMonoidal[Monoid] { | ||
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) |
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.
shame this destroys the combineAllOption optimization. Maybe we can add a ticket to address this after we merge.
In algebird, we buffer 1000 items internally, then pass over the block with each of the underlying monoids.
def pure[A](a: A): CommutativeMonoid[A] = new CommutativeMonoid[A] { | ||
val empty = a | ||
def combine(x: A, y: A): A = a | ||
override def combineAll(as: TraversableOnce[A]): A = a |
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.
can we only override combineAllOption
? If we override only combineAll
, then calls to combineAllOption
still get the slow path. combineAll
calls back to combineAllOption
(combineAll should probably be final, actually).
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.
I did override in all of the instances, but can you comment on how this is useful? I really can't come up with a way 🤔 and therefore can't find a good way to test these
import cats.kernel._ | ||
import cats.InvariantMonoidal | ||
|
||
trait InvariantInstances { |
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.
looks like these are all InvariantMonoidal
not just Invariant
. Should we put them in a different trait name?
I've added an override for Edit: seems like the overriden |
66f8943
to
a85ce5c
Compare
implicit val arbCommutativeGroupInt: Arbitrary[CommutativeGroup[Int]] = | ||
Arbitrary(genCommutativeGroupInt) | ||
|
||
test("Semigroup combineAllOption after imap with id is consistent"){ |
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.
I propose we add this to the Semigroup law as a "Ref" law like these
https://github.com/typelevel/cats/blob/master/laws/src/main/scala/cats/laws/FoldableLaws.scala#L122-L143
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.
Ah, I didn't know we had such a convention, good to know :)
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.
We dont' really, but I'd like to establish one, see discussion here. @peterneyens has some interesting ideas too.
implicit val arbCommutativeGroupInt: Arbitrary[CommutativeGroup[Int]] = | ||
Arbitrary(genCommutativeGroupInt) | ||
|
||
def combineAllOption_Ref[F[_] : InvariantMonoidal, A: Eq](name: String, a: A)(implicit ev: F[A] <:< Semigroup[A]): Unit = { |
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, I was suggesting adding this to the SemigroupLaw
but I just realized that the consolidation PR #1922 isn't merged yet. Let's wait until that one gets merged, shall we?
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.
I agree we should wait, however, this one isn't really about combineAllOption
for any given semigroup, but for the ones created by InvariantMonoidal[F].pure
. SemigroupLaws
already has a check for combineAllOption
here :)
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.
ah I see. so, even without this methods and the tests that use it, the combineAllOption
should be covered once you used GroupLaws
to test the new instances? So for these pure
given instance, why not test them with the GroupLaws
again?
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.
I think we could do that, but I didn't think the pure
ones were actually lawful instances.
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.
I'd be curious to find out.
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.
Okay so the Semigroup
instance is not lawful, because of the combineAllOption
, as defined here, is not consistent with reduceOption(_ |+| _)
, because when you pass a collection with only one value reduceOption
will only turn that into an Option
. One could argue that this doesn't necessarily break the Semigroup laws though.
In addition the Monoid
instance fails to fulfil the left and right identity laws.
Also these instances aren't able to abide by the idempotency law.
Does this mean Monoid
is not a valid InvariantMonoidal
? I'm not sure I know enough about that.
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.
if the pure instance is not lawful, I am not sure if it's still a InvariantMonoidal
, maybe just a Invariant
? maybe worthwhile to find out how it's defined in Haskell.
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.
I was surprised at the pure
. I thought these two were only Invariant
.
By the way, I think Invariant
and Cartesian
are the two ways you actually want to compose these things.
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.
So, I think we should get rid of all Invariant Monoidal except for Semigroup and Commutative Semigroup and Turn the others into Invariant instances
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.
👍 BTW, for Invariant
instances, they can be moved the companion object of Invariant
right?
5f9b12a
to
29e5322
Compare
To summarize the breaking changes introduced in this PR for later use:
|
do you mind also update the doc of |
29e5322
to
6ac8aaf
Compare
@johnynek got any more feedback for this PR? |
070f620
to
60aa276
Compare
@@ -6,8 +6,6 @@ package tests | |||
class MonoidTests extends CatsSuite { | |||
{ | |||
Invariant[Monoid] | |||
Semigroupal[Monoid] |
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.
why did we lose this? I don't see it?
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.
Good catch, I falsely assumed, that because Monoid
wasn't a valid InvariantMonoidal
, that it also wasn't a lawful Semigroupal
. Turns out, it is, and I added it back! Thanks! :)
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.
I missed that too. yeah, what broke the law was the pure
method added in InvariantMonoidal
👍 |
@LukaJCB Thank you so much for carrying this over the line! |
Adds
InvariantMonoidal
to most of the kernel type classes as discussed in #1904.