From 284b1831285bb6b69500d222d6ec199f491d98a9 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Tue, 14 Aug 2018 14:23:16 +0300 Subject: [PATCH 1/4] Add Compose instance for Map --- core/src/main/scala/cats/instances/map.scala | 15 +++++++++++++++ tests/src/test/scala/cats/tests/MapSuite.scala | 4 +++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/instances/map.scala b/core/src/main/scala/cats/instances/map.scala index 573ce85556..0780d8acd6 100644 --- a/core/src/main/scala/cats/instances/map.scala +++ b/core/src/main/scala/cats/instances/map.scala @@ -4,6 +4,7 @@ package instances import cats.kernel.CommutativeMonoid import scala.annotation.tailrec +import cats.arrow.Compose trait MapInstances extends cats.kernel.instances.MapInstances { @@ -79,4 +80,18 @@ trait MapInstances extends cats.kernel.instances.MapInstances { } // scalastyle:on method.length + + implicit def catsStdComposeForMap: Compose[Map] = new Compose[Map] { + + def compose[A, B, C](f: Map[B, C], g: Map[A, B]): Map[A, C] = { + g.foldLeft(Map.empty[A, C]) { + case (acc, (key, value)) => + f.get(value) match { + case Some(other) => acc + (key -> other) + case _ => acc + } + } + } + + } } diff --git a/tests/src/test/scala/cats/tests/MapSuite.scala b/tests/src/test/scala/cats/tests/MapSuite.scala index 9be630d7b5..77846cf6ba 100644 --- a/tests/src/test/scala/cats/tests/MapSuite.scala +++ b/tests/src/test/scala/cats/tests/MapSuite.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.laws.discipline.{FlatMapTests, SemigroupalTests, SerializableTests, UnorderedTraverseTests} +import cats.laws.discipline.{FlatMapTests, SemigroupalTests, SerializableTests, UnorderedTraverseTests, ComposeTests} class MapSuite extends CatsSuite { implicit val iso = SemigroupalTests.Isomorphisms.invariant[Map[Int, ?]] @@ -15,6 +15,8 @@ class MapSuite extends CatsSuite { checkAll("Map[Int, Int] with Option", UnorderedTraverseTests[Map[Int, ?]].unorderedTraverse[Int, Int, Int, Option, Option]) checkAll("UnorderedTraverse[Map[Int, ?]]", SerializableTests.serializable(UnorderedTraverse[Map[Int, ?]])) + checkAll("Compose[Map]", ComposeTests[Map].compose[Int, Long, String, Double]) + test("show isn't empty and is formatted as expected") { forAll { (map: Map[Int, String]) => From c6abd503eaacd44378a5d0380155f61617cea823 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Tue, 14 Aug 2018 16:03:27 +0300 Subject: [PATCH 2/4] Move Compose instance for Map to separate trait Fix binary compatibility issues by moving the new Compose instance for Map to a separate trait. --- core/src/main/scala/cats/implicits.scala | 1 + core/src/main/scala/cats/instances/all.scala | 3 +++ core/src/main/scala/cats/instances/map.scala | 5 +++++ core/src/main/scala/cats/instances/package.scala | 4 ++-- testkit/src/main/scala/cats/tests/CatsSuite.scala | 4 ++-- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/cats/implicits.scala b/core/src/main/scala/cats/implicits.scala index 350fc9fb9b..d89f1f1d46 100644 --- a/core/src/main/scala/cats/implicits.scala +++ b/core/src/main/scala/cats/implicits.scala @@ -7,3 +7,4 @@ object implicits with syntax.AllSyntaxBinCompat2 with instances.AllInstances with instances.AllInstancesBinCompat0 + with instances.AllInstancesBinCompat1 diff --git a/core/src/main/scala/cats/instances/all.scala b/core/src/main/scala/cats/instances/all.scala index d8c3361fa2..5d82c6d715 100644 --- a/core/src/main/scala/cats/instances/all.scala +++ b/core/src/main/scala/cats/instances/all.scala @@ -36,3 +36,6 @@ trait AllInstances trait AllInstancesBinCompat0 extends FunctionInstancesBinCompat0 with Tuple2InstancesBinCompat0 + +trait AllInstancesBinCompat1 + extends MapInstancesBinCompat0 diff --git a/core/src/main/scala/cats/instances/map.scala b/core/src/main/scala/cats/instances/map.scala index 0780d8acd6..1fbf81a559 100644 --- a/core/src/main/scala/cats/instances/map.scala +++ b/core/src/main/scala/cats/instances/map.scala @@ -81,6 +81,10 @@ trait MapInstances extends cats.kernel.instances.MapInstances { } // scalastyle:on method.length +} + +trait MapInstancesBinCompat0 { + implicit def catsStdComposeForMap: Compose[Map] = new Compose[Map] { def compose[A, B, C](f: Map[B, C], g: Map[A, B]): Map[A, C] = { @@ -94,4 +98,5 @@ trait MapInstances extends cats.kernel.instances.MapInstances { } } + } diff --git a/core/src/main/scala/cats/instances/package.scala b/core/src/main/scala/cats/instances/package.scala index 13f70fc8ee..f398b3ad65 100644 --- a/core/src/main/scala/cats/instances/package.scala +++ b/core/src/main/scala/cats/instances/package.scala @@ -1,7 +1,7 @@ package cats package object instances { - object all extends AllInstances with AllInstancesBinCompat0 + object all extends AllInstances with AllInstancesBinCompat0 with AllInstancesBinCompat1 object bigInt extends BigIntInstances object bigDecimal extends BigDecimalInstances object bitSet extends BitSetInstances @@ -21,7 +21,7 @@ package object instances { object invariant extends InvariantMonoidalInstances object list extends ListInstances object long extends LongInstances - object map extends MapInstances + object map extends MapInstances with MapInstancesBinCompat0 object option extends OptionInstances object order extends OrderInstances object ordering extends OrderingInstances diff --git a/testkit/src/main/scala/cats/tests/CatsSuite.scala b/testkit/src/main/scala/cats/tests/CatsSuite.scala index 617712c2b6..fac621f4dd 100644 --- a/testkit/src/main/scala/cats/tests/CatsSuite.scala +++ b/testkit/src/main/scala/cats/tests/CatsSuite.scala @@ -2,7 +2,7 @@ package cats package tests import catalysts.Platform -import cats.instances.{AllInstances, AllInstancesBinCompat0} +import cats.instances.{AllInstances, AllInstancesBinCompat0, AllInstancesBinCompat1} import cats.syntax.{AllSyntax, AllSyntaxBinCompat0, AllSyntaxBinCompat1, AllSyntaxBinCompat2, EqOps} import org.scalactic.anyvals.{PosInt, PosZDouble, PosZInt} import org.scalatest.{FunSuite, FunSuiteLike, Matchers} @@ -33,7 +33,7 @@ trait CatsSuite extends FunSuite with GeneratorDrivenPropertyChecks with Discipline with TestSettings - with AllInstances with AllInstancesBinCompat0 + with AllInstances with AllInstancesBinCompat0 with AllInstancesBinCompat1 with AllSyntax with AllSyntaxBinCompat0 with AllSyntaxBinCompat1 with AllSyntaxBinCompat2 with StrictCatsEquality { self: FunSuiteLike => From 9ffd73901281ca8db9a8be5911a131cb7fda5faa Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 15 Aug 2018 11:35:29 +0300 Subject: [PATCH 3/4] Serialization test for Compose instance for Map --- tests/src/test/scala/cats/tests/MapSuite.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/src/test/scala/cats/tests/MapSuite.scala b/tests/src/test/scala/cats/tests/MapSuite.scala index 77846cf6ba..53fdb73b75 100644 --- a/tests/src/test/scala/cats/tests/MapSuite.scala +++ b/tests/src/test/scala/cats/tests/MapSuite.scala @@ -1,6 +1,7 @@ package cats package tests +import cats.arrow.Compose import cats.laws.discipline.{FlatMapTests, SemigroupalTests, SerializableTests, UnorderedTraverseTests, ComposeTests} class MapSuite extends CatsSuite { @@ -15,7 +16,8 @@ class MapSuite extends CatsSuite { checkAll("Map[Int, Int] with Option", UnorderedTraverseTests[Map[Int, ?]].unorderedTraverse[Int, Int, Int, Option, Option]) checkAll("UnorderedTraverse[Map[Int, ?]]", SerializableTests.serializable(UnorderedTraverse[Map[Int, ?]])) - checkAll("Compose[Map]", ComposeTests[Map].compose[Int, Long, String, Double]) + checkAll("Map[Int, Long]", ComposeTests[Map].compose[Int, Long, String, Double]) + checkAll("Compose[Map]", SerializableTests.serializable(Compose[Map])) test("show isn't empty and is formatted as expected") { From d8ee4bb367e36b07ad7fc2877fa9bbd1d2c49ae1 Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Wed, 15 Aug 2018 23:19:48 +0300 Subject: [PATCH 4/4] Add doctest for Compose[Map] --- core/src/main/scala/cats/instances/map.scala | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/instances/map.scala b/core/src/main/scala/cats/instances/map.scala index 1fbf81a559..dd044faf36 100644 --- a/core/src/main/scala/cats/instances/map.scala +++ b/core/src/main/scala/cats/instances/map.scala @@ -85,8 +85,19 @@ trait MapInstances extends cats.kernel.instances.MapInstances { trait MapInstancesBinCompat0 { - implicit def catsStdComposeForMap: Compose[Map] = new Compose[Map] { - + implicit val catsStdComposeForMap: Compose[Map] = new Compose[Map] { + + /** + * Compose two maps `g` and `f` by using the values in `f` as keys for `g`. + * {{{ + * scala> import cats.arrow.Compose + * scala> import cats.implicits._ + * scala> val first = Map(1 -> "a", 2 -> "b", 3 -> "c", 4 -> "a") + * scala> val second = Map("a" -> true, "b" -> false, "d" -> true) + * scala> Compose[Map].compose(second, first) + * res0: Map[Int, Boolean] = Map(1 -> true, 2 -> false, 4 -> true) + * }}} + */ def compose[A, B, C](f: Map[B, C], g: Map[A, B]): Map[A, C] = { g.foldLeft(Map.empty[A, C]) { case (acc, (key, value)) =>