-
-
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 more Parallel instances #1938
Changes from 72 commits
eaf34e6
21942c5
b37732b
664988a
e7f6f68
f37500a
447d929
4259554
6c5efc9
d7ba338
a7bea82
1556909
b3470db
b458b3d
2815a5b
7844788
378549f
1490fe4
2dc71fe
00664ae
f941a0c
1236205
17c0bc0
1502087
f1b1c1a
9252e36
e8eb35d
1a99aab
ae03b33
11caba0
1027a41
942a152
6354e08
26b7930
9e3891d
4dbc995
61b7cc7
50c5732
c661860
ccc5f45
3a300b4
ecc8e50
4882401
50c9619
db973c9
615a1a5
7395c0a
a8afdfe
7f91072
c5e3423
15d9a45
ad8a7c4
71a1239
eb274cf
4151e7e
87f7b87
c956900
4a9f2ad
e7f659e
17acb6f
7a6cd52
c67c953
cca85ea
ddb2457
8dcfc4a
bcad984
782d568
4bbd46e
0c87a1b
e9ab6b4
7074f8f
d1eeb55
9114b2a
8bfec4b
5f6ef8c
6c22f90
0e752e3
2ca66c9
c92a34a
15def60
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 |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package cats.data | ||
|
||
import cats.Applicative | ||
|
||
import scala.concurrent.{ExecutionContext, Future, Promise} | ||
import scala.util.{Failure, Success} | ||
|
||
class FailFastFuture[A](val value: Future[A]) extends AnyVal | ||
|
||
object FailFastFuture { | ||
|
||
def apply[A](value: Future[A]): FailFastFuture[A] = new FailFastFuture(value) | ||
|
||
def catsDataApplicativeForFailFastFuture(implicit ec: ExecutionContext): Applicative[FailFastFuture] = | ||
new Applicative[FailFastFuture] { | ||
override def pure[A](x: A): FailFastFuture[A] = FailFastFuture(Future.successful(x)) | ||
|
||
override def ap[A, B](ff: FailFastFuture[(A) => B])(fa: FailFastFuture[A]): FailFastFuture[B] = { | ||
val p = Promise[B]() | ||
|
||
ff.value.onComplete { | ||
case Failure(t) => p.tryFailure(t) | ||
case Success(_) => () | ||
} | ||
|
||
fa.value.onComplete { | ||
case Failure(t) => p.tryFailure(t) | ||
case Success(_) => () | ||
} | ||
|
||
p.tryCompleteWith(ff.value.flatMap(f => fa.value.map(f))) | ||
FailFastFuture(p.future) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package cats.data | ||
|
||
import cats.{CommutativeApply, Eq} | ||
import cats.instances.list.catsKernelStdEqForList | ||
|
||
class ZipList[A](val value: List[A]) extends AnyVal | ||
|
||
object ZipList { | ||
|
||
def apply[A](value: List[A]): ZipList[A] = new ZipList(value) | ||
|
||
implicit val catsDataCommutativeApplyForZipList: CommutativeApply[ZipList] = new CommutativeApply[ZipList] { | ||
|
||
override def map[A, B](fa: ZipList[A])(f: (A) => B): ZipList[B] = | ||
ZipList(fa.value.map(f)) | ||
|
||
def ap[A, B](ff: ZipList[A => B])(fa: ZipList[A]): ZipList[B] = | ||
ZipList((ff.value, fa.value).zipped.map(_ apply _)) | ||
|
||
override def product[A, B](fa: ZipList[A], fb: ZipList[B]): ZipList[(A, B)] = | ||
ZipList(fa.value.zip(fb.value)) | ||
|
||
} | ||
|
||
implicit def catsDataEqForZipList[A: Eq]: Eq[ZipList[A]] = Eq.by(_.value) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package cats.data | ||
|
||
import cats.{Alternative, CommutativeApplicative, Eq} | ||
import cats.instances.stream._ | ||
|
||
class ZipStream[A](val value: Stream[A]) extends AnyVal | ||
|
||
object ZipStream { | ||
|
||
def apply[A](value: Stream[A]): ZipStream[A] = new ZipStream(value) | ||
|
||
implicit val catsDataAlternativeForZipStream: Alternative[ZipStream] with CommutativeApplicative[ZipStream] = | ||
new Alternative[ZipStream] with CommutativeApplicative[ZipStream] { | ||
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 should be lawful right? but we can't test it...that's awkward. how about moving it to alleycats? 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. Awkward indeed, I'm not sure if alleycats is the right place, since they can be proven to be lawful in e.g. Haskell, but I agree that it's awkward. Maybe we should move the 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 thought about that but it would cause implicit conflict if you import both right? Unless we do the export hook trick which is kind of deprecated. Maybe we just document a bit more. |
||
def pure[A](x: A): ZipStream[A] = new ZipStream(Stream.continually(x)) | ||
|
||
override def map[A, B](fa: ZipStream[A])(f: (A) => B): ZipStream[B] = | ||
ZipStream(fa.value.map(f)) | ||
|
||
def ap[A, B](ff: ZipStream[A => B])(fa: ZipStream[A]): ZipStream[B] = | ||
ZipStream((ff.value, fa.value).zipped.map(_ apply _)) | ||
|
||
override def product[A, B](fa: ZipStream[A], fb: ZipStream[B]): ZipStream[(A, B)] = | ||
ZipStream(fa.value.zip(fb.value)) | ||
|
||
def empty[A]: ZipStream[A] = ZipStream(Stream.empty[A]) | ||
|
||
def combineK[A](x: ZipStream[A], y: ZipStream[A]): ZipStream[A] = | ||
ZipStream(Alternative[Stream].combineK(x.value, y.value)) | ||
} | ||
|
||
implicit def catsDataEqForZipStream[A: Eq]: Eq[ZipStream[A]] = Eq.by(_.value) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package cats.data | ||
|
||
import cats.{CommutativeApply, Eq} | ||
import cats.instances.vector._ | ||
|
||
class ZipVector[A](val value: Vector[A]) extends AnyVal | ||
|
||
object ZipVector { | ||
|
||
def apply[A](value: Vector[A]): ZipVector[A] = new ZipVector(value) | ||
|
||
implicit val catsDataCommutativeApplyForZipVector: CommutativeApply[ZipVector] = new CommutativeApply[ZipVector] { | ||
|
||
override def map[A, B](fa: ZipVector[A])(f: (A) => B): ZipVector[B] = | ||
ZipVector(fa.value.map(f)) | ||
def ap[A, B](ff: ZipVector[A => B])(fa: ZipVector[A]): ZipVector[B] = | ||
ZipVector((ff.value, fa.value).zipped.map(_ apply _)) | ||
|
||
} | ||
|
||
implicit def catsDataEqForZipVector[A: Eq]: Eq[ZipVector[A]] = Eq.by(_.value) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,9 @@ package cats.instances | |
import cats.data._ | ||
import cats.kernel.Semigroup | ||
import cats.syntax.either._ | ||
import cats.{Applicative, Functor, Monad, Parallel, ~>} | ||
import cats.{Applicative, Apply, FlatMap, Functor, Monad, NonEmptyParallel, Parallel, ~>} | ||
|
||
import scala.concurrent.{ExecutionContext, Future} | ||
|
||
trait ParallelInstances extends ParallelInstances1 { | ||
implicit def catsParallelForEitherValidated[E: Semigroup]: Parallel[Either[E, ?], Validated[E, ?]] = new Parallel[Either[E, ?], Validated[E, ?]] { | ||
|
@@ -36,6 +38,58 @@ trait ParallelInstances extends ParallelInstances1 { | |
λ[OptionT[M, ?] ~> Nested[F, Option, ?]](optT => Nested(P.parallel(optT.value))) | ||
} | ||
|
||
implicit def catsStdNonEmptyParallelForZipList[A]: NonEmptyParallel[List, ZipList] = | ||
new NonEmptyParallel[List, ZipList] { | ||
|
||
def flatMap: FlatMap[List] = cats.instances.list.catsStdInstancesForList | ||
def apply: Apply[ZipList] = ZipList.catsDataCommutativeApplyForZipList | ||
|
||
def sequential: ZipList ~> List = | ||
λ[ZipList ~> List](_.value) | ||
|
||
def parallel: List ~> ZipList = | ||
λ[List ~> ZipList](v => new ZipList(v)) | ||
} | ||
|
||
implicit def catsStdNonEmptyParallelForZipVector[A]: NonEmptyParallel[Vector, ZipVector] = | ||
new NonEmptyParallel[Vector, ZipVector] { | ||
|
||
def flatMap: FlatMap[Vector] = cats.instances.vector.catsStdInstancesForVector | ||
def apply: Apply[ZipVector] = ZipVector.catsDataCommutativeApplyForZipVector | ||
|
||
def sequential: ZipVector ~> Vector = | ||
λ[ZipVector ~> Vector](_.value) | ||
|
||
def parallel: Vector ~> ZipVector = | ||
λ[Vector ~> ZipVector](v => new ZipVector(v)) | ||
} | ||
|
||
implicit def catsStdParallelForZipStream[A]: Parallel[Stream, ZipStream] = | ||
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 idea as above, if we can't test it, maybe moving to alleycats? |
||
new Parallel[Stream, ZipStream] { | ||
|
||
def monad: Monad[Stream] = cats.instances.stream.catsStdInstancesForStream | ||
def applicative: Applicative[ZipStream] = ZipStream.catsDataAlternativeForZipStream | ||
|
||
def sequential: ZipStream ~> Stream = | ||
λ[ZipStream ~> Stream](_.value) | ||
|
||
def parallel: Stream ~> ZipStream = | ||
λ[Stream ~> ZipStream](v => new ZipStream(v)) | ||
} | ||
|
||
implicit def catsStdParallelForFailFastFuture[A](implicit ec: ExecutionContext): Parallel[Future, FailFastFuture] = | ||
new Parallel[Future, FailFastFuture] { | ||
|
||
def monad: Monad[Future] = cats.instances.future.catsStdInstancesForFuture | ||
def applicative: Applicative[FailFastFuture] = FailFastFuture.catsDataApplicativeForFailFastFuture | ||
|
||
def sequential: FailFastFuture ~> Future = | ||
λ[FailFastFuture ~> Future](_.value) | ||
|
||
def parallel: Future ~> FailFastFuture = | ||
λ[Future ~> FailFastFuture](f => FailFastFuture(f)) | ||
} | ||
|
||
|
||
implicit def catsParallelForEitherTNestedParallelValidated[F[_], M[_], E: Semigroup] | ||
(implicit P: Parallel[M, F]): Parallel[EitherT[M, E, ?], Nested[F, Validated[E, ?], ?]] = | ||
|
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.
shall we also test the Applicative laws on this? Also want to link @tpolecat's comment here suggesting it should limit to
CommutativeApply
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.
Just tested, this doesn't seem to pass the
ApplicativeLaw
.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.
What law does it fail?
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.
Several of them
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'll try to see if it might be a valid
Apply
instead.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.
FastFuture.applicative.ap consistent with product + map *** FAILED ***
FastFuture.applicative.apply composition *** FAILED ***
FastFuture.applicative.followedBy consistent map2 *** FAILED ***
FastFuture.applicative.forEffect consistent map2 *** FAILED ***
FastFuture.applicative.map2/map2Eval consistency *** FAILED ***
FastFuture.applicative.map2/product-map consistency *** FAILED ***
FastFuture.applicative.semigroupal associativity *** FAILED ***