Skip to content

Commit

Permalink
Merge pull request #1021 from adelbertc/compose
Browse files Browse the repository at this point in the history
Replace type class composition with Nested data type
  • Loading branch information
non committed Jun 5, 2016
2 parents 3608287 + 86ce5ed commit d541f5d
Show file tree
Hide file tree
Showing 27 changed files with 603 additions and 426 deletions.
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

0 comments on commit d541f5d

Please sign in to comment.