Skip to content

Add CommutativeApply and CommutativeApplicative - #1927

Merged
johnynek merged 20 commits into
typelevel:masterfrom
LukaJCB:add-commutative-apply
Oct 26, 2017
Merged

Add CommutativeApply and CommutativeApplicative#1927
johnynek merged 20 commits into
typelevel:masterfrom
LukaJCB:add-commutative-apply

Conversation

@LukaJCB

@LukaJCB LukaJCB commented Sep 24, 2017

Copy link
Copy Markdown
Member

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

codecov-io commented Sep 24, 2017

Copy link
Copy Markdown

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
Copy Markdown
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
Copy Markdown
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

tpolecat commented Sep 24, 2017

Copy link
Copy Markdown
Member

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

edmundnoble commented Sep 25, 2017

Copy link
Copy Markdown
Contributor

@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
Copy Markdown
Contributor

those are good examples.

Could we add the methods to this PR?

@LukaJCB

LukaJCB commented Sep 28, 2017

Copy link
Copy Markdown
Member Author

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

LukaJCB commented Sep 28, 2017

Copy link
Copy Markdown
Member Author

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

tpolecat commented Sep 28, 2017

Copy link
Copy Markdown
Member

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

@LukaJCB

LukaJCB commented Sep 28, 2017

Copy link
Copy Markdown
Member Author

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
Copy Markdown
Member

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

@LukaJCB

LukaJCB commented Sep 28, 2017

Copy link
Copy Markdown
Member Author

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

LukaJCB commented Sep 30, 2017

Copy link
Copy Markdown
Member Author

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
}

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

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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

LukaJCB commented Oct 19, 2017

Copy link
Copy Markdown
Member Author

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
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
Copy Markdown
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

LukaJCB commented Oct 19, 2017

Copy link
Copy Markdown
Member Author

Moved the unordered stuff to #1981

@kailuowang

Copy link
Copy Markdown
Contributor

thanks so much! @LukaJCB

@kailuowang

kailuowang commented Oct 20, 2017

Copy link
Copy Markdown
Contributor

@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?

@johnynek johnynek left a comment

Copy link
Copy Markdown
Contributor

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