From fcb429da3ff23654f982be9f8fde33c7dc393628 Mon Sep 17 00:00:00 2001 From: Luka Jacobowitz Date: Sat, 30 Sep 2017 20:27:22 +0200 Subject: [PATCH] Add traverseUnordered for Set --- core/src/main/scala/cats/data/Nested.scala | 16 +++++++++- core/src/main/scala/cats/data/Tuple2K.scala | 20 +++++++++++- .../discipline/CommutativeApplyTests.scala | 2 +- .../test/scala/cats/tests/NestedTests.scala | 13 ++++++++ .../src/test/scala/cats/tests/SetTests.scala | 32 +++++++++++++++++++ .../test/scala/cats/tests/Tuple2KTests.scala | 12 ++++++- 6 files changed, 91 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/cats/data/Nested.scala b/core/src/main/scala/cats/data/Nested.scala index 956ec67773a..4ff3a519206 100644 --- a/core/src/main/scala/cats/data/Nested.scala +++ b/core/src/main/scala/cats/data/Nested.scala @@ -135,13 +135,27 @@ private[data] sealed abstract class NestedInstances9 extends NestedInstances10 { } } -private[data] sealed abstract class NestedInstances10 { +private[data] sealed abstract class NestedInstances10 extends NestedInstances11 { 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, ?]] = + new NestedApply[F, G] with CommutativeApply[Nested[F, G, ?]] { + val FG: Apply[λ[α => F[G[α]]]] = Apply[F].compose[G] + } +} + +private[data] sealed abstract class NestedInstances12 { + implicit def catsDataCommutativeApplicativeForNestedContravariant[F[_]: CommutativeApplicative, G[_]: CommutativeApplicative]: CommutativeApplicative[Nested[F, G, ?]] = + new NestedApplicative[F, G] with CommutativeApplicative[Nested[F, G, ?]] { + val FG: Applicative[λ[α => F[G[α]]]] = Applicative[F].compose[G] + } +} + private[data] trait NestedInvariant[F[_], G[_]] extends Invariant[Nested[F, G, ?]] { def FG: Invariant[λ[α => F[G[α]]]] diff --git a/core/src/main/scala/cats/data/Tuple2K.scala b/core/src/main/scala/cats/data/Tuple2K.scala index 28a40bbc8eb..633c0030831 100644 --- a/core/src/main/scala/cats/data/Tuple2K.scala +++ b/core/src/main/scala/cats/data/Tuple2K.scala @@ -78,13 +78,31 @@ private[data] sealed abstract class Tuple2KInstances4 extends Tuple2KInstances5 } } -private[data] sealed abstract class Tuple2KInstances5 { +private[data] sealed abstract class Tuple2KInstances5 extends Tuple2KInstances6 { implicit def catsDataFunctorForTuple2K[F[_], G[_]](implicit FF: Functor[F], GG: Functor[G]): Functor[λ[α => Tuple2K[F, G, α]]] = new Tuple2KFunctor[F, G] { def F: Functor[F] = FF def G: Functor[G] = GG } } + +private[data] sealed abstract class Tuple2KInstances6 extends Tuple2KInstances7 { + + implicit def catsDataCommutativeApplyForTuple2K[F[_], G[_]](implicit FF: CommutativeApply[F], GG: CommutativeApply[G]): CommutativeApply[λ[α => Tuple2K[F, G, α]]] = + new Tuple2KApply[F, G] with CommutativeApply[λ[α => Tuple2K[F, G, α]]] { + def F: Apply[F] = FF + def G: Apply[G] = GG + } +} + +private[data] sealed abstract class Tuple2KInstances7 { + implicit def catsDataCommutativeApplicativeForTuple2K[F[_], G[_]](implicit FF: CommutativeApplicative[F], GG: CommutativeApplicative[G]): CommutativeApplicative[λ[α => Tuple2K[F, G, α]]] = + new Tuple2KApplicative[F, G] with CommutativeApplicative[λ[α => Tuple2K[F, G, α]]] { + def F: Applicative[F] = FF + def G: Applicative[G] = GG + } +} + private[data] sealed trait Tuple2KFunctor[F[_], G[_]] extends Functor[λ[α => Tuple2K[F, G, α]]] { def F: Functor[F] def G: Functor[G] diff --git a/laws/src/main/scala/cats/laws/discipline/CommutativeApplyTests.scala b/laws/src/main/scala/cats/laws/discipline/CommutativeApplyTests.scala index 4946a90f64f..cd272ffd414 100644 --- a/laws/src/main/scala/cats/laws/discipline/CommutativeApplyTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/CommutativeApplyTests.scala @@ -38,7 +38,7 @@ trait CommutativeApplyTests[F[_]] extends ApplyTests[F] { } object CommutativeApplyTests { - def apply[F[_]: CommutativeFlatMap]: CommutativeApplyTests[F] = + def apply[F[_]: CommutativeApply]: CommutativeApplyTests[F] = new CommutativeApplyTests[F] { def laws: CommutativeApplyLaws[F] = CommutativeApplyLaws[F] } diff --git a/tests/src/test/scala/cats/tests/NestedTests.scala b/tests/src/test/scala/cats/tests/NestedTests.scala index b8481377655..59d1a8f37f8 100644 --- a/tests/src/test/scala/cats/tests/NestedTests.scala +++ b/tests/src/test/scala/cats/tests/NestedTests.scala @@ -70,6 +70,12 @@ class NestedTests extends CatsSuite { checkAll("Apply[Nested[List, ListWrapper, ?]]", SerializableTests.serializable(Apply[Nested[List, ListWrapper, ?]])) } + { + // CommutativeApply composition + checkAll("Nested[Option, Validated[Int, ?], ?]", CommutativeApplyTests[Nested[Option, Validated[Int, ?], ?]].commutativeApply[Int, Int, Int]) + checkAll("CommutativeApply[Nested[List, ListWrapper, ?]]", SerializableTests.serializable(CommutativeApply[Nested[Option, Validated[Int, ?], ?]])) + } + { // Applicative composition implicit val instance = ListWrapper.applicative @@ -77,6 +83,13 @@ class NestedTests extends CatsSuite { checkAll("Applicative[Nested[List, ListWrapper, ?]]", SerializableTests.serializable(Applicative[Nested[List, ListWrapper, ?]])) } + { + // CommutativeApplicative composition + implicit val instance = ListWrapper.applicative + checkAll("Nested[Option, Validated[Int, ?], ?]", CommutativeApplicativeTests[Nested[Option, Validated[Int, ?], ?]].commutativeApplicative[Int, Int, Int]) + checkAll("CommutativeApplicative[Nested[List, ListWrapper, ?]]", SerializableTests.serializable(CommutativeApplicative[Nested[Option, Validated[Int, ?], ?]])) + } + { //ApplicativeError composition implicit val instance = ListWrapper.applicative diff --git a/tests/src/test/scala/cats/tests/SetTests.scala b/tests/src/test/scala/cats/tests/SetTests.scala index 3dfb7055824..74155eaddeb 100644 --- a/tests/src/test/scala/cats/tests/SetTests.scala +++ b/tests/src/test/scala/cats/tests/SetTests.scala @@ -1,6 +1,7 @@ package cats package tests +import cats.data.{Nested, Tuple2K} import cats.laws.discipline.{FoldableTests, MonoidKTests, SerializableTests} class SetTests extends CatsSuite { @@ -21,6 +22,37 @@ class SetTests extends CatsSuite { } } + test("traverseUnordered identity") { + forAll { (si: Set[Int], f: Int => String) => + CommutativeApplicative.traverseUnordered[Id, Int, String](si)(f) should === (si.map(f)) + } + } + + test("traverseUnordered sequential composition") { + forAll { (si: Set[Int], f: Int => Option[String], g: String => Option[Int]) => + val lhs = Nested(CommutativeApplicative.traverseUnordered(si)(f).map(ss => CommutativeApplicative.traverseUnordered(ss)(g))) + val rhs = CommutativeApplicative.traverseUnordered(si)(i => Nested(f(i).map(g))) + lhs should === (rhs) + } + } + + test("traverseUnordered parallel composition") { + forAll { (si: Set[Int], f: Int => Option[String], g: Int => Option[String]) => + type Two[A] = Tuple2K[Option, Option, A] + + val lhs = CommutativeApplicative.traverseUnordered(si)(i => Tuple2K(f(i), g(i))) + val rhs = Tuple2K(CommutativeApplicative.traverseUnordered(si)(f), CommutativeApplicative.traverseUnordered(si)(g)) + lhs should ===(rhs) + } + } + + test("traverseUnordered consistent with sequenceUnordered") { + forAll { (si: Set[Int], f: Int => String) => + CommutativeApplicative.traverseUnordered(si)(i => f(i).valid[Int]) should + === (CommutativeApplicative.sequenceUnordered(si.map(i => f(i).valid[Int]))) + } + } + test("show keeps separate entries for items that map to identical strings"){ //note: this val name has to be the same to shadow the cats.instances instance implicit val catsStdShowForInt: Show[Int] = Show.show(_ => "1") diff --git a/tests/src/test/scala/cats/tests/Tuple2KTests.scala b/tests/src/test/scala/cats/tests/Tuple2KTests.scala index fa041999fae..b69f383571d 100644 --- a/tests/src/test/scala/cats/tests/Tuple2KTests.scala +++ b/tests/src/test/scala/cats/tests/Tuple2KTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.data.Tuple2K +import cats.data.{Tuple2K, Validated} import cats.functor.Contravariant import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ @@ -40,6 +40,16 @@ class Tuple2KTests extends CatsSuite { checkAll("Apply[Tuple2K[ListWrapper, ListWrapper, ?]]", SerializableTests.serializable(Apply[Tuple2K[ListWrapper, ListWrapper, ?]])) } + { + checkAll("Tuple2K[Option, Validated[Int, ?], ?]", CommutativeApplyTests[Tuple2K[Option, Validated[Int, ?], ?]].commutativeApply[Int, Int, Int]) + checkAll("Apply[Tuple2K[Option, Validated[Int, ?], ?]]", SerializableTests.serializable(CommutativeApply[Tuple2K[Option, Validated[Int, ?], ?]])) + } + + { + checkAll("Tuple2K[Option, Validated[Int, ?], ?]", CommutativeApplicativeTests[Tuple2K[Option, Validated[Int, ?], ?]].commutativeApplicative[Int, Int, Int]) + checkAll("Applicative[Tuple2K[Option, Validated[Int, ?], ?]]", SerializableTests.serializable(CommutativeApplicative[Tuple2K[Option, Validated[Int, ?], ?]])) + } + { implicit val functor = ListWrapper.functor checkAll("Tuple2K[ListWrapper, ListWrapper, ?]", FunctorTests[Tuple2K[ListWrapper, ListWrapper, ?]].functor[Int, Int, Int])