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 Parallel type class #1837

Merged
merged 59 commits into from
Oct 26, 2017
Merged

Conversation

LukaJCB
Copy link
Member

@LukaJCB LukaJCB commented Aug 21, 2017

Should resolve #1830. Only Syntax and type class for now, but more to follow :)

@codecov-io
Copy link

codecov-io commented Aug 21, 2017

Codecov Report

Merging #1837 into master will increase coverage by 0.01%.
The diff coverage is 96.66%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1837      +/-   ##
==========================================
+ Coverage   96.21%   96.22%   +0.01%     
==========================================
  Files         272      279       +7     
  Lines        4628     4748     +120     
  Branches      124      115       -9     
==========================================
+ Hits         4453     4569     +116     
- Misses        175      179       +4
Impacted Files Coverage Δ
...rc/main/scala/cats/laws/discipline/Arbitrary.scala 92.18% <ø> (ø) ⬆️
core/src/main/scala/cats/data/NonEmptyVector.scala 100% <ø> (ø) ⬆️
core/src/main/scala/cats/data/OneAnd.scala 98.36% <ø> (ø) ⬆️
core/src/main/scala/cats/data/NonEmptyList.scala 100% <ø> (ø) ⬆️
laws/src/main/scala/cats/laws/ParallelLaws.scala 100% <100%> (ø)
core/src/main/scala/cats/data/WriterT.scala 94.62% <100%> (+0.43%) ⬆️
core/src/main/scala/cats/syntax/parallel.scala 100% <100%> (ø)
core/src/main/scala/cats/data/Kleisli.scala 100% <100%> (ø) ⬆️
...ain/scala/cats/laws/discipline/ParallelTests.scala 100% <100%> (ø)
core/src/main/scala/cats/instances/parallel.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...0c87a1b. Read the comment docs.

Parallel.parTraverse(self)(f)

def parSequence[M[_]: Monad, F[_], B](implicit ev: A <:< M[B], P: Parallel[M, F]): M[T[B]] =
Parallel.parSequence(self.asInstanceOf[T[M[B]]])
Copy link
Member Author

Choose a reason for hiding this comment

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

Anyone know a better solution? Feels a little bit dirty.

@LukaJCB LukaJCB changed the title Add Parallel type class (WIP) Add Parallel type class Aug 24, 2017
@LukaJCB
Copy link
Member Author

LukaJCB commented Aug 25, 2017

Summary of what this entails:

Instances for:

  • Semigroup[E] => Either[E, ?] <-> Validated[E,?]
  • (Semigroup[E], Monad[M]) => EitherT[M, E, ?] <-> Nested[M, Validated[E,?]]
  • Parallel[M, F] => OptionT[M, ?] <-> Nested[F, Option, ?]
  • Parallel[M, F] => WriterT[M, A, ?] <-> WriterT[F, A, ?]
  • Parallel[M, F] => Kleisli[M, A, ?] <-> Kleisli[F, A, ?]
  • (Parallel[M, F], Semigroup[E]) => EitherT[M, E, ?] <-> Nested[F, Validated[E, ?]
  • NonEmptyList[A] <-> ZipNonEmptyList[A]
  • NonEmptyVector[A] <-> ZipNonEmptyVector[A]
  • Vector[A] <-> ZipVector[A]
  • Stream[A] <-> ZipStream[A]
  • List[A] <-> ZipList[A]
  • (Parallel[M, F], Alternative[M], Alternative[F]) => OneAnd[M, ?] <-> OneAnd[F, ?] (this is really just for NonEmptyStream)
  • Future[A] <-> FailFastFuture[A]

Syntax for:

parTraverse
parSequence
Tuple syntax parMapN

Other notes:

We could also add an instance for List with an Applicative instance that zips, there currently is a newtype ZipList in newts, so either it would have to be defined there, or we would have to pull it inside cats.

@edmundnoble
Copy link
Contributor

I will also transport cats-mtl type classes across parallel.

In cats, you should also have (Parallel[M,F], MonadError[M]) => ApplicativeError[F].

@LukaJCB
Copy link
Member Author

LukaJCB commented Aug 26, 2017

@edmundnoble I've added the instance for it, but now I'm getting failing Serializable Tests, any pointers there? 🤔

It seems, that the instance forApplicativeError[WriterT[Validated[String, ?], ListWrapper[Int], ?], String] is actually the derived one from Parallel[Either[String, ?], Validated[String, ?]] instead of the one given by WriterT.
I don't understand why this doesn't throw an "ambiguous implicits" error.
I'm not sure how implicit resolution for these types of derived instances work, when there's another instance in scope. If anyone had some reading material for this kind of thing, I'd greatly appreciate it.

Otherwise, I'd just delete the ApplicativeError instance, and include it in a follow up PR, this one is already getting very large.

@LukaJCB
Copy link
Member Author

LukaJCB commented Aug 27, 2017

Another open question is about an instance for Future. Currently the standard Applicative instance already executes in parallel, which is certainly nice for a lot of people, but also inconsistent with it's Monad instance. I'm not sure how strict we are with inconsistent Monad/Applicative instances, but changing the current Applicative instance would be a massive breaking change, so I'm thinking we should not do it anytime soon and thus a Parallel instance doesn't really make all that much sense right now. Opinions? :)

def applicative: Applicative[F]

