Skip to content

Commit

Permalink
WIP fix for #1363
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidGregory084 committed Dec 8, 2016
1 parent 31080da commit b900413
Show file tree
Hide file tree
Showing 13 changed files with 155 additions and 130 deletions.
107 changes: 107 additions & 0 deletions core/src/main/scala/cats/Unapply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,110 @@ private[cats] sealed abstract class Unapply3Instances {
def subst: F[AA, BB, C] => M[C] = identity
}
}

trait Unapply2[TC[_[_]], MA1, MA2] {
// a type constructor which is properly kinded for the type class
type M[_]
// the types applied to the type constructor to make an MA
type A1
type A2

// the actual type class instance found
def TC: TC[M]

// functions which will coerce the MA values into one of type M[A]
// this will end up being the identity function, but we can't supply
// it until we have proven that MA and M[A] are the same type
def subst1: MA1 => M[A1]
def subst2: MA2 => M[A2]
}

object Unapply2 {
// the type we will instantiate when we find a type class instance
// for a type in the shape F[_, _] when we fix the left type
type Aux2Left2[TC[_[_]], FA1, FA2, F[_, _], AA1, AA2, B] = Unapply2[TC, FA1, FA2] {
type M[X] = F[X, B]
type A1 = AA1
type A2 = AA2
}

// the type we will instantiate when we find a type class instance
// for a type in the shape F[_, _[_]] when we fix the left type
type Aux2Left2K[TC[_[_]], FA1, FA2, F[_, _[_]], AA1, AA2, BX[_]] = Unapply2[TC, FA1, FA2] {
type M[X] = F[X, BX]
type A1 = AA1
type A2 = AA2
}

// the type we will instantiate when we find a type class instance
// for a type in the shape F[_, _] when we fix the right type
type Aux2Right2[TC[_[_]], MA1, MA2, F[_, _], AA, B1, B2] = Unapply2[TC, MA1, MA2] {
type M[X] = F[AA, X]
type A1 = B1
type A2 = B2
}

// the type we will instantiate when we find a type class instance
// for a type in the shape F[_[_], _] when we fix the right type
type Aux2Right2K[TC[_[_]], MA1, MA2, F[_[_], _], AX[_], B1, B2] = Unapply2[TC, MA1, MA2] {
type M[X] = F[AX, X]
type A1 = B1
type A2 = B2
}

implicit def catsUnapply2Left2[TC[_[_]], FA1, FA2, F[_, _], AA1, AA2, B](
implicit
U1: Unapply.Aux2Left[TC, FA1, F, AA1, B],
U2: Unapply.Aux2Left[TC, FA2, F, AA2, B]
): Aux2Left2[TC, FA1, FA2, F, AA1, AA2, B] =
new Unapply2[TC, FA1, FA2] {
type M[X] = F[X, B]
type A1 = AA1
type A2 = AA2
def TC = U1.TC
def subst1 = U1.subst
def subst2 = U2.subst
}

implicit def catsUnapply2Left2K[TC[_[_]], FA1, FA2, F[_, _[_]], AA1, AA2, B[_]](
implicit
U1: Unapply.Aux2LeftK[TC, FA1, F, AA1, B],
U2: Unapply.Aux2LeftK[TC, FA2, F, AA2, B]
): Aux2Left2K[TC, FA1, FA2, F, AA1, AA2, B] =
new Unapply2[TC, FA1, FA2] {
type M[X] = F[X, B]
type A1 = AA1
type A2 = AA2
def TC = U1.TC
def subst1 = U1.subst
def subst2 = U2.subst
}

implicit def catsUnapply2Right2[TC[_[_]], FA1, FA2, F[_, _], AA, B1, B2](
implicit
U1: Unapply.Aux2Right[TC, FA1, F, AA, B1],
U2: Unapply.Aux2Right[TC, FA2, F, AA, B2]
): Aux2Right2[TC, FA1, FA2, F, AA, B1, B2] =
new Unapply2[TC, FA1, FA2] {
type M[X] = F[AA, X]
type A1 = B1
type A2 = B2
def TC = U1.TC
def subst1 = U1.subst
def subst2 = U2.subst
}

implicit def catsUnapply2Right2K[TC[_[_]], FA1, FA2, F[_[_], _], AA[_], B1, B2](
implicit
U1: Unapply.Aux2RightK[TC, FA1, F, AA, B1],
U2: Unapply.Aux2RightK[TC, FA2, F, AA, B2]
): Aux2Right2K[TC, FA1, FA2, F, AA, B1, B2] =
new Unapply2[TC, FA1, FA2] {
type M[X] = F[AA, X]
type A1 = B1
type A2 = B2
def TC = U1.TC
def subst1 = U1.subst
def subst2 = U2.subst
}
}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/data/EitherT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ final case class EitherT[F[_], A, B](value: F[Either[A, B]]) {
* scala> val v1: Validated[NonEmptyList[Error], Int] = Validated.Invalid(NonEmptyList.of("error 1"))
* scala> val v2: Validated[NonEmptyList[Error], Int] = Validated.Invalid(NonEmptyList.of("error 2"))
* scala> val eithert: EitherT[Option, Error, Int] = EitherT(Some(Either.left("error 3")))
* scala> eithert.withValidated { v3 => (v1 |@| v2 |@| v3.leftMap(NonEmptyList.of(_))).map{ case (i, j, k) => i + j + k } }
* scala> eithert.withValidated { v3 => (v1, v2, v3.leftMap(NonEmptyList.of(_))).mapN { case (i, j, k) => i + j + k } }
* res0: EitherT[Option, NonEmptyList[Error], Int] = EitherT(Some(Left(NonEmptyList(error 1, error 2, error 3))))
* }}}
*/
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/cats/data/Prod.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package cats
package data

