From 254ec632cbd7f4635cf30700c484689f3f8efb39 Mon Sep 17 00:00:00 2001 From: Gagandeep kalra Date: Wed, 6 Nov 2019 18:34:25 +0800 Subject: [PATCH] add unzip to Functor (#3062) * added unzip method to Functor typeClass * fixed formatting * added @noop on unzip, to fix issue with Simulacrum-generated extension method * remove exclusion from build.sbt * updated unzip declaration to include implicit pair derivation, removed noop * changed signature back to the original proposal --- core/src/main/scala/cats/Functor.scala | 19 ++++++++++++++++++- .../test/scala/cats/tests/FunctorSuite.scala | 8 ++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/Functor.scala b/core/src/main/scala/cats/Functor.scala index 55be0035cb..cbc3feb559 100644 --- a/core/src/main/scala/cats/Functor.scala +++ b/core/src/main/scala/cats/Functor.scala @@ -1,6 +1,6 @@ package cats -import simulacrum.typeclass +import simulacrum.{noop, typeclass} /** * Functor. @@ -144,6 +144,23 @@ import simulacrum.typeclass */ def tupleRight[A, B](fa: F[A], b: B): F[(A, B)] = map(fa)(a => (a, b)) + /** + * Un-zips an `F[(A, B)]` consisting of element pairs or Tuple2 into two separate F's tupled. + * + * NOTE: Check for effect duplication, possibly memoize before + * + * {{{ + * scala> import cats.Functor + * scala> import cats.implicits.catsStdInstancesForList + * + * scala> Functor[List].unzip(List((1,2), (3, 4))) + * res0: (List[Int], List[Int]) = (List(1, 3),List(2, 4)) + * }}} + * + */ + @noop + def unzip[A, B](fab: F[(A, B)]): (F[A], F[B]) = (map(fab)(_._1), map(fab)(_._2)) + /** * Lifts `if` to Functor * diff --git a/tests/src/test/scala/cats/tests/FunctorSuite.scala b/tests/src/test/scala/cats/tests/FunctorSuite.scala index d92d962046..60b2c8733c 100644 --- a/tests/src/test/scala/cats/tests/FunctorSuite.scala +++ b/tests/src/test/scala/cats/tests/FunctorSuite.scala @@ -29,6 +29,14 @@ class FunctorSuite extends CatsSuite { } } + test("unzip preserves structure") { + forAll { (l: List[Int], o: Option[Int], m: Map[String, Int]) => + Functor[List].unzip(l.map(i => (i, i))) === ((l, l)) + Functor[Option].unzip(o.map(i => (i, i))) === ((o, o)) + Functor[Map[String, *]].unzip(m.map { case (k, v) => (k, (v, v)) }) === ((m, m)) + } + } + test("widen equals map(identity)") { forAll { (i: Int) => val list: List[Some[Int]] = List(Some(i))