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 CommutativeApply and CommutativeApplicative #1927

Merged
merged 20 commits into from
Oct 26, 2017

Conversation

LukaJCB
Copy link
Member

@LukaJCB LukaJCB commented Sep 24, 2017

Had a quick discussion about this on gitter. Also adds an instance for Validated[E: CommutativeSemigroup, ?] :)

@LukaJCB LukaJCB force-pushed the add-commutative-apply branch from 8c95312 to 0ea4ac0 Compare September 24, 2017 16:23
@codecov-io
Copy link

codecov-io commented Sep 24, 2017

Codecov Report

Merging #1927 into master will decrease coverage by 0.08%.
The diff coverage is 84.61%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1927      +/-   ##
==========================================
- Coverage   96.21%   96.13%   -0.09%     
==========================================
  Files         272      278       +6     
  Lines        4628     4655      +27     
  Branches      124      130       +6     
==========================================
+ Hits         4453     4475      +22     
- Misses        175      180       +5
Impacted Files Coverage Δ
...rc/main/scala/cats/laws/CommutativeMonadLaws.scala 100% <ø> (ø) ⬆️
.../main/scala/cats/laws/CommutativeFlatMapLaws.scala 66.66% <ø> (ø) ⬆️
core/src/main/scala/cats/CommutativeApply.scala 0% <0%> (ø)
core/src/main/scala/cats/CommutativeMonad.scala 0% <0%> (ø) ⬆️
core/src/main/scala/cats/CommutativeFlatMap.scala 0% <0%> (ø) ⬆️
...e/src/main/scala/cats/CommutativeApplicative.scala 0% <0%> (ø)
core/src/main/scala/cats/data/Validated.scala 100% <100%> (ø) ⬆️
...n/scala/cats/laws/CommutativeApplicativeLaws.scala 100% <100%> (ø)
.../laws/discipline/CommutativeApplicativeTests.scala 100% <100%> (ø)
...rc/main/scala/cats/laws/CommutativeApplyLaws.scala 100% <100%> (ø)
... and 11 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b66b121...d673655. Read the comment docs.

*
* Must obey the laws defined in cats.laws.CommutativeApplicativeLaws.
*/
@typeclass trait CommutativeApplicative[F[_]] extends Applicative[F] with CommutativeApply[F]
Copy link
Member Author

Choose a reason for hiding this comment

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

For some reason Codecov doesn't think this is being hit, does anyone know why?

@johnynek
Copy link
Contributor

This seems like a very interesting property (one that Option springs to mind as a canonical example), but chat can I do with an instance?

Can we not write a method or two to show the value of the abstraction. I tend to think to make it into cats an abstraction needs to pay its way. I haven’t yet seen that here, but probably because I am unfamiliar.

@tpolecat
Copy link
Member

tpolecat commented Sep 24, 2017

Future is another effect with this property. The order in which you construct futures is significant if you're side-effecting, but the order of applicative composition doesn't matter. Also the "sbt effect": the rewrite macro pulls the out the .value symbols in an arbitrary order and it doesn't matter.

You could use this property to properly constrain general rewrite systems like monadless (which preserves equational reasoning only for commutative monads).

None of these observations leads us closer to an easy example though. @edmundnoble mentioned that it might be handy for optimizing free applicatives since you could re-order effects.

@edmundnoble
Copy link
Contributor

edmundnoble commented Sep 25, 2017

@johnynek Given the ability to reorder computations in FreeApplicative, you can batch them safely.

Also, unordered containers can be traversed safely with CommutativeApplicative, e.g. Set and Map. Right now, if you want to traverse them, you are forced into observing the implementation-defined ordering, and violating the contract of equality.

@johnynek
Copy link
Contributor

those are good examples.

Could we add the methods to this PR?

@LukaJCB
Copy link
Member Author

LukaJCB commented Sep 28, 2017

Hmm, so as I see we could either create some sort of traverseUnordered as syntax sugar on Set and Map, or we could want to create a type class for such unordered containers?

Leaning towards the former, as the latter might open another larger can of worms (Would a TraverseUnordered inherit from a theoretical non-structure preserving Functor?)

On another note, once #1837 is merged, I'm pretty sure all the Zip{List, Vector...} are all instances of CommutativeApplicative :)

@LukaJCB LukaJCB force-pushed the add-commutative-apply branch 2 times, most recently from ef2c94b to edd6ba8 Compare September 28, 2017 11:44
@LukaJCB LukaJCB force-pushed the add-commutative-apply branch from edd6ba8 to 0cd4eb9 Compare September 28, 2017 12:14
@LukaJCB
Copy link
Member Author

LukaJCB commented Sep 28, 2017

Okay, maybe Future is not a valid CommutativeApplicative because the ordering matters if one of the Futures is a Failure, so it only works for Successful cases.

@tpolecat
Copy link
Member

tpolecat commented Sep 28, 2017

@LukaJCB What was the issue with Future? (a *> b) and (b <* a) should always compute the same result.

@LukaJCB
Copy link
Member Author

LukaJCB commented Sep 28, 2017

Yeah, but only if the result is successful:

scala> val a: Future[Int] = Future.failed(new Exception("A"))
a: scala.concurrent.Future[Int] = Future(Failure(java.lang.Exception: A))

scala> val b: Future[Int] = Future.failed(new Exception("B"))
b: scala.concurrent.Future[Int] = Future(Failure(java.lang.Exception: B))

scala> a *> b
res0: scala.concurrent.Future[Int] = Future(Failure(java.lang.Exception: A))

scala> b <* a
res1: scala.concurrent.Future[Int] = Future(Failure(java.lang.Exception: B))

@tpolecat
Copy link
Member

