Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace type class composition with Nested data type #1021

Merged
merged 9 commits into from
Jun 5, 2016
19 changes: 4 additions & 15 deletions core/src/main/scala/cats/Alternative.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,9 @@ package cats
import simulacrum.typeclass

@typeclass trait Alternative[F[_]] extends Applicative[F] with MonoidK[F] { self =>

/**
* Compose this `Alternative` instance with an [[Applicative]] instance.
*/
override def compose[G[_]](implicit GG: Applicative[G]): Alternative[λ[α => F[G[α]]]] =
new CompositeAlternative[F, G] {
implicit def F: Alternative[F] = self
implicit def G: Applicative[G] = GG
override def compose[G[_]: Applicative]: Alternative[Lambda[A => F[G[A]]]] =
new ComposedAlternative[F, G] {
val F = self
val G = Applicative[G]
}
}

trait CompositeAlternative[F[_], G[_]]
extends Alternative[λ[α => F[G[α]]]] with CompositeApplicative[F, G] with CompositeMonoidK[F, G] {

implicit def F: Alternative[F]
implicit def G: Applicative[G]
}
30 changes: 6 additions & 24 deletions core/src/main/scala/cats/Applicative.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats

import simulacrum.typeclass
import cats.std.list._
import simulacrum.typeclass

/**
* Applicative functor.
Expand Down Expand Up @@ -38,33 +38,15 @@ import cats.std.list._
def replicateA[A](n: Int, fa: F[A]): F[List[A]] =
sequence(List.fill(n)(fa))

/**
* 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))
*/
def compose[G[_]](implicit GG : Applicative[G]): Applicative[λ[α => F[G[α]]]] =
new CompositeApplicative[F,G] {
implicit def F: Applicative[F] = self
implicit def G: Applicative[G] = GG

}

def traverse[A, G[_], B](value: G[A])(f: A => F[B])(implicit G: Traverse[G]): F[G[B]] =
G.traverse(value)(f)(this)

def sequence[G[_], A](as: G[F[A]])(implicit G: Traverse[G]): F[G[A]] =
G.sequence(as)(this)

}

