diff --git a/core/src/main/scala/cats/data/Compose.scala b/core/src/main/scala/cats/data/Compose.scala deleted file mode 100644 index 8ff58fc8ff..0000000000 --- a/core/src/main/scala/cats/data/Compose.scala +++ /dev/null @@ -1,201 +0,0 @@ -package cats -package data - -import cats.functor._ - -final case class Compose[F[_], G[_], A](value: F[G[A]]) - -object Compose extends ComposeInstances - -private[data] sealed abstract class ComposeInstances extends ComposeInstances1 { - implicit def composeTraverse[F[_]: Traverse, G[_]: Traverse]: Traverse[Compose[F, G, ?]] = - new ComposeTraverse[F, G] { - val F = Traverse[F] - val G = Traverse[G] - } -} - -private[data] sealed abstract class ComposeInstances1 extends ComposeInstances2 { - /** - * This composes two `Reducible` instances to provide an - * instance for the nested types. - * - * In other words, given a `Reducible[F]` instance (which can reduce - * `F[A]`) and a `Reducible[G]` instance (which can reduce `G[A]` - * values), this class is able to reduce `F[G[A]]` values. - */ - implicit def composeReducible[F[_]: Reducible, G[_]: Reducible]: Reducible[Compose[F, G, ?]] = - new ComposeReducible[F, G] { - val F = Reducible[F] - val G = Reducible[G] - } -} - -private[data] sealed abstract class ComposeInstances2 extends ComposeInstances3 { - implicit def composeFoldable[F[_]: Foldable, G[_]: Foldable]: Foldable[Compose[F, G, ?]] = - new ComposeFoldable[F, G] { - val F = Foldable[F] - val G = Foldable[G] - } -} - -private[data] sealed abstract class ComposeInstances3 extends ComposeInstances4 { - implicit def composeAlternative[F[_]: Alternative, G[_]: Applicative]: Alternative[Compose[F, G, ?]] = - new CompositeAlternative[F, G] { - val F = Alternative[F] - val G = Applicative[G] - } -} - -private[data] sealed abstract class ComposeInstances4 extends ComposeInstances5 { - /** - * Two sequentially dependent Applicatives can be composed. - * - * The composition of Applicatives `F` and `G`, `F[G[x]]`, is also an Applicative - * - * Applicative[Option].compose[List].pure(10) = Some(List(10)) - */ - implicit def composeApplicative[F[_]: Applicative, G[_]: Applicative]: Applicative[Compose[F, G, ?]] = - new ComposeApplicative[F, G] { - val F = Applicative[F] - val G = Applicative[G] - } - - implicit def composeMonoidK[F[_]: MonoidK, G[_]]: MonoidK[Compose[F, G, ?]] = - new ComposeMonoidK[F, G] { - val F = MonoidK[F] - } -} - -private[data] sealed abstract class ComposeInstances5 extends ComposeInstances6 { - /** - * Two sequentially dependent Applys can be composed. - * - * The composition of Applys `F` and `G`, `F[G[x]]`, is also an Apply. - * - * Example: - * {{{ - * scala> import cats.Apply - * scala> import cats.implicits._ - * scala> val ap = Apply[Option].compose[List] - * scala> val x: Option[List[Int]] = Some(List(1, 2)) - * scala> val y: Option[List[Int]] = Some(List(10, 20)) - * scala> ap.map2(x, y)(_ + _) - * res0: Option[List[Int]] = Some(List(11, 21, 12, 22)) - * }}} - */ - implicit def composeApply[F[_]: Apply, G[_]: Apply]: Apply[Compose[F, G, ?]] = - new ComposeApply[F, G] { - val F = Apply[F] - val G = Apply[G] - } - - implicit def composeSemigroupK[F[_]: SemigroupK, G[_]]: SemigroupK[Compose[F, G, ?]] = - new ComposeSemigroupK[F, G] { - val F = SemigroupK[F] - } -} - -private[data] sealed abstract class ComposeInstances6 extends ComposeInstances7 { - implicit def composeFunctor[F[_]: Functor, G[_]: Functor]: Functor[Compose[F, G, ?]] = - new ComposeFunctor[F, G] { - val F = Functor[F] - val G = Functor[G] - } -} - -private[data] sealed abstract class ComposeInstances7 { - implicit def composeInvariant[F[_]: Invariant, G[_]: Invariant]: Invariant[Compose[F, G, ?]] = - new ComposeInvariant[F, G] { - val F = Invariant[F] - val G = Invariant[G] - } -} - -private[data] trait ComposeInvariant[F[_], G[_]] extends Invariant[Compose[F, G, ?]] { - def F: Invariant[F] - def G: Invariant[G] - - override def imap[A, B](fga: Compose[F, G, A])(f: A => B)(g: B => A): Compose[F, G, B] = - Compose(F.imap(fga.value)(ga => G.imap(ga)(f)(g))(gb => G.imap(gb)(g)(f))) -} - -private[data] trait ComposeFunctor[F[_], G[_]] extends Functor[Compose[F, G, ?]] with ComposeInvariant[F, G] { - def F: Functor[F] - def G: Functor[G] - - override def map[A, B](fga: Compose[F, G, A])(f: A => B): Compose[F, G, B] = - Compose(F.map(fga.value)(ga => G.map(ga)(f))) -} - -private[data] trait ComposeApply[F[_], G[_]] extends Apply[Compose[F, G, ?]] with ComposeFunctor[F, G] { - def F: Apply[F] - def G: Apply[G] - - override def ap[A, B](ff: Compose[F, G, A => B])(fa: Compose[F, G, A]): Compose[F, G, B] = - Compose(F.ap(F.map(ff.value)(gab => G.ap(gab)(_)))(fa.value)) - - override def product[A, B](fa: F[G[A]], fb: F[G[B]]): F[G[(A, B)]] = - F.map2(fa, fb)(G.product) -} - -private[data] trait ComposeApplicative[F[_], G[_]] extends Applicative[Compose[F, G, ?]] with ComposeApply[F, G] { - def F: Applicative[F] - def G: Applicative[G] - - override def pure[A](x: A): Compose[F, G, A] = Compose(F.pure(G.pure(x))) -} - -private[data] trait ComposeSemigroupK[F[_], G[_]] extends SemigroupK[Compose[F, G, ?]] { - def F: SemigroupK[F] - - def combineK[A](x: Compose[F, G, A], y: Compose[F, G, A]): Compose[F, G, A] = Compose(F.combineK(x.value, y.value)) -} - -private[data] trait ComposeMonoidK[F[_], G[_]] extends MonoidK[Compose[F, G, ?]] with ComposeSemigroupK[F, G] { - def F: MonoidK[F] - - def empty[A]: Compose[F, G, A] = Compose(F.empty) -} - -private[data] trait CompositeAlternative[F[_], G[_]] extends Alternative[Compose[F, G, ?]] with ComposeApplicative[F, G] with ComposeMonoidK[F, G] { - def F: Alternative[F] -} - -private[data] trait ComposeFoldable[F[_], G[_]] extends Foldable[Compose[F, G, ?]] { - def F: Foldable[F] - def G: Foldable[G] - - def foldLeft[A, B](fga: Compose[F, G, A], b: B)(f: (B, A) => B): B = - F.foldLeft(fga.value, b)((b, a) => G.foldLeft(a, b)(f)) - - def foldRight[A, B](fga: Compose[F, G, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - F.foldRight(fga.value, lb)((ga, lb) => G.foldRight(ga, lb)(f)) -} - -private[data] trait ComposeTraverse[F[_], G[_]] extends Traverse[Compose[F, G, ?]] with ComposeFoldable[F, G] with ComposeFunctor[F, G] { - def F: Traverse[F] - def G: Traverse[G] - - def traverse[H[_]: Applicative, A, B](fga: Compose[F, G, A])(f: A => H[B]): H[Compose[F, G, B]] = - Applicative[H].map(F.traverse(fga.value)(ga => G.traverse(ga)(f)))(Compose(_)) -} - -private[data] trait ComposeReducible[F[_], G[_]] extends Reducible[Compose[F, G, ?]] with ComposeFoldable[F, G] { - def F: Reducible[F] - def G: Reducible[G] - - override def reduceLeftTo[A, B](fga: Compose[F, G, A])(f: A => B)(g: (B, A) => B): B = { - def toB(ga: G[A]): B = G.reduceLeftTo(ga)(f)(g) - F.reduceLeftTo(fga.value)(toB) { (b, ga) => - G.foldLeft(ga, b)(g) - } - } - - override def reduceRightTo[A, B](fga: Compose[F, G, A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = { - def toB(ga: G[A]): B = G.reduceRightTo(ga)(f)(g).value - F.reduceRightTo(fga.value)(toB) { (ga, lb) => - G.foldRight(ga, lb)(g) - } - } -} diff --git a/core/src/main/scala/cats/data/Func.scala b/core/src/main/scala/cats/data/Func.scala index e6a993c52a..7571b1acd9 100644 --- a/core/src/main/scala/cats/data/Func.scala +++ b/core/src/main/scala/cats/data/Func.scala @@ -87,14 +87,14 @@ sealed abstract class AppFunc[F[_], A, B] extends Func[F, A, B] { self => } } - def compose[G[_], C](g: AppFunc[G, C, A]): AppFunc[Compose[G, F, ?], C, B] = { - implicit val gfApplicative: Applicative[Compose[G, F, ?]] = Compose.composeApplicative[G, F](g.F, F) - Func.appFunc[Compose[G, F, ?], C, B]({ - c: C => Compose(g.F.map(g.run(c))(self.run)) + def compose[G[_], C](g: AppFunc[G, C, A]): AppFunc[Nested[G, F, ?], C, B] = { + implicit val gfApplicative: Applicative[Nested[G, F, ?]] = Nested.nestedApplicative[G, F](g.F, F) + Func.appFunc[Nested[G, F, ?], C, B]({ + c: C => Nested(g.F.map(g.run(c))(self.run)) }) } - def andThen[G[_], C](g: AppFunc[G, B, C]): AppFunc[Compose[F, G, ?], A, C] = + def andThen[G[_], C](g: AppFunc[G, B, C]): AppFunc[Nested[F, G, ?], A, C] = g.compose(self) def map[C](f: B => C): AppFunc[F, A, C] = diff --git a/core/src/main/scala/cats/data/Nested.scala b/core/src/main/scala/cats/data/Nested.scala new file mode 100644 index 0000000000..1cd3a30421 --- /dev/null +++ b/core/src/main/scala/cats/data/Nested.scala @@ -0,0 +1,265 @@ +package cats +package data + +import cats.functor._ + +/** Similar to [[cats.data.Prod]], but for nested composition. + * + * For instance, since both `List` and `Option` have a `Functor`, then so does + * `List[Option[_]]`. This is represented by this data type via the instantiation + * `Nested[List, Option, ?]`. + * + * {{{ + * scala> import cats.Functor + * scala> import cats.data.Nested + * scala> import cats.implicits._ + * scala> val listOption: List[Option[Int]] = List(Some(1), None) + * scala> val f: Int => String = i => (i * 2).toString + * scala> Functor[List].map(listOption)(opt => opt.map(f)) + * res0: List[Option[String]] = List(Some(2), None) + * scala> val nested: Nested[List, Option, Int] = Nested(listOption) + * scala> val result: Nested[List, Option, String] = Functor[Nested[List, Option, ?]].map(nested)(f) + * scala> result.value + * res1: List[Option[String]] = List(Some(2), None) + * }}} + */ +final case class Nested[F[_], G[_], A](value: F[G[A]]) + +object Nested extends NestedInstances + +private[data] sealed abstract class NestedInstances extends NestedInstances1 { + implicit def nestedEq[F[_], G[_], A](implicit FGA: Eq[F[G[A]]]): Eq[Nested[F, G, A]] = + FGA.on(_.value) + + implicit def nestedTraverse[F[_]: Traverse, G[_]: Traverse]: Traverse[Nested[F, G, ?]] = + new NestedTraverse[F, G] { + val F = Traverse[F] + val G = Traverse[G] + } +} + +private[data] sealed abstract class NestedInstances1 extends NestedInstances2 { + implicit def nestedReducible[F[_]: Reducible, G[_]: Reducible]: Reducible[Nested[F, G, ?]] = + new NestedReducible[F, G] { + val F = Reducible[F] + val G = Reducible[G] + } + + implicit def nestedContravariant[F[_]: Contravariant, G[_]: Contravariant]: Functor[Nested[F, G, ?]] = + new NestedContravariant[F, G] { + val F = Contravariant[F] + val G = Contravariant[G] + } +} + +private[data] sealed abstract class NestedInstances2 extends NestedInstances3 { + implicit def nestedFoldable[F[_]: Foldable, G[_]: Foldable]: Foldable[Nested[F, G, ?]] = + new NestedFoldable[F, G] { + val F = Foldable[F] + val G = Foldable[G] + } + + implicit def nestedContravariantCovariant[F[_]: Contravariant, G[_]: Functor]: Contravariant[Nested[F, G, ?]] = + new NestedContravariantCovariant[F, G] { + val F = Contravariant[F] + val G = Functor[G] + } +} + +private[data] sealed abstract class NestedInstances3 extends NestedInstances4 { + implicit def nestedAlternative[F[_]: Alternative, G[_]: Applicative]: Alternative[Nested[F, G, ?]] = + new CompositeAlternative[F, G] { + val F = Alternative[F] + val G = Applicative[G] + } + + implicit def nestedCovariantContravariant[F[_]: Functor, G[_]: Contravariant]: Contravariant[Nested[F, G, ?]] = + new NestedCovariantContravariant[F, G] { + val F = Functor[F] + val G = Contravariant[G] + } +} + +private[data] sealed abstract class NestedInstances4 extends NestedInstances5 { + implicit def nestedApplicative[F[_]: Applicative, G[_]: Applicative]: Applicative[Nested[F, G, ?]] = + new NestedApplicative[F, G] { + val F = Applicative[F] + val G = Applicative[G] + } + + implicit def nestedMonoidK[F[_]: MonoidK, G[_]]: MonoidK[Nested[F, G, ?]] = + new NestedMonoidK[F, G] { + val F = MonoidK[F] + } +} + +private[data] sealed abstract class NestedInstances5 extends NestedInstances6 { + implicit def nestedApply[F[_]: Apply, G[_]: Apply]: Apply[Nested[F, G, ?]] = + new NestedApply[F, G] { + val F = Apply[F] + val G = Apply[G] + } + + implicit def nestedSemigroupK[F[_]: SemigroupK, G[_]]: SemigroupK[Nested[F, G, ?]] = + new NestedSemigroupK[F, G] { + val F = SemigroupK[F] + } +} + +private[data] sealed abstract class NestedInstances6 extends NestedInstances7 { + implicit def nestedFunctor[F[_]: Functor, G[_]: Functor]: Functor[Nested[F, G, ?]] = + new NestedFunctor[F, G] { + val F = Functor[F] + val G = Functor[G] + } +} + +private[data] sealed abstract class NestedInstances7 extends NestedInstances8 { + implicit def nestedInvariant[F[_]: Invariant, G[_]: Invariant]: Invariant[Nested[F, G, ?]] = + new NestedInvariant[F, G] { + val F = Invariant[F] + val G = Invariant[G] + } +} + +private[data] sealed abstract class NestedInstances8 { + implicit def nestedInvariantCovariant[F[_]: Invariant, G[_]: Functor]: Invariant[Nested[F, G, ?]] = + new NestedInvariantCovariant[F, G] { + val F = Invariant[F] + val G = Functor[G] + } + + implicit def nestedInvariantContravariant[F[_]: Invariant, G[_]: Contravariant]: Invariant[Nested[F, G, ?]] = + new NestedInvariantContravariant[F, G] { + val F = Invariant[F] + val G = Contravariant[G] + } +} + +private[data] trait NestedInvariant[F[_], G[_]] extends Invariant[Nested[F, G, ?]] { + def F: Invariant[F] + def G: Invariant[G] + + override def imap[A, B](fga: Nested[F, G, A])(f: A => B)(g: B => A): Nested[F, G, B] = + Nested(F.imap(fga.value)(ga => G.imap(ga)(f)(g))(gb => G.imap(gb)(g)(f))) +} + +private[data] trait NestedFunctor[F[_], G[_]] extends Functor[Nested[F, G, ?]] with NestedInvariant[F, G] { + def F: Functor[F] + def G: Functor[G] + + override def map[A, B](fga: Nested[F, G, A])(f: A => B): Nested[F, G, B] = + Nested(F.map(fga.value)(ga => G.map(ga)(f))) +} + +private[data] trait NestedApply[F[_], G[_]] extends Apply[Nested[F, G, ?]] with NestedFunctor[F, G] { + def F: Apply[F] + def G: Apply[G] + + override def ap[A, B](ff: Nested[F, G, A => B])(fa: Nested[F, G, A]): Nested[F, G, B] = + Nested(F.ap(F.map(ff.value)(gab => G.ap(gab)(_)))(fa.value)) + + override def product[A, B](fa: Nested[F, G, A], fb: Nested[F, G, B]): Nested[F, G, (A, B)] = + Nested(F.map2(fa.value, fb.value)(G.product)) +} + +private[data] trait NestedApplicative[F[_], G[_]] extends Applicative[Nested[F, G, ?]] with NestedApply[F, G] { + def F: Applicative[F] + def G: Applicative[G] + + override def pure[A](x: A): Nested[F, G, A] = Nested(F.pure(G.pure(x))) +} + +private[data] trait NestedSemigroupK[F[_], G[_]] extends SemigroupK[Nested[F, G, ?]] { + def F: SemigroupK[F] + + override def combineK[A](x: Nested[F, G, A], y: Nested[F, G, A]): Nested[F, G, A] = Nested(F.combineK(x.value, y.value)) +} + +private[data] trait NestedMonoidK[F[_], G[_]] extends MonoidK[Nested[F, G, ?]] with NestedSemigroupK[F, G] { + def F: MonoidK[F] + + override def empty[A]: Nested[F, G, A] = Nested(F.empty) +} + +private[data] trait CompositeAlternative[F[_], G[_]] extends Alternative[Nested[F, G, ?]] with NestedApplicative[F, G] with NestedMonoidK[F, G] { + def F: Alternative[F] +} + +private[data] trait NestedFoldable[F[_], G[_]] extends Foldable[Nested[F, G, ?]] { + def F: Foldable[F] + def G: Foldable[G] + + override def foldLeft[A, B](fga: Nested[F, G, A], b: B)(f: (B, A) => B): B = + F.foldLeft(fga.value, b)((b, a) => G.foldLeft(a, b)(f)) + + override def foldRight[A, B](fga: Nested[F, G, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + F.foldRight(fga.value, lb)((ga, lb) => G.foldRight(ga, lb)(f)) +} + +private[data] trait NestedTraverse[F[_], G[_]] extends Traverse[Nested[F, G, ?]] with NestedFoldable[F, G] with NestedFunctor[F, G] { + def F: Traverse[F] + def G: Traverse[G] + + override def traverse[H[_]: Applicative, A, B](fga: Nested[F, G, A])(f: A => H[B]): H[Nested[F, G, B]] = + Applicative[H].map(F.traverse(fga.value)(ga => G.traverse(ga)(f)))(Nested(_)) +} + +private[data] trait NestedReducible[F[_], G[_]] extends Reducible[Nested[F, G, ?]] with NestedFoldable[F, G] { + def F: Reducible[F] + def G: Reducible[G] + + override def reduceLeftTo[A, B](fga: Nested[F, G, A])(f: A => B)(g: (B, A) => B): B = { + def toB(ga: G[A]): B = G.reduceLeftTo(ga)(f)(g) + F.reduceLeftTo(fga.value)(toB) { (b, ga) => + G.foldLeft(ga, b)(g) + } + } + + override def reduceRightTo[A, B](fga: Nested[F, G, A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = { + def toB(ga: G[A]): B = G.reduceRightTo(ga)(f)(g).value + F.reduceRightTo(fga.value)(toB) { (ga, lb) => + G.foldRight(ga, lb)(g) + } + } +} + +private[data] trait NestedContravariant[F[_], G[_]] extends Functor[Nested[F, G, ?]] { + def F: Contravariant[F] + def G: Contravariant[G] + + override def map[A, B](fga: Nested[F, G, A])(f: A => B): Nested[F, G, B] = + Nested(F.contramap(fga.value)(gb => G.contramap(gb)(f))) +} + +private[data] trait NestedContravariantCovariant[F[_], G[_]] extends Contravariant[Nested[F, G, ?]] { + def F: Contravariant[F] + def G: Functor[G] + + override def contramap[A, B](fga: Nested[F, G, A])(f: B => A): Nested[F, G, B] = + Nested(F.contramap(fga.value)(gb => G.map(gb)(f))) +} + +private[data] trait NestedCovariantContravariant[F[_], G[_]] extends Contravariant[Nested[F, G, ?]] { + def F: Functor[F] + def G: Contravariant[G] + + override def contramap[A, B](fga: Nested[F, G, A])(f: B => A): Nested[F, G, B] = + Nested(F.map(fga.value)(ga => G.contramap(ga)(f))) +} + +private[data] trait NestedInvariantCovariant[F[_], G[_]] extends Invariant[Nested[F, G, ?]] { + def F: Invariant[F] + def G: Functor[G] + + override def imap[A, B](fga: Nested[F, G, A])(f: A => B)(g: B => A): Nested[F, G, B] = + Nested(F.imap(fga.value)(ga => G.map(ga)(f))(gb => G.map(gb)(g))) +} + +private[data] trait NestedInvariantContravariant[F[_], G[_]] extends Invariant[Nested[F, G, ?]] { + def F: Invariant[F] + def G: Contravariant[G] + + override def imap[A, B](fga: Nested[F, G, A])(f: A => B)(g: B => A): Nested[F, G, B] = + Nested(F.imap(fga.value)(ga => G.contramap(ga)(g))(gb => G.contramap(gb)(f))) +} diff --git a/core/src/main/scala/cats/functor/Contravariant.scala b/core/src/main/scala/cats/functor/Contravariant.scala index b8bec7ee01..28b249dc7e 100644 --- a/core/src/main/scala/cats/functor/Contravariant.scala +++ b/core/src/main/scala/cats/functor/Contravariant.scala @@ -9,34 +9,4 @@ import simulacrum.typeclass @typeclass trait Contravariant[F[_]] extends Invariant[F] { self => def contramap[A, B](fa: F[A])(f: B => A): F[B] override def imap[A, B](fa: F[A])(f: A => B)(fi: B => A): F[B] = contramap(fa)(fi) - - def compose[G[_]](implicit G: Contravariant[G]): Functor[Lambda[X => F[G[X]]]] = { - val G0 = G - new Contravariant.Composite[F, G] { - def F: Contravariant[F] = self - def G: Contravariant[G] = G0 - } - } - - override def composeWithFunctor[G[_]](implicit G: Functor[G]): Contravariant[Lambda[X => F[G[X]]]] = { - val G0 = G - new Contravariant.CovariantComposite[F, G] { - def F: Contravariant[F] = self - def G: Functor[G] = G0 - } - } -} - -object Contravariant { - trait Composite[F[_], G[_]] extends Functor[Lambda[X => F[G[X]]]] { - def F: Contravariant[F] - def G: Contravariant[G] - def map[A, B](fga: F[G[A]])(f: A => B): F[G[B]] = F.contramap(fga)(gb => G.contramap(gb)(f)) - } - - trait CovariantComposite[F[_], G[_]] extends Contravariant[Lambda[X => F[G[X]]]] { - def F: Contravariant[F] - def G: Functor[G] - def contramap[A, B](fga: F[G[A]])(f: B => A): F[G[B]] = F.contramap(fga)(gb => G.map(gb)(f)) - } } diff --git a/core/src/main/scala/cats/functor/Invariant.scala b/core/src/main/scala/cats/functor/Invariant.scala index a8d969d8a5..2851958418 100644 --- a/core/src/main/scala/cats/functor/Invariant.scala +++ b/core/src/main/scala/cats/functor/Invariant.scala @@ -8,41 +8,9 @@ import simulacrum.typeclass */ @typeclass trait Invariant[F[_]] { self => def imap[A, B](fa: F[A])(f: A => B)(g: B => A): F[B] - - /** - * Compose the Invariant Functor F with a normal (Covariant) Functor to get a new Invariant Functor for [F[G[_]]. - */ - def composeWithFunctor[G[_]](implicit GG: Functor[G]): Invariant[Lambda[X => F[G[X]]]] = new Invariant.CovariantComposite[F, G] { - def F: Invariant[F] = self - def G: Functor[G] = GG - } - - /** - * Compose the Invariant Functor F with a Contravariant Functor to get a new Invariant Functor for [F[G[_]]]. - */ - def composeWithContravariant[G[_]](implicit GG: Contravariant[G]): Invariant[Lambda[X => F[G[X]]]] = new Invariant.ContravariantComposite[F, G] { - def F: Invariant[F] = self - def G: Contravariant[G] = GG - } } -object Invariant extends AlgebraInvariantInstances { - trait CovariantComposite[F[_], G[_]] extends Invariant[Lambda[X => F[G[X]]]] { - def F: Invariant[F] - def G: Functor[G] - - override def imap[A, B](fga: F[G[A]])(f: A => B)(g: B => A): F[G[B]] = - F.imap(fga)(ga => G.map(ga)(f))(gb => G.map(gb)(g)) - } - - trait ContravariantComposite[F[_], G[_]] extends Invariant[Lambda[X => F[G[X]]]] { - def F: Invariant[F] - def G: Contravariant[G] - - override def imap[A, B](fga: F[G[A]])(f: A => B)(g: B => A): F[G[B]] = - F.imap(fga)(ga => G.contramap(ga)(g))(gb => G.contramap(gb)(f)) - } -} +object Invariant extends AlgebraInvariantInstances /** * Invariant instances for types that are housed in Algebra and therefore diff --git a/docs/src/main/tut/applicative.md b/docs/src/main/tut/applicative.md index 833c7acc89..8baf35be45 100644 --- a/docs/src/main/tut/applicative.md +++ b/docs/src/main/tut/applicative.md @@ -35,7 +35,9 @@ operation will lift the passed value into one context, and the result into the other context: ```tut -(Applicative[List] compose Applicative[Option]).pure(1) +import cats.data.Nested +val nested = Applicative[Nested[List, Option, ?]].pure(1) +val unwrapped = nested.value ``` ## Applicative Functors & Monads diff --git a/docs/src/main/tut/apply.md b/docs/src/main/tut/apply.md index 9a69e14a66..cc816fb7f5 100644 --- a/docs/src/main/tut/apply.md +++ b/docs/src/main/tut/apply.md @@ -48,12 +48,14 @@ Apply[Option].map(None)(double) ### compose -And like functors, `Apply` instances also compose: +And like functors, `Apply` instances also compose (via the `Nested` data type): ```tut -val listOpt = Apply[List] compose Apply[Option] +import cats.data.Nested +val listOpt = Nested[List, Option, Int](List(Some(1), None, Some(3))) val plusOne = (x:Int) => x + 1 -listOpt.ap(List(Some(plusOne)))(List(Some(1), None, Some(3))) +val f = Nested[List, Option, Int => Int](List(Some(plusOne))) +Apply[Nested[List, Option, ?]].ap(f)(listOpt) ``` ### ap diff --git a/docs/src/main/tut/foldable.md b/docs/src/main/tut/foldable.md index 81e4ea09fa..08062ff566 100644 --- a/docs/src/main/tut/foldable.md +++ b/docs/src/main/tut/foldable.md @@ -64,10 +64,11 @@ prints.value Foldable[List].dropWhile_(List[Int](2,4,5,6,7))(_ % 2 == 0) Foldable[List].dropWhile_(List[Int](1,2,4,5,6,7))(_ % 2 == 0) -val FoldableListOption = Foldable[List].compose[Option] -FoldableListOption.fold(List(Option(1), Option(2), Option(3), Option(4))) -FoldableListOption.fold(List(Option(1), Option(2), Option(3), None)) -FoldableListOption.fold(List(Option("1"), Option("2"), Option("3"), None)) +import cats.data.Nested +val listOption0 = Nested(List(Option(1), Option(2), Option(3))) +val listOption1 = Nested(List(Option(1), Option(2), None)) +Foldable[Nested[List, Option, ?]].fold(listOption0) +Foldable[Nested[List, Option, ?]].fold(listOption1) ``` Hence when defining some new data structure, if we can define a `foldLeft` and diff --git a/docs/src/main/tut/functor.md b/docs/src/main/tut/functor.md index 1d31cd809e..4e6d52b907 100644 --- a/docs/src/main/tut/functor.md +++ b/docs/src/main/tut/functor.md @@ -109,13 +109,13 @@ Functor[List].fproduct(source)(len).toMap ### compose Functors compose! Given any functor `F[_]` and any functor `G[_]` we can -create a new functor `F[G[_]]` by composing them: +create a new functor `F[G[_]]` by composing them via the `Nested` data type: ```tut -val listOpt = Functor[List] compose Functor[Option] -listOpt.map(List(Some(1), None, Some(3)))(_ + 1) -val optList = Functor[Option] compose Functor[List] -optList.map(Some(List(1, 2, 3)))(_ + 1) -val listOptList = listOpt compose Functor[List] -listOptList.map(List(Some(List(1,2)), None, Some(List(3,4))))(_ + 1) +import cats.data.Nested +val listOpt = Nested[List, Option, Int](List(Some(1), None, Some(3))) +Functor[Nested[List, Option, ?]].map(listOpt)(_ + 1) + +val optList = Nested[Option, List, Int](Some(List(1, 2, 3))) +Functor[Nested[Option, List, ?]].map(optList)(_ + 1) ``` diff --git a/laws/src/main/scala/cats/laws/BitraverseLaws.scala b/laws/src/main/scala/cats/laws/BitraverseLaws.scala index b76573ccc3..d22172d4f3 100644 --- a/laws/src/main/scala/cats/laws/BitraverseLaws.scala +++ b/laws/src/main/scala/cats/laws/BitraverseLaws.scala @@ -1,6 +1,8 @@ package cats package laws +import cats.data.Nested + trait BitraverseLaws[F[_, _]] extends BifoldableLaws[F] with BifunctorLaws[F] { implicit override def F: Bitraverse[F] @@ -19,16 +21,13 @@ trait BitraverseLaws[F[_, _]] extends BifoldableLaws[F] with BifunctorLaws[F] { val fg = F.bitraverse(fab)(f, g) val hi = G.map(fg)(f => F.bitraverse(f)(h, i)) - type GCompose[X] = G[G[X]] - val GCompose = G.compose[G] - val c = - F.bitraverse[GCompose, A, B, E, H](fab)( - a => G.map(f(a))(h), - b => G.map(g(b))(i) - )(GCompose) + F.bitraverse[Nested[G, G, ?], A, B, E, H](fab)( + a => Nested(G.map(f(a))(h)), + b => Nested(G.map(g(b))(i)) + ) - hi <-> c + hi <-> c.value } } diff --git a/laws/src/main/scala/cats/laws/TraverseLaws.scala b/laws/src/main/scala/cats/laws/TraverseLaws.scala index 5ae13c2c21..add5bb70b1 100644 --- a/laws/src/main/scala/cats/laws/TraverseLaws.scala +++ b/laws/src/main/scala/cats/laws/TraverseLaws.scala @@ -2,7 +2,7 @@ package cats package laws import cats.Id -import cats.data.Const +import cats.data.{Const, Nested} import cats.syntax.traverse._ import cats.syntax.foldable._ @@ -20,11 +20,10 @@ trait TraverseLaws[F[_]] extends FunctorLaws[F] with FoldableLaws[F] { )(implicit N: Applicative[N], M: Applicative[M] - ): IsEq[M[N[F[C]]]] = { - implicit val MN = M.compose(N) - type MN[Z] = M[N[Z]] - val lhs: MN[F[C]] = M.map(fa.traverse(f))(fb => fb.traverse(g)) - val rhs: MN[F[C]] = fa.traverse[MN, C](a => M.map(f(a))(g)) + ): IsEq[Nested[M, N, F[C]]] = { + + val lhs = Nested(M.map(fa.traverse(f))(fb => fb.traverse(g))) + val rhs = fa.traverse[Nested[M, N, ?], C](a => Nested(M.map(f(a))(g))) lhs <-> rhs } diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index d22eabb378..2299bf29f7 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -76,6 +76,9 @@ object arbitrary extends ArbitraryInstances0 { implicit def function0Arbitrary[A: Arbitrary]: Arbitrary[() => A] = Arbitrary(getArbitrary[A].map(() => _)) + + implicit def nestedArbitrary[F[_], G[_], A](implicit FG: Arbitrary[F[G[A]]]): Arbitrary[Nested[F, G, A]] = + Arbitrary(FG.arbitrary.map(Nested(_))) } private[discipline] sealed trait ArbitraryInstances0 { diff --git a/tests/src/test/scala/cats/tests/ComposeTests.scala b/tests/src/test/scala/cats/tests/ComposeTests.scala deleted file mode 100644 index 2cccfa1eff..0000000000 --- a/tests/src/test/scala/cats/tests/ComposeTests.scala +++ /dev/null @@ -1,65 +0,0 @@ -package cats -package tests - -import cats.data.{ NonEmptyList, NonEmptyVector } -import cats.laws.discipline.{ AlternativeTests, ApplicativeTests, FoldableTests, CartesianTests, MonoidKTests, SemigroupKTests, arbitrary }, arbitrary._ - -class ComposeTests extends CatsSuite { - // we have a lot of generated lists of lists in these tests. We have to tell - // Scalacheck to calm down a bit so we don't hit memory and test duration - // issues. - implicit override val generatorDrivenConfig: PropertyCheckConfiguration = - PropertyCheckConfig(maxSize = 5, minSuccessful = 20) - - { - // Alternative composition - - implicit val alternativeListVector: Alternative[Lambda[A => List[Vector[A]]]] = Alternative[List] compose Alternative[Vector] - implicit val iso = CartesianTests.Isomorphisms.invariant[Lambda[A => List[Vector[A]]]] - - checkAll("Alternative[Lambda[A => List[Vector[A]]]]", AlternativeTests[Lambda[A => List[Vector[A]]]].alternative[Int, Int, Int]) - } - - { - // Applicative composition - - implicit val applicativeListVector: Applicative[Lambda[A => List[Vector[A]]]] = Applicative[List] compose Applicative[Vector] - implicit val iso = CartesianTests.Isomorphisms.invariant[Lambda[A => List[Vector[A]]]] - - checkAll("Applicative[Lambda[A => List[Vector[A]]]]", ApplicativeTests[Lambda[A => List[Vector[A]]]].applicative[Int, Int, Int]) - } - - { - // Foldable composition - - implicit val foldableListVector: Foldable[Lambda[A => List[Vector[A]]]] = Foldable[List] compose Foldable[Vector] - - checkAll("Foldable[Lambda[A => List[Vector[A]]]]", FoldableTests[Lambda[A => List[Vector[A]]]].foldable[Int, Int]) - } - - { - // MonoidK composition - - implicit val monoidKListVector: MonoidK[Lambda[A => List[Vector[A]]]] = MonoidK[List].composeK[Vector] - - checkAll("MonoidK[Lambda[A => List[Vector[A]]]]", MonoidKTests[Lambda[A => List[Vector[A]]]].monoidK[Int]) - } - - { - // Reducible composition - - implicit val reducibleListVector: Reducible[Lambda[A => NonEmptyList[NonEmptyVector[A]]]] = - Reducible[NonEmptyList] compose Reducible[NonEmptyVector] - - // No Reducible-specific laws, so check the Foldable laws are satisfied - checkAll("Reducible[Lambda[A => List[Vector[A]]]]", FoldableTests[Lambda[A => NonEmptyList[NonEmptyVector[A]]]].foldable[Int, Int]) - } - - { - // SemigroupK composition - - implicit val semigroupKListVector: SemigroupK[Lambda[A => List[Vector[A]]]] = SemigroupK[List].composeK[Vector] - - checkAll("SemigroupK[Lambda[A => List[Vector[A]]]]", SemigroupKTests[Lambda[A => List[Vector[A]]]].semigroupK[Int]) - } -} diff --git a/tests/src/test/scala/cats/tests/FunctorTests.scala b/tests/src/test/scala/cats/tests/FunctorTests.scala index d934e0a39c..9af4db22ae 100644 --- a/tests/src/test/scala/cats/tests/FunctorTests.scala +++ b/tests/src/test/scala/cats/tests/FunctorTests.scala @@ -1,32 +1,7 @@ package cats package tests -import cats.data.Xor -import cats.functor.Contravariant -import cats.laws.discipline.{ContravariantTests, FunctorTests, SerializableTests} -import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq._ -import cats.laws.discipline.eq.showEq - class FunctorTest extends CatsSuite { - type OptionXor[A] = Option[Xor[String, A]] - - val optionXorFunctor: Functor[OptionXor] = - Functor[Option].compose[Xor[String, ?]] - checkAll("Option compose Xor", FunctorTests(optionXorFunctor).functor[Int, Int, Int]) - checkAll("Functor[Option compose Xor]", SerializableTests.serializable(optionXorFunctor)) - - val optionComposeWithFunctorXor: Functor[OptionXor] = - Functor[Option].composeWithFunctor[Xor[String, ?]] - checkAll("Option composeWithFunctor Xor", FunctorTests(optionComposeWithFunctorXor).functor[Int, Int, Int]) - checkAll("Functor[Option composeWithFunctor Xor]", SerializableTests.serializable(optionComposeWithFunctorXor)) - - type OptionShow[A] = Option[Show[A]] - val optionShowContravariant: Contravariant[OptionShow] = - Functor[Option].composeWithContravariant[Show] - checkAll("Option composeWithContravariant Show", ContravariantTests(optionShowContravariant).contravariant[Int, Int, Int]) - checkAll("Contravariant[Option composeWithContravariant Show]", SerializableTests.serializable(optionShowContravariant)) - test("void replaces values with unit preserving structure") { forAll { (l: List[Int], o: Option[Int], m: Map[String, Int]) => l.void should === (List.fill(l.length)(())) diff --git a/tests/src/test/scala/cats/tests/ListWrapper.scala b/tests/src/test/scala/cats/tests/ListWrapper.scala index 06d58e5d4b..8a6a8faf2d 100644 --- a/tests/src/test/scala/cats/tests/ListWrapper.scala +++ b/tests/src/test/scala/cats/tests/ListWrapper.scala @@ -84,6 +84,8 @@ object ListWrapper { val monad: Monad[ListWrapper] = monadCombine + val applicative: Applicative[ListWrapper] = monadCombine + /** apply is taken due to ListWrapper being a case class */ val applyInstance: Apply[ListWrapper] = monadCombine diff --git a/tests/src/test/scala/cats/tests/NestedTests.scala b/tests/src/test/scala/cats/tests/NestedTests.scala new file mode 100644 index 0000000000..6d3296f624 --- /dev/null +++ b/tests/src/test/scala/cats/tests/NestedTests.scala @@ -0,0 +1,82 @@ +package cats +package tests + +import cats.data._ +import cats.functor.Contravariant +import cats.laws.discipline._ +import cats.laws.discipline.CartesianTests.Isomorphisms._ +import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq.showEq + +class NestedTests extends CatsSuite { + // we have a lot of generated lists of lists in these tests. We have to tell + // Scalacheck to calm down a bit so we don't hit memory and test duration + // issues. + implicit override val generatorDrivenConfig: PropertyCheckConfiguration = + PropertyCheckConfig(maxSize = 5, minSuccessful = 20) + + implicit val iso = { + implicit val instance = ListWrapper.functor + invariant[Nested[List, ListWrapper, ?]] + } + + { + // Functor composition + implicit val instance = ListWrapper.functor + checkAll("Nested[Option, ListWrapper, ?]", FunctorTests[Nested[Option, ListWrapper, ?]].functor[Int, Int, Int]) + checkAll("Functor[Nested[Option, ListWrapper, ?]]", SerializableTests.serializable(Functor[Nested[Option, ListWrapper, ?]])) + } + + { + // Covariant + contravariant functor composition + checkAll("Nested[Option, Show, ?]", ContravariantTests[Nested[Option, Show, ?]].contravariant[Int, Int, Int]) + checkAll("Contravariant[Nested[Option, Show, ?]]", SerializableTests.serializable(Contravariant[Nested[Option, Show, ?]])) + } + + { + // Applicative composition + implicit val instance = ListWrapper.applicative + checkAll("Nested[List, ListWrapper, ?]", ApplicativeTests[Nested[List, ListWrapper, ?]].applicative[Int, Int, Int]) + checkAll("Applicative[Nested[List, ListWrapper, ?]]", SerializableTests.serializable(Applicative[Nested[List, ListWrapper, ?]])) + } + + { + // Alternative composition + implicit val instance = ListWrapper.alternative + checkAll("Nested[List, ListWrapper, ?]", AlternativeTests[Nested[List, ListWrapper, ?]].alternative[Int, Int, Int]) + checkAll("Alternative[Nested[List, ListWrapper, ?]]", SerializableTests.serializable(Alternative[Nested[List, ListWrapper, ?]])) + } + + { + // Foldable composition + implicit val instance = ListWrapper.foldable + checkAll("Nested[List, ListWrapper, ?]", FoldableTests[Nested[List, ListWrapper, ?]].foldable[Int, Int]) + checkAll("Foldable[Nested[List, ListWrapper, ?]]", SerializableTests.serializable(Foldable[Nested[List, ListWrapper, ?]])) + } + + { + // SI-2712? It can resolve Reducible[NonEmptyList] and Reducible[NonEmptyVector] but not + // Reducible[Nested[NonEmptyList, NonEmptyVector, ?]] + // Similarly for Arbitrary. + implicit val reducible = Nested.nestedReducible[NonEmptyList, NonEmptyVector] + implicit val arbitrary0 = nestedArbitrary[NonEmptyList, NonEmptyVector, Int] + implicit val arbitrary1 = nestedArbitrary[NonEmptyList, NonEmptyVector, Option[Int]] + + checkAll("Nested[NonEmptyList, NonEmptyVector, ?]", ReducibleTests[Nested[NonEmptyList, NonEmptyVector, ?]].reducible[Option, Int, Int]) + checkAll("Reducible[Nested[NonEmptyList, NonEmptyVector, ?]]", SerializableTests.serializable(reducible)) + } + + { + // SemigroupK composition + implicit val instance = ListWrapper.semigroupK + checkAll("Nested[List, ListWrapper, ?]", SemigroupKTests[Nested[List, ListWrapper, ?]].semigroupK[Int]) + checkAll("SemigroupK[Nested[List, ListWrapper, ?]]", SerializableTests.serializable(SemigroupK[Nested[List, ListWrapper, ?]])) + } + + { + // MonoidK composition + implicit val instance = ListWrapper.monoidK + checkAll("Nested[List, ListWrapper, ?]", MonoidKTests[Nested[List, ListWrapper, ?]].monoidK[Int]) + checkAll("MonoidK[Nested[List, ListWrapper, ?]]", SerializableTests.serializable(MonoidK[Nested[List, ListWrapper, ?]])) + } +} diff --git a/tests/src/test/scala/cats/tests/ReducibleTests.scala b/tests/src/test/scala/cats/tests/ReducibleTests.scala deleted file mode 100644 index d834a99e02..0000000000 --- a/tests/src/test/scala/cats/tests/ReducibleTests.scala +++ /dev/null @@ -1,18 +0,0 @@ -package cats -package tests - -import cats.data.{NonEmptyList, NonEmptyVector} -import cats.laws.discipline.{ReducibleTests, SerializableTests} -import cats.laws.discipline.arbitrary.oneAndArbitrary - -class ReducibleTest extends CatsSuite { - // Lots of collections here.. telling ScalaCheck to calm down a bit - implicit override val generatorDrivenConfig: PropertyCheckConfiguration = - PropertyCheckConfig(maxSize = 5, minSuccessful = 20) - - type NEVL[A] = NonEmptyList[NonEmptyVector[A]] - val nevlReducible: Reducible[NEVL] = - Reducible[NonEmptyList].compose[NonEmptyVector] - checkAll("NonEmptyList compose NonEmptyVector", ReducibleTests(nevlReducible).reducible[Option, Int, String]) - checkAll("Reducible[NonEmptyList compose NonEmptyVector]", SerializableTests.serializable(nevlReducible)) -} diff --git a/tests/src/test/scala/cats/tests/WordCountTest.scala b/tests/src/test/scala/cats/tests/WordCountTest.scala index e1dfaeb154..131b70cadd 100644 --- a/tests/src/test/scala/cats/tests/WordCountTest.scala +++ b/tests/src/test/scala/cats/tests/WordCountTest.scala @@ -43,7 +43,7 @@ class WordCountTest extends CatsSuite { val wordCountState = allResults.first.first val lineCount = allResults.first.second val charCount = allResults.second - val wordCount = wordCountState.runA(false).value + val wordCount = wordCountState.value.runA(false).value charCount.getConst should === (96) lineCount.getConst should === (2) wordCount.getConst should === (17)