From dce315d07bc76ed1ad35a18db55b65b5995b66ae Mon Sep 17 00:00:00 2001 From: Denis Rosca Date: Thu, 16 Aug 2018 08:53:45 +0300 Subject: [PATCH] Add Compose instance for Map (#2402) * Add Compose instance for Map * Move Compose instance for Map to separate trait Fix binary compatibility issues by moving the new Compose instance for Map to a separate trait. * Serialization test for Compose instance for Map * Add doctest for Compose[Map] --- 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 | 31 +++++++++++++++++++ .../main/scala/cats/instances/package.scala | 4 +-- .../src/main/scala/cats/tests/CatsSuite.scala | 4 +-- .../src/test/scala/cats/tests/MapSuite.scala | 6 +++- 6 files changed, 44 insertions(+), 5 deletions(-) diff --git a/core/src/main/scala/cats/implicits.scala b/core/src/main/scala/cats/implicits.scala index 350fc9fb9bd..d89f1f1d468 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 d8c3361fa2c..5d82c6d715c 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 573ce855569..dd044faf363 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,34 @@ trait MapInstances extends cats.kernel.instances.MapInstances { } // scalastyle:on method.length + +} + +trait MapInstancesBinCompat0 { + + 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)) => + f.get(value) match { + case Some(other) => acc + (key -> other) + case _ => acc + } + } + } + + } + } diff --git a/core/src/main/scala/cats/instances/package.scala b/core/src/main/scala/cats/instances/package.scala index 13f70fc8ee3..f398b3ad650 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 617712c2b61..fac621f4dd4 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 => diff --git a/tests/src/test/scala/cats/tests/MapSuite.scala b/tests/src/test/scala/cats/tests/MapSuite.scala index 9be630d7b55..53fdb73b753 100644 --- a/tests/src/test/scala/cats/tests/MapSuite.scala +++ b/tests/src/test/scala/cats/tests/MapSuite.scala @@ -1,7 +1,8 @@ package cats package tests -import cats.laws.discipline.{FlatMapTests, SemigroupalTests, SerializableTests, UnorderedTraverseTests} +import cats.arrow.Compose +import cats.laws.discipline.{FlatMapTests, SemigroupalTests, SerializableTests, UnorderedTraverseTests, ComposeTests} class MapSuite extends CatsSuite { implicit val iso = SemigroupalTests.Isomorphisms.invariant[Map[Int, ?]] @@ -15,6 +16,9 @@ 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("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") { forAll { (map: Map[Int, String]) =>