Skip to content

Commit

Permalink
Add parFlatTraverse and sequence
Browse files Browse the repository at this point in the history
  • Loading branch information
Luka Jacobowitz committed Oct 14, 2017
1 parent ad8a7c4 commit 0276415
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 6 deletions.
42 changes: 42 additions & 0 deletions core/src/main/scala/cats/Parallel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,26 @@ object Parallel extends ParallelArityFunctions {
P.sequential(gtb)
}

/**
* Like `Traverse[A].flatTraverse`, but uses the applicative instance
* corresponding to the Parallel instance instead.
*/
def parFlatTraverse[T[_]: Traverse: FlatMap, M[_], F[_], A, B]
(ta: T[A])(f: A => M[T[B]])(implicit P: Parallel[M, F]): M[T[B]] = {
val gtb: F[T[B]] = Traverse[T].flatTraverse(ta)(f andThen P.parallel.apply)(P.applicative, FlatMap[T])
P.sequential(gtb)
}

/**
* Like `Traverse[A].flatSequence`, but uses the applicative instance
* corresponding to the Parallel instance instead.
*/
def parFlatSequence[T[_]: Traverse: FlatMap, M[_], F[_], A]
(tma: T[M[T[A]]])(implicit P: Parallel[M, F]): M[T[A]] = {
val fta: F[T[A]] = Traverse[T].flatTraverse(tma)(P.parallel.apply)(P.applicative, FlatMap[T])
P.sequential(fta)
}

/**
* Like `Foldable[A].sequence_`, but uses the applicative instance
* corresponding to the Parallel instance instead.
Expand Down Expand Up @@ -151,6 +171,28 @@ object Parallel extends ParallelArityFunctions {
P.sequential(gtb)
}


/**
* Like `NonEmptyTraverse[A].nonEmptyFlatTraverse`, but uses the apply instance
* corresponding to the Parallel instance instead.
*/
def parNonEmptyFlatTraverse[T[_]: NonEmptyTraverse: FlatMap, M[_], F[_], A, B]
(ta: T[A])(f: A => M[T[B]])(implicit P: NonEmptyParallel[M, F]): M[T[B]] = {
val gtb: F[T[B]] = NonEmptyTraverse[T].nonEmptyFlatTraverse(ta)(f andThen P.parallel.apply)(P.apply, FlatMap[T])
P.sequential(gtb)
}


/**
* Like `NonEmptyTraverse[A].nonEmptyFlatSequence`, but uses the apply instance
* corresponding to the Parallel instance instead.
*/
def parNonEmptyFlatSequence[T[_]: NonEmptyTraverse: FlatMap, M[_], F[_], A]
(tma: T[M[T[A]]])(implicit P: NonEmptyParallel[M, F]): M[T[A]] = {
val fta: F[T[A]] = NonEmptyTraverse[T].nonEmptyFlatTraverse(tma)(P.parallel.apply)(P.apply, FlatMap[T])
P.sequential(fta)
}

/**
* Like `Reducible[A].nonEmptySequence_`, but uses the apply instance
* corresponding to the Parallel instance instead.
Expand Down
2 changes: 1 addition & 1 deletion tests/src/test/scala/cats/tests/ListTests.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats
package tests

import cats.data.{NonEmptyList}
import cats.data.NonEmptyList
import cats.laws.discipline.{TraverseTests, CoflatMapTests, AlternativeTests, SerializableTests, CartesianTests}
import cats.laws.discipline.arbitrary._

Expand Down
38 changes: 33 additions & 5 deletions tests/src/test/scala/cats/tests/ParallelTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,59 @@ class ParallelTests extends CatsSuite with ApplicativeErrorForEitherTest {
case Left(e) => e
}.foldMap(identity)

(es.parSequence.fold(identity, i => Monoid[String].empty)) should === (lefts)
es.parSequence.fold(identity, i => Monoid[String].empty) should === (lefts)
}
}

test("ParTraverse identity should be equivalent to parSequence") {
forAll { es: List[Either[String, Int]] =>
(es.parTraverse(identity)) should === (es.parSequence)
es.parTraverse(identity) should === (es.parSequence)
}
}

test("ParTraverse_ identity should be equivalent to parSequence_") {
forAll { es: Set[Either[String, Int]] =>
(Parallel.parTraverse_(es)(identity)) should === (Parallel.parSequence_(es))
Parallel.parTraverse_(es)(identity) should === (Parallel.parSequence_(es))
}
}

test("ParNonEmptyTraverse identity should be equivalent to parNonEmptySequence") {
forAll { es: NonEmptyVector[Either[String, Int]] =>
(Parallel.parNonEmptyTraverse(es)(identity)) should === (Parallel.parNonEmptySequence(es))
Parallel.parNonEmptyTraverse(es)(identity) should === (Parallel.parNonEmptySequence(es))
}
}

test("ParNonEmptyTraverse_ identity should be equivalent to parNonEmptySequence_") {
forAll { es: NonEmptyList[Either[String, Int]] =>
(Parallel.parNonEmptyTraverse_(es)(identity)) should === (Parallel.parNonEmptySequence_(es))
Parallel.parNonEmptyTraverse_(es)(identity) should === (Parallel.parNonEmptySequence_(es))
}
}

test("ParFlatTraverse should be equivalent to parTraverse map flatten") {
forAll { es: List[Either[String, Int]] =>
val f: Int => List[Int] = i => List(i, i + 1)
Parallel.parFlatTraverse(es)(e => e.map(f)) should
=== (es.parTraverse(e => e.map(f)).map(_.flatten))
}
}

test("ParFlatTraverse identity should be equivalent to parFlatSequence") {
forAll { es: List[Either[String, List[Int]]] =>
Parallel.parFlatTraverse(es)(identity) should === (Parallel.parFlatSequence(es))
}
}

test("ParNonEmptyFlatTraverse should be equivalent to parNonEmptyTraverse map flatten") {
forAll { es: NonEmptyList[Either[String, Int]] =>
val f: Int => NonEmptyList[Int] = i => NonEmptyList.of(i, i + 1)
Parallel.parNonEmptyFlatTraverse(es)(e => e.map(f)) should
=== (Parallel.parNonEmptyTraverse(es)(e => e.map(f)).map(_.flatten))
}
}

test("ParNonEmptyFlatTraverse identity should be equivalent to parNonEmptyFlatSequence") {
forAll { es: NonEmptyList[Either[String, NonEmptyList[Int]]] =>
Parallel.parNonEmptyFlatTraverse(es)(identity) should === (Parallel.parNonEmptyFlatSequence(es))
}
}

Expand Down

0 comments on commit 0276415

Please sign in to comment.