import cats.functor.Contravariant
import cats.syntax.cartesian._
import cats.syntax.apply._

/**
* [[Prod]] is a product to two independent functor values.
Expand Down Expand Up @@ -160,7 +160,7 @@ private[data] sealed trait ProdTraverse[F[_], G[_]] extends Traverse[λ[α => Pr
def G: Traverse[G]

override def traverse[H[_]: Applicative, A, B](fa: Prod[F, G, A])(f: A => H[B]): H[Prod[F, G, B]] =
(F.traverse(fa.first)(f) |@| G.traverse(fa.second)(f)).map(Prod(_, _))
(F.traverse(fa.first)(f), G.traverse(fa.second)(f)).mapN(Prod(_, _))
}

private[data] sealed trait ProdMonadCombine[F[_], G[_]] extends MonadCombine[λ[α => Prod[F, G, α]]]
Expand Down
1 change: 0 additions & 1 deletion core/src/main/scala/cats/syntax/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ trait AllSyntax
with TransLiftSyntax
with TraverseFilterSyntax
with TraverseSyntax
with TupleSyntax
with ValidatedSyntax
with VectorSyntax
with WriterSyntax
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/syntax/apply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ private[syntax] trait ApplySyntax1 {
}
}

trait ApplySyntax extends ApplySyntax1 {
trait ApplySyntax extends ApplySyntax1 with TupleCartesianSyntax {
implicit def catsSyntaxApply[F[_], A](fa: F[A])(implicit F: Apply[F]): Apply.Ops[F, A] =
new Apply.Ops[F, A] {
val self = fa
Expand Down
4 changes: 0 additions & 4 deletions core/src/main/scala/cats/syntax/cartesian.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ trait CartesianSyntax extends CartesianSyntax1 {
}

abstract class CartesianOps[F[_], A] extends Cartesian.Ops[F, A] {
def |@|[B](fb: F[B]): CartesianBuilder[F]#CartesianBuilder2[A, B] =
new CartesianBuilder[F] |@| self |@| fb

def *>[B](fb: F[B])(implicit F: Functor[F]): F[B] = F.map(typeClassInstance.product(self, fb)) { case (a, b) => b }

def <*[B](fb: F[B])(implicit F: Functor[F]): F[A] = F.map(typeClassInstance.product(self, fb)) { case (a, b) => a }

}
1 change: 0 additions & 1 deletion core/src/main/scala/cats/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ package object syntax {
object transLift extends TransLiftSyntax
object traverse extends TraverseSyntax
object traverseFilter extends TraverseFilterSyntax
object tuple extends TupleSyntax
object validated extends ValidatedSyntax
object vector extends VectorSyntax
object writer extends WriterSyntax
Expand Down
4 changes: 0 additions & 4 deletions core/src/main/scala/cats/syntax/tuple.scala

This file was deleted.

2 changes: 1 addition & 1 deletion free/src/test/scala/cats/free/FreeApplicativeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class FreeApplicativeTests extends CatsSuite {
// fixed by #568
val fli1 = FreeApplicative.lift[List, Int](List(1, 3, 5, 7))
val fli2 = FreeApplicative.lift[List, Int](List(1, 3, 5, 7))
(fli1 |@| fli2).map(_ + _)
(fli1, fli2).mapN(_ + _)
}

test("FreeApplicative#analyze") {
Expand Down
77 changes: 14 additions & 63 deletions project/Boilerplate.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ object Boilerplate {


val templates: Seq[Template] = Seq(
GenCartesianBuilders,
GenCartesianArityFunctions,
GenApplyArityFunctions,
GenTupleCartesianSyntax
Expand Down Expand Up @@ -95,63 +94,6 @@ object Boilerplate {
The block otherwise behaves as a standard interpolated string with regards to variable substitution.
*/

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

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

val tpes = synTypes map { tpe => s"F[$tpe]" }
val tpesString = synTypes mkString ", "
val params = (synVals zip tpes) map { case (v,t) => s"$v:$t"} mkString ", "
val next = if (arity + 1 <= maxArity) {
s"def |@|[Z](z: F[Z]) = new CartesianBuilder${arity + 1}(${`a..n`}, z)"
} else {
""
}

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(${`a..n`})(f)"
else s"def map[Z](f: (${`A..N`}) => Z)(implicit functor: Functor[F], cartesian: Cartesian[F]): F[Z] = Cartesian.map$n(${`a..n`})(f)"