trait CompositeApplicative[F[_],G[_]]
extends Applicative[λ[α => F[G[α]]]] with CompositeApply[F,G] {

implicit def F: Applicative[F]
implicit def G: Applicative[G]

def pure[A](a: A): F[G[A]] = F.pure(G.pure(a))
def compose[G[_]: Applicative]: Applicative[Lambda[A => F[G[A]]]] =
new ComposedApplicative[F, G] {
val F = self
val G = Applicative[G]
}
}
37 changes: 4 additions & 33 deletions core/src/main/scala/cats/Apply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,38 +58,9 @@ trait Apply[F[_]] extends Functor[F] with Cartesian[F] with ApplyArityFunctions[
def map2Eval[A, B, Z](fa: F[A], fb: Eval[F[B]])(f: (A, B) => Z): Eval[F[Z]] =
fb.map(fb => map2(fa, fb)(f))

/**
* 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))
* }}}
*/
def compose[G[_]](implicit GG: Apply[G]): Apply[Lambda[X => F[G[X]]]] =
new CompositeApply[F, G] {
def F: Apply[F] = self
def G: Apply[G] = GG
def compose[G[_]: Apply]: Apply[Lambda[A => F[G[A]]]] =
new ComposedApply[F, G] {
val F = self
val G = Apply[G]
}

}

trait CompositeApply[F[_], G[_]]
extends Apply[Lambda[X => F[G[X]]]] with Functor.Composite[F, G] {
def F: Apply[F]
def G: Apply[G]

def ap[A, B](f: F[G[A => B]])(fa: F[G[A]]): F[G[B]] =
F.ap(F.map(f)(gab => G.ap(gab)(_)))(fa)

override def product[A, B](fa: F[G[A]], fb: F[G[B]]): F[G[(A, B)]] =
F.map2(fa, fb)(G.product)
}
131 changes: 131 additions & 0 deletions core/src/main/scala/cats/Composed.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package cats

import cats.functor._

private[cats] trait ComposedInvariant[F[_], G[_]] extends Invariant[Lambda[A => F[G[A]]]] { outer =>
def F: Invariant[F]
def G: Invariant[G]

override def imap[A, B](fga: F[G[A]])(f: A => B)(g: B => A): F[G[B]] =
F.imap(fga)(ga => G.imap(ga)(f)(g))(gb => G.imap(gb)(g)(f))
}

private[cats] trait ComposedFunctor[F[_], G[_]] extends Functor[Lambda[A => F[G[A]]]] with ComposedInvariant[F, G] { outer =>
def F: Functor[F]
def G: Functor[G]

override def map[A, B](fga: F[G[A]])(f: A => B): F[G[B]] =
F.map(fga)(ga => G.map(ga)(f))
}

private[cats] trait ComposedApply[F[_], G[_]] extends Apply[Lambda[A => F[G[A]]]] with ComposedFunctor[F, G] { outer =>
def F: Apply[F]
def G: Apply[G]

override def ap[A, B](fgf: F[G[A => B]])(fga: F[G[A]]): F[G[B]] =
F.ap(F.map(fgf)(gf => G.ap(gf)(_)))(fga)

override def product[A, B](fga: F[G[A]], fgb: F[G[B]]): F[G[(A, B)]] =
F.map2(fga, fgb)(G.product)
}

private[cats] trait ComposedApplicative[F[_], G[_]] extends Applicative[Lambda[A => F[G[A]]]] with ComposedApply[F, G] { outer =>
def F: Applicative[F]
def G: Applicative[G]

override def pure[A](x: A): F[G[A]] = F.pure(G.pure(x))
}

private[cats] trait ComposedSemigroupK[F[_], G[_]] extends SemigroupK[Lambda[A => F[G[A]]]] { outer =>
def F: SemigroupK[F]

override def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.combineK(x, y)
}

private[cats] trait ComposedMonoidK[F[_], G[_]] extends MonoidK[Lambda[A => F[G[A]]]] with ComposedSemigroupK[F, G] { outer =>
def F: MonoidK[F]

override def empty[A]: F[G[A]] = F.empty
}

private[cats] trait ComposedAlternative[F[_], G[_]] extends Alternative[Lambda[A => F[G[A]]]] with ComposedApplicative[F, G] with ComposedMonoidK[F, G] { outer =>
def F: Alternative[F]
}

private[cats] trait ComposedFoldable[F[_], G[_]] extends Foldable[Lambda[A => F[G[A]]]] { outer =>
def F: Foldable[F]
def G: Foldable[G]

override def foldLeft[A, B](fga: F[G[A]], b: B)(f: (B, A) => B): B =
F.foldLeft(fga, b)((b, a) => G.foldLeft(a, b)(f))

override def foldRight[A, B](fga: F[G[A]], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
F.foldRight(fga, lb)((ga, lb) => G.foldRight(ga, lb)(f))
}

private[cats] trait ComposedTraverse[F[_], G[_]] extends Traverse[Lambda[A => F[G[A]]]] with ComposedFoldable[F, G] with ComposedFunctor[F, G] { outer =>
def F: Traverse[F]
def G: Traverse[G]

override def traverse[H[_]: Applicative, A, B](fga: F[G[A]])(f: A => H[B]): H[F[G[B]]] =
F.traverse(fga)(ga => G.traverse(ga)(f))
}

private[cats] trait ComposedReducible[F[_], G[_]] extends Reducible[Lambda[A => F[G[A]]]] with ComposedFoldable[F, G] { outer =>
def F: Reducible[F]
def G: Reducible[G]

override def reduceLeftTo[A, B](fga: 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)(toB) { (b, ga) =>
G.foldLeft(ga, b)(g)
}
}

override def reduceRightTo[A, B](fga: 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)(toB) { (ga, lb) =>
G.foldRight(ga, lb)(g)
}
}
}

private[cats] trait ComposedContravariant[F[_], G[_]] extends Functor[Lambda[A => F[G[A]]]] { outer =>
def F: Contravariant[F]
def G: Contravariant[G]

override def map[A, B](fga: F[G[A]])(f: A => B): F[G[B]] =
F.contramap(fga)(gb => G.contramap(gb)(f))
}

private[cats] trait ComposedContravariantCovariant[F[_], G[_]] extends Contravariant[Lambda[A => F[G[A]]]] { outer =>
def F: Contravariant[F]
def G: Functor[G]

override def contramap[A, B](fga: F[G[A]])(f: B => A): F[G[B]] =
F.contramap(fga)(gb => G.map(gb)(f))
}

private[cats] trait ComposedCovariantContravariant[F[_], G[_]] extends Contravariant[Lambda[A => F[G[A]]]] { outer =>
def F: Functor[F]
def G: Contravariant[G]

override def contramap[A, B](fga: F[G[A]])(f: B => A): F[G[B]] =
F.map(fga)(ga => G.contramap(ga)(f))
}

private[cats] trait ComposedInvariantCovariant[F[_], G[_]] extends Invariant[Lambda[A => F[G[A]]]] { outer =>
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))
}

private[cats] trait ComposedInvariantContravariant[F[_], G[_]] extends Invariant[Lambda[A => F[G[A]]]] { outer =>
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))
}
30 changes: 3 additions & 27 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -269,37 +269,13 @@ import simulacrum.typeclass
def nonEmpty[A](fa: F[A]): Boolean =
!isEmpty(fa)

/**
* Compose this `Foldable[F]` with a `Foldable[G]` to create
* a `Foldable[F[G]]` instance.
*/
def compose[G[_]](implicit ev: Foldable[G]): Foldable[λ[α => F[G[α]]]] =
new CompositeFoldable[F, G] {
def compose[G[_]: Foldable]: Foldable[Lambda[A => F[G[A]]]] =
new ComposedFoldable[F, G] {
val F = self
val G = ev
val G = Foldable[G]
}
}

/**
* Methods that apply to 2 nested Foldable instances
*/
trait CompositeFoldable[F[_], G[_]] extends Foldable[λ[α => F[G[α]]]] {
implicit def F: Foldable[F]
implicit def G: Foldable[G]

/**
* Left associative fold on F[G[A]] using 'f'
*/
def foldLeft[A, B](fga: F[G[A]], b: B)(f: (B, A) => B): B =
F.foldLeft(fga, b)((b, a) => G.foldLeft(a, b)(f))

/**
* Right associative lazy fold on `F` using the folding function 'f'.
*/
def foldRight[A, B](fga: F[G[A]], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
F.foldRight(fga, lb)((ga, lb) => G.foldRight(ga, lb)(f))
}

object Foldable {
def iterateRight[A, B](it: Iterator[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = {
def loop(): Eval[B] =
Expand Down
52 changes: 12 additions & 40 deletions core/src/main/scala/cats/Functor.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package cats

import cats.functor.Contravariant

import simulacrum.typeclass
import functor.Contravariant

/**
* Functor.
Expand All @@ -15,29 +16,6 @@ import functor.Contravariant

def imap[A, B](fa: F[A])(f: A => B)(fi: B => A): F[B] = map(fa)(f)

/**
* Compose this functor F with a functor G to produce a composite
* Functor on G[F[_]], with a map method which uses an A => B to
* map a G[F[A]] to a G[F[B]].
*/
def compose[G[_]](implicit GG: Functor[G]): Functor[Lambda[X => F[G[X]]]] = new Functor.Composite[F, G] {
def F: Functor[F] = self
def G: Functor[G] = GG
}

/**
* Compose this functor F with a Contravariant Functor G to produce a new Contravariant Functor
* on F[G[_]].
*/
override def composeWithContravariant[G[_]](implicit GG: Contravariant[G]): Contravariant[Lambda[X => F[G[X]]]] =
new Functor.ContravariantComposite[F, G] {
def F: Functor[F] = self
def G: Contravariant[G] = GG
}

override def composeWithFunctor[G[_]: Functor]: Functor[Lambda[X => F[G[X]]]] = compose[G]


// derived methods

/**
Expand All @@ -60,22 +38,16 @@ import functor.Contravariant
* Replaces the `A` value in `F[A]` with the supplied value.
*/
def as[A, B](fa: F[A], b: B): F[B] = map(fa)(_ => b)
}

object Functor {
trait Composite[F[_], G[_]] extends Functor[Lambda[X => F[G[X]]]] {
def F: Functor[F]
def G: Functor[G]

override def map[A, B](fa: F[G[A]])(f: A => B): F[G[B]] =
F.map(fa)(G.lift(f))
}

trait ContravariantComposite[F[_], G[_]] extends Contravariant[Lambda[X => F[G[X]]]] {
def F: Functor[F]
def G: Contravariant[G]
def compose[G[_]: Functor]: Functor[Lambda[A => F[G[A]]]] =
new ComposedFunctor[F, G] {
val F = self
val G = Functor[G]
}

override def contramap[A, B](fa: F[G[A]])(f: B => A): F[G[B]] =
F.map(fa)(ga => G.contramap(ga)(f))
}
override def composeContravariant[G[_]: Contravariant]: Contravariant[Lambda[A => F[G[A]]]] =
new ComposedCovariantContravariant[F, G] {
val F = self
val G = Contravariant[G]
}
}
19 changes: 4 additions & 15 deletions core/src/main/scala/cats/MonoidK.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,6 @@ import simulacrum.typeclass
*/
def empty[A]: F[A]

/**
* Compose this MonoidK with an arbitrary type constructor
*/
override def composeK[G[_]]: MonoidK[λ[α => F[G[α]]]] =
new CompositeMonoidK[F, G] {
implicit def F: MonoidK[F] = self
}

/**
* Given a type A, create a concrete Monoid[F[A]].
*/
Expand All @@ -45,12 +37,9 @@ import simulacrum.typeclass
def empty: F[A] = self.empty
def combine(x: F[A], y: F[A]): F[A] = self.combineK(x, y)
}
}

trait CompositeMonoidK[F[_],G[_]]
extends MonoidK[λ[α => F[G[α]]]] with CompositeSemigroupK[F, G] {

implicit def F: MonoidK[F]

def empty[A]: F[G[A]] = F.empty
override def compose[G[_]]: MonoidK[Lambda[A => F[G[A]]]] =
new ComposedMonoidK[F, G] {
val F = self
}
}
Loading