@LukaJCB hell. Can't ever win with Future. Good counterexample, thanks!

@LukaJCB
Copy link
Member Author

LukaJCB commented Sep 28, 2017

You really can't 😄. Want to weigh in on syntax sugar vs type class? :)

@LukaJCB LukaJCB force-pushed the add-commutative-apply branch from 6c2a240 to fcb429d Compare September 30, 2017 18:34
@LukaJCB
Copy link
Member Author

LukaJCB commented Sep 30, 2017

Another cool thing is that CommutativeApplicative also composes using Nested and Tuple2K

@LukaJCB LukaJCB force-pushed the add-commutative-apply branch from fcb429d to 5bf6f35 Compare September 30, 2017 20:18
@LukaJCB LukaJCB force-pushed the add-commutative-apply branch from 5bf6f35 to d3a803a Compare October 2, 2017 16:22
implicit def catsDataInvariantForNestedContravariant[F[_]: Invariant, G[_]: Contravariant]: Invariant[Nested[F, G, ?]] =
new NestedInvariant[F, G] {
val FG: Invariant[λ[α => F[G[α]]]] = Invariant[F].composeContravariant[G]
}
}

private[data] sealed abstract class NestedInstances11 extends NestedInstances12 {
implicit def catsDataCommutativeApplyForNestedContravariant[F[_]: CommutativeApply, G[_]: CommutativeApply]: CommutativeApply[Nested[F, G, ?]] =
Copy link
Contributor

Choose a reason for hiding this comment

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

Usually we place more specific instance higher priority, i.e. CommutativeApply and CommutativeApplicative should have higher priority than Apply and Applicative.
CommutativeApplicative should have higher priority than CommutativeApply.

Also line 53 the instance name was wrong catsDataContravariantForNested -> catsDataFunctorForNested do you mind fix that in this PR as well?

kailuowang
kailuowang previously approved these changes Oct 17, 2017
*
* Must obey the laws defined in cats.laws.CommutativeApplicativeLaws.
*/
@typeclass trait CommutativeApplicative[F[_]] extends Applicative[F] with CommutativeApply[F] {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we really want an UnorderedFoldable and UnorderedTraverse that instead of toList has toSet and has a foldMap that takes a CommutativeMonoid and for sequence/traverse uses CommutativeApplicative.

That seems cleaner to me than putting these methods here now that I think about it.

I think we can construct sane laws for UnorderedFoldable that would make sense. If we added a method like insert[T](f: F[T], item: T): F[T] then I think you could make some law.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree that an extra type class would feel a lot cleaner than baking these 4 operations into the CommutativeApplicative type class. Related thoughts here.
I've played around with some laws, and we wouldn't have to get rid of too many laws too make it fit. I also like the insert, although I'd have to think about it more. We could let Foldable extend UnorderedFoldable, if that makes any sense. I'll play around with it and update the PR accordingly :)

Copy link
Member Author

Choose a reason for hiding this comment

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

This opens up a can of worms though. We could probably also define something like unorderedFoldM with CommutativeMonad, a UnorderedReducible, NonEmptyUnordered and all kinds of others. I came up with something crazy a while ago on gitter https://gitter.im/typelevel/cats-dev?at=59cf81a5210ac269208e0bcf but seems I've only scratched the surface 😄

I'll try to define this as minimal as possible and go from there. Might make a good blog post though.

Copy link
Member Author

Choose a reason for hiding this comment

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

I had to remove insert for now, because it's not possible to implement it for Map :/

@LukaJCB LukaJCB force-pushed the add-commutative-apply branch from d3f10ca to d308cf7 Compare October 18, 2017 22:51
@kailuowang
Copy link
Contributor

I have a proposal:
CommunitiveApply and CommutativeApplicative are obviously useful, straightforward and clearly defined. Can we limit this PR to these two type classes (without the unorderedTraverseXXX methods) and move all the unordered traverse stuff to a separate follow up PR?

@LukaJCB
Copy link
Member Author

LukaJCB commented Oct 19, 2017

Yeah I can do that :)

@LukaJCB LukaJCB force-pushed the add-commutative-apply branch 2 times, most recently from 4889a52 to e0069a8 Compare October 19, 2017 17:40
@LukaJCB LukaJCB force-pushed the add-commutative-apply branch from 908c914 to 305d740 Compare October 19, 2017 17:52
@@ -15,6 +16,28 @@ class MapSuite extends CatsSuite {
checkAll("Map[Int, Int] with Option", TraverseTests[Map[Int, ?]].traverse[Int, Int, Int, Int, Option, Option])
checkAll("Traverse[Map[Int, ?]]", SerializableTests.serializable(Traverse[Map[Int, ?]]))

test("traverseUnordered identity") {
Copy link
Contributor

Choose a reason for hiding this comment

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

you forgot to remove these tests

@LukaJCB LukaJCB force-pushed the add-commutative-apply branch from 305d740 to 028b9a7 Compare October 19, 2017 17:55
@LukaJCB
Copy link
Member Author

LukaJCB commented Oct 19, 2017

Moved the unordered stuff to #1981

@kailuowang
Copy link
Contributor

thanks so much! @LukaJCB

@kailuowang
Copy link
Contributor

kailuowang commented Oct 20, 2017

@johnynek this PR is simplified with only the Communtative type classes. Any other comments/feedbacks? Shall we merge this and then we can move to #1981?

Copy link
Contributor

@johnynek johnynek left a comment

Choose a reason for hiding this comment

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

Thanks @LukaJCB !

@johnynek johnynek merged commit 8fd709a into typelevel:master Oct 26, 2017
@stew stew removed the in progress label Oct 26, 2017
@LukaJCB LukaJCB deleted the add-commutative-apply branch October 26, 2017 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants