diff --git a/core/src/main/scala/cats/Functor.scala b/core/src/main/scala/cats/Functor.scala index d73ac6b606..5199ab48ab 100644 --- a/core/src/main/scala/cats/Functor.scala +++ b/core/src/main/scala/cats/Functor.scala @@ -52,6 +52,16 @@ import simulacrum.typeclass */ def as[A, B](fa: F[A], b: B): F[B] = map(fa)(_ => b) + /** + * Tuples the `A` value in `F[A]` with the supplied `B` value, with the `B` value on the left. + */ + def tupleLeft[A, B](fa: F[A], b: B): F[(B, A)] = map(fa)(a => (b, a)) + + /** + * Tuples the `A` value in `F[A]` with the supplied `B` value, with the `B` value on the right. + */ + def tupleRight[A, B](fa: F[A], b: B): F[(A, B)] = map(fa)(a => (a, b)) + def compose[G[_]: Functor]: Functor[λ[α => F[G[α]]]] = new ComposedFunctor[F, G] { val F = self diff --git a/tests/src/test/scala/cats/tests/FunctorTests.scala b/tests/src/test/scala/cats/tests/FunctorTests.scala index b041cb3b9c..e98940b61f 100644 --- a/tests/src/test/scala/cats/tests/FunctorTests.scala +++ b/tests/src/test/scala/cats/tests/FunctorTests.scala @@ -18,6 +18,17 @@ class FunctorTest extends CatsSuite { } } + test("tupleLeft and tupleRight tuple values with a constant value preserving structure") { + forAll { (l: List[Int], o: Option[Int], m: Map[String, Int], i: Int) => + l.tupleLeft(i) should === (List.tabulate(l.length)(in => (i, l(in)))) + o.tupleLeft(i) should === (if (o.nonEmpty) Some((i, o.get)) else None) + m.tupleLeft(i) should === (m.map { case (k, v) => (k, (i, v)) }.toMap) + l.tupleRight(i) should === (List.tabulate(l.length)(in => (l(in), i))) + o.tupleRight(i) should === (if (o.nonEmpty) Some((o.get, i)) else None) + m.tupleRight(i) should === (m.map { case (k, v) => (k, (v, i)) }.toMap) + } + } + test("widen equals map(identity)") { forAll { (i: Int) => val list: List[Some[Int]] = List(Some(i))