/**
* Natural Transformation from the sequential Monad M[_] to the parallel Applicative F[_].
Copy link
Contributor

Choose a reason for hiding this comment

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

the other way? from the parallel Applicative F[_] to the sequential Monad M[_]

def sequential(implicit M: Monad[M]): F ~> M

/**
* Natural Transformation from the parallel Applicative F[_] to the sequential Monad M[_].
Copy link
Contributor

Choose a reason for hiding this comment

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

also in reverse order

* Some types that form a Monad, are also capable of forming an Applicative that supports parallel composition.
* The Parallel type class allows us to represent this relationship.
*/
trait Parallel[M[_], F[_]] {
Copy link
Contributor

@kailuowang kailuowang Aug 28, 2017

Choose a reason for hiding this comment

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

you want extends Serializable here

@LukaJCB
Copy link
Member Author

LukaJCB commented Aug 28, 2017

Thank you @kailuowang! That fixed the failing tests. :)

@kailuowang
Copy link
Contributor

My opinion over Future is that we shouldn't try to provide more functionalities over it than we currently have. I think users who are already coding at this level of pure FP probably abandoned Future long ago, especially now that we have so many good options for replacement in the cats ecosystem.

@LukaJCB
Copy link
Member Author

LukaJCB commented Aug 28, 2017

Totally agree with you @kailuowang.

CI does not pass the Validated tut tests right now because of ambiguous implicits as I feared before. I 'm not sure why it doesn't fail in the same way in the WriterT and ValidatedTests, but right now importing from cats.implicits._ results in ambiguous implicits for ApplicativeError for `Validated.

We can delete the either derived ApplicativeError instance or the specific one for Validated, or somehow find a way to give the specific instance higher priority. Although I don't really know how we could right now.

@edmundnoble
Copy link
Contributor

The Applicative instance for Future is actually not responsible for the evaluation strategy, seeing as all Futures are already evaluated. That is to say, even if you changed Future's Applicative operations to use flatMap, Applicative is not lazy in its first or second parameter, so it's not even possible to make a sequential Applicative. This is fine if you make the frequently-made assumption that if you're using cats with Future you aren't using side effects, because with Future that's exactly what they are, side effects (not suspendable into Future), and cats doesn't morally care about side effects like we don't care about null.

@kailuowang
Copy link
Contributor

@LukaJCB to avoid the ambiguous implicits, an option that I can think of is to not implicitly provide the ApplicativeX instances through Parallel (which I am not sure about the use cases are anyway), rather, make them available through explicit methods on Parallel. Users will need to explicitly use them if they need parallel execution.

@LukaJCB
Copy link
Member Author

LukaJCB commented Aug 29, 2017

That's an excellent idea, will do! :)

@LukaJCB
Copy link
Member Author

LukaJCB commented Aug 30, 2017

CI is happy again, I'd be fine with merging this without further functionality and deal with e.g. a List/ZipList instance afterwards :)

@LukaJCB
Copy link
Member Author

LukaJCB commented Sep 2, 2017

Now that #1885 is merged, I went ahead and added instances for NEL and NEV :)

@kailuowang kailuowang self-assigned this Oct 11, 2017
val n = if (arity == 1) { "" } else { arity.toString }

val parMap =
if (arity == 1) s"def parMap[F[_], Z](f: (${`A..N`}) => Z)(implicit p: NonEmptyParallel[M, F]): M[Z] = p.flatMap.map($tupleArgs)(f)"
Copy link
Contributor

Choose a reason for hiding this comment

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

is the arity=1 version tested?

block"""
|package cats
|trait ParallelArityFunctions {
- def parMap$arity[M[_], F[_], ${`A..N`}, Z]($fparams)(f: (${`A..N`}) => Z)(implicit p: NonEmptyParallel[M, F]): M[Z] =
Copy link
Contributor

Choose a reason for hiding this comment

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

are these parMap1~22 tested?

Copy link
Member Author

Choose a reason for hiding this comment

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

They're not. Related to #1968

@@ -1,7 +1,7 @@
package cats
package tests

import cats.data.NonEmptyList
import cats.data.{NonEmptyList}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit, no need for the braces here.

@kailuowang kailuowang added this to the 1.0.0-RC1 milestone Oct 13, 2017
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.

Looks great. One suggestion: I’d like the Parallel to require that the functors are isomorphic, not just the pure.

*/
trait NonEmptyParallelLaws[M[_], F[_]] {
def P: NonEmptyParallel[M, F]

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we say that the Functors should be the same. If not it seems pretty hard to reason about how they are related.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes that's a good idea, I'll add a law!

kailuowang
kailuowang previously approved these changes Oct 16, 2017
Copy link
Contributor

@kailuowang kailuowang left a comment

Choose a reason for hiding this comment

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

This is really cool. LGTM

@kailuowang kailuowang mentioned this pull request Oct 16, 2017
12 tasks
@SystemFw
Copy link
Contributor

Just want to reiterate that this is awesome, great work @LukaJCB

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.

Sorry for the review latency!

This looks really great.

@kailuowang kailuowang merged commit 58a9fd6 into typelevel:master Oct 26, 2017
@alexandru
Copy link
Member

Just wanted to drop a line to say that I'm really glad to see this PR merged 🤗

@LukaJCB LukaJCB deleted the add-parallel-class branch October 27, 2017 08:56
@johnynek
Copy link
Contributor

This with the opaque types SIP will be great.

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.

Add a Parallel type class
7 participants