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 573ce85556..dd044faf36 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 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 => diff --git a/tests/src/test/scala/cats/tests/MapSuite.scala b/tests/src/test/scala/cats/tests/MapSuite.scala index 9be630d7b5..53fdb73b75 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]) =>