diff --git a/core/src/main/scala/cats/syntax/functor.scala b/core/src/main/scala/cats/syntax/functor.scala index fe9ff040ff..d211d4c8b5 100644 --- a/core/src/main/scala/cats/syntax/functor.scala +++ b/core/src/main/scala/cats/syntax/functor.scala @@ -1,7 +1,7 @@ package cats package syntax -trait FunctorSyntax extends Functor.ToFunctorOps { +trait FunctorSyntax extends Functor.ToFunctorOps with FunctorTupleNSyntax { implicit final def catsSyntaxFunctorTuple2Ops[F[_], A, B](fab: F[(A, B)]): FunctorTuple2Ops[F, A, B] = new FunctorTuple2Ops[F, A, B](fab) } diff --git a/project/Boilerplate.scala b/project/Boilerplate.scala index 5e674181e7..53bdb526df 100644 --- a/project/Boilerplate.scala +++ b/project/Boilerplate.scala @@ -36,7 +36,8 @@ object Boilerplate { GenTupleMonadInstances, GenTupleBifunctorInstances, GenTupleBitraverseInstances, - GenTupleUnorderedFoldableInstances + GenTupleUnorderedFoldableInstances, + GenFunctorTupleNSyntax ) val header = "// auto-generated boilerplate by /project/Boilerplate.scala" // TODO: put something meaningful here? diff --git a/project/GenFunctorTupleNSyntax.scala b/project/GenFunctorTupleNSyntax.scala new file mode 100644 index 0000000000..b24eff6181 --- /dev/null +++ b/project/GenFunctorTupleNSyntax.scala @@ -0,0 +1,43 @@ +import sbt._ + +import Boilerplate._ +import Boilerplate.{Template, TemplateVals} +import sbt.File + +object GenFunctorTupleNSyntax extends Template { + // we generate syntax for Tuple3..22 because already there is [[cats.syntax.FunctorTuple2Ops]]. + override def range = 3 to maxArity + override def filename(root: sbt.File): File = + root / "cats" / "syntax" / "FunctorTupleNSyntax.scala" + + override def content(tv: TemplateVals): String = { + import tv._ + + val generatedFunctions: String = + (1 to arity) + .map { n => + s""" + - /** + - * Lifts [[Tuple$arity._$n]] into `F[_]`. + - */ + - def _${n}F(implicit F: Functor[F]): F[A${n - 1}] = F.map(ftuple)(_._$n) + - + """ + } + .mkString("\n") + + block""" + | + |package cats + |package syntax + | + |trait FunctorTupleNSyntax { + - implicit final def catsSyntaxFunctorTuple${arity}Ops[F[_], ${`A..N`}](ftuple: F[(${`A..N`})]): FunctorTuple${arity}Ops[F, ${`A..N`}] = new FunctorTuple${arity}Ops[F, ${`A..N`}](ftuple) + - + - private[syntax] final class FunctorTuple${arity}Ops[F[_], ${`A..N`}](ftuple: F[(${`A..N`})]) extends Serializable { + $generatedFunctions + - } + - + |}""" + } +} diff --git a/tests/src/test/scala/cats/tests/FunctorTupleNSyntaxSuite.scala b/tests/src/test/scala/cats/tests/FunctorTupleNSyntaxSuite.scala new file mode 100644 index 0000000000..bf8a8c2cb9 --- /dev/null +++ b/tests/src/test/scala/cats/tests/FunctorTupleNSyntaxSuite.scala @@ -0,0 +1,388 @@ +package cats.tests + +import cats.syntax.functor._ +import cats.laws.discipline.arbitrary._ +import org.scalacheck.Prop._ + +class FunctorTupleNSyntaxSuite extends CatsSuite { + test("_1F, _2F, _3F works for Tuple3") { + forAll { (l: List[(Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + } + } + + test("_1F, _2F, _3F, _4F works for Tuple4") { + forAll { (l: List[(Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + } + } + + test("_1F, _2F, _3F, _4F, _5F works for Tuple5") { + forAll { (l: List[(Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + } + } + + test("_1F, _2F, _3F, _4F, _5F, _6F works for Tuple6") { + forAll { (l: List[(Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + } + } + + test("_1F, _2F, _3F, _4F, _5F, _6F works for Tuple6") { + forAll { (l: List[(Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + } + } + + test("_1F, _2F, _3F, _4F, _5F, _6F, _7F works for Tuple7") { + forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + } + } + + test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, 8F works for Tuple8") { + forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + } + } + + test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F works for Tuple9") { + forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + } + } + + test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F works for Tuple10") { + forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + } + } + + test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F works for Tuple11") { + forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + assertEquals(l._11F, l.map(_._11)) + } + } + + test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F works for Tuple12") { + forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + assertEquals(l._11F, l.map(_._11)) + assertEquals(l._12F, l.map(_._12)) + } + } + + test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F works for Tuple13") { + forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + assertEquals(l._11F, l.map(_._11)) + assertEquals(l._12F, l.map(_._12)) + assertEquals(l._13F, l.map(_._13)) + } + } + + test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F works for Tuple14") { + forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + assertEquals(l._11F, l.map(_._11)) + assertEquals(l._12F, l.map(_._12)) + assertEquals(l._13F, l.map(_._13)) + assertEquals(l._14F, l.map(_._14)) + } + } + + test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F works for Tuple15") { + forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + assertEquals(l._11F, l.map(_._11)) + assertEquals(l._12F, l.map(_._12)) + assertEquals(l._13F, l.map(_._13)) + assertEquals(l._14F, l.map(_._14)) + assertEquals(l._15F, l.map(_._15)) + } + } + + test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F works for Tuple16") { + forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + assertEquals(l._11F, l.map(_._11)) + assertEquals(l._12F, l.map(_._12)) + assertEquals(l._13F, l.map(_._13)) + assertEquals(l._14F, l.map(_._14)) + assertEquals(l._15F, l.map(_._15)) + assertEquals(l._16F, l.map(_._16)) + } + } + + test( + "_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F, _17F works for Tuple17" + ) { + forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + assertEquals(l._11F, l.map(_._11)) + assertEquals(l._12F, l.map(_._12)) + assertEquals(l._13F, l.map(_._13)) + assertEquals(l._14F, l.map(_._14)) + assertEquals(l._15F, l.map(_._15)) + assertEquals(l._16F, l.map(_._16)) + assertEquals(l._17F, l.map(_._17)) + } + } + + test( + "_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F, _17F, _18F works for Tuple18" + ) { + forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + assertEquals(l._11F, l.map(_._11)) + assertEquals(l._12F, l.map(_._12)) + assertEquals(l._13F, l.map(_._13)) + assertEquals(l._14F, l.map(_._14)) + assertEquals(l._15F, l.map(_._15)) + assertEquals(l._16F, l.map(_._16)) + assertEquals(l._17F, l.map(_._17)) + assertEquals(l._18F, l.map(_._18)) + } + } + + test( + "_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F, _17F, _18F, _19F works for Tuple19" + ) { + forAll { + (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + assertEquals(l._11F, l.map(_._11)) + assertEquals(l._12F, l.map(_._12)) + assertEquals(l._13F, l.map(_._13)) + assertEquals(l._14F, l.map(_._14)) + assertEquals(l._15F, l.map(_._15)) + assertEquals(l._16F, l.map(_._16)) + assertEquals(l._17F, l.map(_._17)) + assertEquals(l._18F, l.map(_._18)) + assertEquals(l._19F, l.map(_._19)) + } + } + + test( + "_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F, _17F, _18F, _19F, _20F works for Tuple20" + ) { + forAll { + (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + assertEquals(l._11F, l.map(_._11)) + assertEquals(l._12F, l.map(_._12)) + assertEquals(l._13F, l.map(_._13)) + assertEquals(l._14F, l.map(_._14)) + assertEquals(l._15F, l.map(_._15)) + assertEquals(l._16F, l.map(_._16)) + assertEquals(l._17F, l.map(_._17)) + assertEquals(l._18F, l.map(_._18)) + assertEquals(l._19F, l.map(_._19)) + assertEquals(l._20F, l.map(_._20)) + } + } + + test( + "_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F, _17F, _18F, _19F, _20F, _21F works for Tuple21" + ) { + forAll { + (l: List[ + (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) + ]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + assertEquals(l._11F, l.map(_._11)) + assertEquals(l._12F, l.map(_._12)) + assertEquals(l._13F, l.map(_._13)) + assertEquals(l._14F, l.map(_._14)) + assertEquals(l._15F, l.map(_._15)) + assertEquals(l._16F, l.map(_._16)) + assertEquals(l._17F, l.map(_._17)) + assertEquals(l._18F, l.map(_._18)) + assertEquals(l._19F, l.map(_._19)) + assertEquals(l._20F, l.map(_._20)) + assertEquals(l._21F, l.map(_._21)) + } + } + + test( + "_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F, _17F, _18F, _19F, _20F, _21F, _22F works for Tuple22" + ) { + forAll { + (l: List[ + (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) + ]) => + assertEquals(l._1F, l.map(_._1)) + assertEquals(l._2F, l.map(_._2)) + assertEquals(l._3F, l.map(_._3)) + assertEquals(l._4F, l.map(_._4)) + assertEquals(l._5F, l.map(_._5)) + assertEquals(l._6F, l.map(_._6)) + assertEquals(l._7F, l.map(_._7)) + assertEquals(l._8F, l.map(_._8)) + assertEquals(l._9F, l.map(_._9)) + assertEquals(l._10F, l.map(_._10)) + assertEquals(l._11F, l.map(_._11)) + assertEquals(l._12F, l.map(_._12)) + assertEquals(l._13F, l.map(_._13)) + assertEquals(l._14F, l.map(_._14)) + assertEquals(l._15F, l.map(_._15)) + assertEquals(l._16F, l.map(_._16)) + assertEquals(l._17F, l.map(_._17)) + assertEquals(l._18F, l.map(_._18)) + assertEquals(l._19F, l.map(_._19)) + assertEquals(l._20F, l.map(_._20)) + assertEquals(l._21F, l.map(_._21)) + assertEquals(l._22F, l.map(_._22)) + } + } +}