val contramap =
if (arity == 1) s"def contramap[Z](f: Z => (${`A..N`}))(implicit contravariant: Contravariant[F]): F[Z] = contravariant.contramap(${`a..n`})(f)"
else s"def contramap[Z](f: Z => (${`A..N`}))(implicit contravariant: Contravariant[F], cartesian: Cartesian[F]): F[Z] = Cartesian.contramap$n(${`a..n`})(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(${`a..n`})(f)(g)"
else s"def imap[Z](f: (${`A..N`}) => Z)(g: Z => (${`A..N`}))(implicit invariant: Invariant[F], cartesian: Cartesian[F]): F[Z] = Cartesian.imap$n(${`a..n`})(f)(g)"

val tupled = if (arity != 1) {
s"def tupled(implicit invariant: Invariant[F], cartesian: Cartesian[F]): F[(${`A..N`})] = Cartesian.tuple$n(${`a..n`})"
} else {
""
}

block"""
|package cats
|package syntax
|
|import cats.functor.{Contravariant, Invariant}
|
|private[syntax] final class CartesianBuilder[F[_]] {
| def |@|[A](a: F[A]) = new CartesianBuilder1(a)
|
- private[syntax] final class CartesianBuilder$arity[${`A..N`}]($params) {
- $next
- def apWith[Z](f: F[(${`A..N`}) => Z])(implicit apply: Apply[F]): F[Z] = apply.ap$n(f)(${`a..n`})
- $map
- $contramap
- $imap
- $tupled
- }
|}
"""
}
}

object GenApplyArityFunctions extends Template {
def filename(root: File) = root / "cats" / "ApplyArityFunctions.scala"
override def range = 3 to maxArity
Expand Down Expand Up @@ -238,15 +180,21 @@ object Boilerplate {

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)"
else s"def mapN[Z](f: (${`A..N`}) => Z)(implicit functor: Functor[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)"
else s"def contramapN[Z](f: Z => (${`A..N`}))(implicit contravariant: Contravariant[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)"
else s"def imapN[Z](f: (${`A..N`}) => Z)(g: Z => (${`A..N`}))(implicit invariant: Invariant[F]): F[Z] = Cartesian.imap$arity($tupleArgs)(f)(g)"

val tupled = if (arity != 1) {
s"def tupled(implicit invariant: Invariant[F]): F[(${`A..N`})] = Cartesian.tuple$n($tupleArgs)"
} else {
""
}

block"""
|package cats
Expand All @@ -255,13 +203,16 @@ object Boilerplate {
|import cats.functor.{Contravariant, Invariant}
|
|trait TupleCartesianSyntax {
- implicit def catsSyntaxTuple${arity}Cartesian[F[_], ${`A..N`}]($tupleTpe): Tuple${arity}CartesianOps[F, ${`A..N`}] = new Tuple${arity}CartesianOps(t$arity)
| implicit def catsSyntaxTuple2CartesianU[FA1, FA2](t2: Tuple2[FA1, FA2])(implicit U: Unapply2[Cartesian, FA1, FA2]): Tuple2CartesianOps[U.M, U.A1, U.A2] = new Tuple2CartesianOps((U.subst1(t2._1), U.subst2(t2._2)), U.TC)
- implicit def catsSyntaxTuple${arity}Cartesian[F[_], ${`A..N`}]($tupleTpe)(implicit C: Cartesian[F]): Tuple${arity}CartesianOps[F, ${`A..N`}] = new Tuple${arity}CartesianOps(t$arity, C)
|}
|
-private[syntax] final class Tuple${arity}CartesianOps[F[_], ${`A..N`}]($tupleTpe) {
-private[syntax] final class Tuple${arity}CartesianOps[F[_], ${`A..N`}]($tupleTpe, C: Cartesian[F]) {
- implicit val cartesian: Cartesian[F] = C
- $map
- $contramap
- $imap
- $tupled
- def apWith[Z](f: F[(${`A..N`}) => Z])(implicit apply: Apply[F]): F[Z] = apply.ap$n(f)($tupleArgs)
-}
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package tests
import cats.laws.discipline.eq.catsLawsEqForFn1
import cats.laws.discipline.{InvariantMonoidalTests, SerializableTests}
import cats.instances.all._
import cats.syntax.cartesian._
import cats.syntax.apply._
import cats.Eq
import org.scalacheck.{Arbitrary, Gen}

Expand Down Expand Up @@ -42,7 +42,7 @@ object CsvCodecInvariantMonoidalTests {
def read(s: CSV): (Option[(A, B)], CSV) = {
val (a1, s1) = fa.read(s)
val (a2, s2) = fb.read(s1)
((a1 |@| a2).map(_ -> _), s2)
((a1, a2).mapN(_ -> _), s2)
}

def write(a: (A, B)): CSV =
Expand Down
Loading

0 comments on commit b900413

Please sign in to comment.