Skip to content

Commit

Permalink
Syntax for Cartesian operations with tuples
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidGregory084 committed Jun 10, 2016
1 parent 5cf9da3 commit afe548a
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 1 deletion.
1 change: 1 addition & 0 deletions core/src/main/scala/cats/syntax/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ trait AllSyntax
with StrongSyntax
with TransLiftSyntax
with TraverseSyntax
with TupleSyntax
with XorSyntax
with ValidatedSyntax
with CoproductSyntax
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/scala/cats/syntax/tuple.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package cats
package syntax

trait TupleSyntax extends TupleAritySyntax
48 changes: 47 additions & 1 deletion project/Boilerplate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ object Boilerplate {
val templates: Seq[Template] = Seq(
GenCartesianBuilders,
GenCartesianArityFunctions,
GenApplyArityFunctions
GenApplyArityFunctions,
GenTupleAritySyntax
)

val header = "// auto-generated boilerplate" // TODO: put something meaningful here?
Expand Down Expand Up @@ -211,4 +212,49 @@ object Boilerplate {
}
}

object GenTupleAritySyntax extends Template {
def filename(root: File) = root / "cats" / "syntax" / "TupleAritySyntax.scala"

def content(tv: TemplateVals) = {
import tv._

val tpes = synTypes map { tpe => s"F[$tpe]" }
val tpesString = tpes mkString ", "

val tuple = s"Tuple$arity[$tpesString]"
val tupleTpe = s"t$arity: $tuple"
val tupleArgs = (1 to arity) map { case n => s"t$arity._$n"} mkString ", "

val n = if (arity == 1) { "" } else { arity.toString }

val map =
if (arity == 1) s"def map[Z](f: (${`A..N`}) => Z)(implicit functor: Functor[F]): F[Z] = functor.map($tupleArgs)(f)"
else s"def map$arity[Z](f: (${`A..N`}) => Z)(implicit functor: Functor[F], cartesian: Cartesian[F]): F[Z] = Cartesian.map$arity($tupleArgs)(f)"

val contramap =
if (arity == 1) s"def contramap[Z](f: Z => (${`A..N`}))(implicit contravariant: Contravariant[F]): F[Z] = contravariant.contramap($tupleArgs)(f)"
else s"def contramap$arity[Z](f: Z => (${`A..N`}))(implicit contravariant: Contravariant[F], cartesian: Cartesian[F]): F[Z] = Cartesian.contramap$arity($tupleArgs)(f)"

val imap =
if (arity == 1) s"def imap[Z](f: (${`A..N`}) => Z)(g: Z => (${`A..N`}))(implicit invariant: Invariant[F]): F[Z] = invariant.imap($tupleArgs)(f)(g)"
else s"def imap$arity[Z](f: (${`A..N`}) => Z)(g: Z => (${`A..N`}))(implicit invariant: Invariant[F], cartesian: Cartesian[F]): F[Z] = Cartesian.imap$arity($tupleArgs)(f)(g)"

block"""
|package cats
|package syntax
|
|import cats.functor.{Contravariant, Invariant}
|
|trait TupleAritySyntax {
- private[syntax] final class Tuple${arity}Ops[F[_], ${`A..N`}]($tupleTpe) {
- $map
- $contramap
- $imap
- }
- implicit def tuple${arity}Syntax[F[_], ${`A..N`}]($tupleTpe): Tuple${arity}Ops[F, ${`A..N`}] = new Tuple${arity}Ops(t$arity)
|}
"""
}
}

}
31 changes: 31 additions & 0 deletions tests/src/test/scala/cats/tests/SyntaxTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,35 @@ class SyntaxTests extends AllInstances with AllSyntax {
val pfegea = mock[PartialFunction[E, G[A]]]
val gea4 = ga.recoverWith(pfegea)
}

def testTupleArity[F[_]: Functor : Cartesian, G[_]: Contravariant : Cartesian, H[_]: Invariant : Cartesian, A, B, C, D, E, Z] = {
val tfabc = mock[(F[A], F[B], F[C])]
val fa = mock[F[A]]
val fb = mock[F[B]]
val fc = mock[F[C]]
val f = mock[(A, B, C) => Z]

tfabc map3 f
(fa, fb, fc) map3 f

val tgabc = mock[(G[A], G[B])]
val ga = mock[G[A]]
val gb = mock[G[B]]
val g = mock[Z => (A, B)]

tgabc contramap2 g
(ga, gb) contramap2 g

val thabcde = mock[(H[A], H[B], H[C], H[D], H[E])]
val ha = mock[H[A]]
val hb = mock[H[B]]
val hc = mock[H[C]]
val hd = mock[H[D]]
val he = mock[H[E]]
val f5 = mock[(A, B, C, D, E) => Z]
val g5 = mock[Z => (A, B, C, D, E)]

thabcde.imap5(f5)(g5)
(ha, hb, hc, hd, he).imap5(f5)(g5)
}
}

0 comments on commit afe548a

Please sign in to comment.