Skip to content

Commit

Permalink
Generalize ApplyBuilder to Monoidal.
Browse files Browse the repository at this point in the history
  • Loading branch information
julienrf committed Oct 2, 2015
1 parent 5b45c9e commit ff5b4c4
Show file tree
Hide file tree
Showing 19 changed files with 198 additions and 65 deletions.
13 changes: 4 additions & 9 deletions core/src/main/scala/cats/Apply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import simulacrum.typeclass
* Must obey the laws defined in cats.laws.ApplyLaws.
*/
@typeclass(excludeParents=List("ApplyArityFunctions"))
trait Apply[F[_]] extends Functor[F] with ApplyArityFunctions[F] { self =>
trait Apply[F[_]] extends Functor[F] with Monoidal[F] with ApplyArityFunctions[F] { self =>

/**
* Given a value and a function in the Apply context, applies the
Expand All @@ -22,14 +22,6 @@ trait Apply[F[_]] extends Functor[F] with ApplyArityFunctions[F] { self =>
def ap2[A, B, Z](fa: F[A], fb: F[B])(f: F[(A, B) => Z]): F[Z] =
ap(fb)(ap(fa)(map(f)(f => (a: A) => (b: B) => f(a, b))))

/**
* Applies the pure (binary) function f to the effectful values fa and fb.
*
* map2 can be seen as a binary version of [[cats.Functor]]#map.
*/
def map2[A, B, Z](fa: F[A], fb: F[B])(f: (A, B) => Z): F[Z] =
ap(fb)(map(fa)(a => (b: B) => f(a, b)))

/**
* Two sequentially dependent Applys can be composed.
*
Expand All @@ -45,6 +37,9 @@ trait Apply[F[_]] extends Functor[F] with ApplyArityFunctions[F] { self =>
def F: Apply[F] = self
def G: Apply[G] = GG
}

override def product[A, B](fa: F[A], fb: F[B]): F[(A, B)] = ap(fb)(map(fa)(a => b => (a, b)))

}

trait CompositeApply[F[_], G[_]]
Expand Down
12 changes: 11 additions & 1 deletion core/src/main/scala/cats/Functor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import functor.Contravariant
*
* Must obey the laws defined in cats.laws.FunctorLaws.
*/
@typeclass trait Functor[F[_]] extends functor.Invariant[F] { self =>
@typeclass(excludeParents=List("FunctorArityFunctions"))
trait Functor[F[_]] extends functor.Invariant[F] with FunctorArityFunctions[F] { self =>
def map[A, B](fa: F[A])(f: A => B): F[B]

def imap[A, B](fa: F[A])(f: A => B)(fi: B => A): F[B] = map(fa)(f)
Expand Down Expand Up @@ -60,6 +61,15 @@ 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)

/**
* Applies the pure (binary) function f to the effectful values fa and fb.
*
* map2 can be seen as a binary version of [[cats.Functor]]#map.
*/
def map2[A, B, Z](fa: F[A], fb: F[B])(f: (A, B) => Z)(implicit F: Monoidal[F]): F[Z] =
map(F.product(fa, fb))(f.tupled)

}

object Functor {
Expand Down
13 changes: 13 additions & 0 deletions core/src/main/scala/cats/Monoidal.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package cats

import simulacrum.typeclass

/**
* Monoidal allows us to express uncurried function application within a context,
* whatever the context variance is.
*
* It is worth noting that the couple Monoidal and [[Functor]] is interdefinable with [[Apply]].
*/
@typeclass trait Monoidal[F[_]] {
def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]
}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/data/Streaming.scala
Original file line number Diff line number Diff line change
Expand Up @@ -887,7 +887,7 @@ trait StreamingInstances extends StreamingInstances1 {
def combine[A](xs: Streaming[A], ys: Streaming[A]): Streaming[A] =
xs concat ys

override def map2[A, B, Z](fa: Streaming[A], fb: Streaming[B])(f: (A, B) => Z): Streaming[Z] =
override def map2[A, B, Z](fa: Streaming[A], fb: Streaming[B])(f: (A, B) => Z)(implicit ev: Monoidal[Streaming]): Streaming[Z] =
fa.flatMap(a => fb.map(b => f(a, b)))

def coflatMap[A, B](fa: Streaming[A])(f: Streaming[A] => B): Streaming[B] =
Expand Down
12 changes: 11 additions & 1 deletion core/src/main/scala/cats/functor/Contravariant.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import simulacrum.typeclass
/**
* Must obey the laws defined in cats.laws.ContravariantLaws.
*/
@typeclass trait Contravariant[F[_]] extends Invariant[F] { self =>
@typeclass(excludeParents=List("ContravariantArityFunctions"))
trait Contravariant[F[_]] extends Invariant[F] with ContravariantArityFunctions[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)

Expand All @@ -25,6 +26,15 @@ import simulacrum.typeclass
def G: Functor[G] = G0
}
}

/**
* Applies the pure function f to the effectful values fb and fb.
*
* contramap2 can be seen as a binary version of [[cats.functor.Contravariant]]#contramap.
*/
def contramap2[A, B, Z](fa: F[A], fb: F[B])(f: Z => (A, B))(implicit F: Monoidal[F]): F[Z] =
contramap(F.product(fa, fb))(f)

}

object Contravariant {
Expand Down
14 changes: 13 additions & 1 deletion core/src/main/scala/cats/functor/Invariant.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import simulacrum.typeclass
/**
* Must obey the laws defined in cats.laws.InvariantLaws.
*/
@typeclass trait Invariant[F[_]] extends Any with Serializable { self =>
@typeclass(excludeParents=List("InvariantArityFunctions"))
trait Invariant[F[_]] extends InvariantArityFunctions[F] with Serializable { self =>
def imap[A, B](fa: F[A])(f: A => B)(g: B => A): F[B]

/**
Expand All @@ -32,6 +33,17 @@ import simulacrum.typeclass
def F: Invariant[F] = self
def G: Contravariant[G] = GG
}

/**
* Applies the pure functions f and g to the effectful values fa and fb.
*
* imap2 can be seen as a binary version of [[cats.functor.Invariant]]#imap.
*/
def imap2[A, B, Z](fa: F[A], fb: F[B])(f: (A, B) => Z)(g: Z => (A, B))(implicit F: Monoidal[F]): F[Z] =
imap(F.product(fa, fb))(f.tupled)(g)

def tuple2[A, B](fa: F[A], fb: F[B])(implicit F: Monoidal[F]): F[(A, B)] = F.product(fa, fb)

}

object Invariant extends AlgebraInvariantInstances {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ package object cats {
override def map[A, B](fa: A)(f: A => B): B = f(fa)
override def ap[A, B](fa: A)(ff: A => B): B = ff(fa)
override def flatten[A](ffa: A): A = ffa
override def map2[A, B, Z](fa: A, fb: B)(f: (A, B) => Z): Z = f(fa, fb)
override def map2[A, B, Z](fa: A, fb: B)(f: (A, B) => Z)(implicit ev: Monoidal[Id]): Z = f(fa, fb)
override def lift[A, B](f: A => B): A => B = f
override def imap[A, B](fa: A)(f: A => B)(fi: B => A): B = f(fa)
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/std/list.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ trait ListInstances extends ListInstances1 {
def flatMap[A, B](fa: List[A])(f: A => List[B]): List[B] =
fa.flatMap(f)

override def map2[A, B, Z](fa: List[A], fb: List[B])(f: (A, B) => Z): List[Z] =
override def map2[A, B, Z](fa: List[A], fb: List[B])(f: (A, B) => Z)(implicit ev: Monoidal[List]): List[Z] =
fa.flatMap(a => fb.map(b => f(a, b)))

def coflatMap[A, B](fa: List[A])(f: List[A] => B): List[B] = {
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/std/map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ trait MapInstances extends algebra.std.MapInstances {
override def map[A, B](fa: Map[K, A])(f: A => B): Map[K, B] =
fa.map { case (k, a) => (k, f(a)) }

override def map2[A, B, Z](fa: Map[K, A], fb: Map[K, B])(f: (A, B) => Z): Map[K, Z] =
override def map2[A, B, Z](fa: Map[K, A], fb: Map[K, B])(f: (A, B) => Z)(implicit ev: Monoidal[Map[K, ?]]): Map[K, Z] =
fa.flatMap { case (k, a) => fb.get(k).map(b => (k, f(a, b))) }

override def ap[A, B](fa: Map[K, A])(ff: Map[K, A => B]): Map[K, B] =
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/std/option.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ trait OptionInstances extends OptionInstances1 {
def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] =
fa.flatMap(f)

override def map2[A, B, Z](fa: Option[A], fb: Option[B])(f: (A, B) => Z): Option[Z] =
override def map2[A, B, Z](fa: Option[A], fb: Option[B])(f: (A, B) => Z)(implicit ev: Monoidal[Option]): Option[Z] =
fa.flatMap(a => fb.map(b => f(a, b)))

def coflatMap[A, B](fa: Option[A])(f: Option[A] => B): Option[B] =
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/std/stream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ trait StreamInstances {
def flatMap[A, B](fa: Stream[A])(f: A => Stream[B]): Stream[B] =
fa.flatMap(f)

override def map2[A, B, Z](fa: Stream[A], fb: Stream[B])(f: (A, B) => Z): Stream[Z] =
override def map2[A, B, Z](fa: Stream[A], fb: Stream[B])(f: (A, B) => Z)(implicit ev: Monoidal[Stream]): Stream[Z] =
fa.flatMap(a => fb.map(b => f(a, b)))

def coflatMap[A, B](fa: Stream[A])(f: Stream[A] => B): Stream[B] =
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/std/vector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ trait VectorInstances {
def flatMap[A, B](fa: Vector[A])(f: A => Vector[B]): Vector[B] =
fa.flatMap(f)

override def map2[A, B, Z](fa: Vector[A], fb: Vector[B])(f: (A, B) => Z): Vector[Z] =
override def map2[A, B, Z](fa: Vector[A], fb: Vector[B])(f: (A, B) => Z)(implicit ev: Monoidal[Vector]): Vector[Z] =
fa.flatMap(a => fb.map(b => f(a, b)))

def foldLeft[A, B](fa: Vector[A], b: B)(f: (B, A) => B): B =
Expand Down
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 @@ -3,6 +3,7 @@ package syntax

trait AllSyntax
extends ApplySyntax
with MonoidalSyntax
with BifunctorSyntax
with CoflatMapSyntax
with ComonadSyntax
Expand Down
22 changes: 4 additions & 18 deletions core/src/main/scala/cats/syntax/apply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,17 @@ package cats
package syntax

trait ApplySyntax1 {
implicit def applySyntaxU[FA](fa: FA)(implicit U: Unapply[Apply, FA]): ApplyOps[U.M, U.A] =
new ApplyOps[U.M, U.A] {
implicit def applySyntaxU[FA](fa: FA)(implicit U: Unapply[Apply, FA]): Apply.Ops[U.M, U.A] =
new Apply.Ops[U.M, U.A] {
val self = U.subst(fa)
val typeClassInstance = U.TC
}
}

trait ApplySyntax extends ApplySyntax1 {
implicit def applySyntax[F[_], A](fa: F[A])(implicit F: Apply[F]): ApplyOps[F, A] =
new ApplyOps[F,A] {
implicit def applySyntax[F[_], A](fa: F[A])(implicit F: Apply[F]): Apply.Ops[F, A] =
new Apply.Ops[F,A] {
val self = fa
val typeClassInstance = F
}
}

abstract class ApplyOps[F[_], A] extends Apply.Ops[F, A] {
def |@|[B](fb: F[B]): ApplyBuilder[F]#ApplyBuilder2[A, B] = new ApplyBuilder[F] |@| self |@| fb

/**
* combine both contexts but only return the right value
*/
def *>[B](fb: F[B]): F[B] = typeClassInstance.map2(self, fb)((a,b) => b)

/**
* combine both contexts but only return the left value
*/
def <*[B](fb: F[B]): F[A] = typeClassInstance.map2(self, fb)((a,b) => a)
}
28 changes: 28 additions & 0 deletions core/src/main/scala/cats/syntax/monoidal.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package cats
package syntax

trait MonoidalSyntax1 {
implicit def monoidalSyntaxU[FA](fa: FA)(implicit U: Unapply[Monoidal, FA]): MonoidalOps[U.M, U.A] =
new MonoidalOps[U.M, U.A] {
val self = U.subst(fa)
val typeClassInstance = U.TC
}
}

trait MonoidalSyntax extends MonoidalSyntax1 {
implicit def monoidalSyntax[F[_], A](fa: F[A])(implicit F: Monoidal[F]): MonoidalOps[F, A] =
new MonoidalOps[F, A] {
val self = fa
val typeClassInstance = F
}
}

abstract class MonoidalOps[F[_], A] extends Monoidal.Ops[F, A] {
def |@|[B](fb: F[B]): MonoidalBuilder[F]#MonoidalBuilder2[A, B] =
new MonoidalBuilder[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: 1 addition & 0 deletions core/src/main/scala/cats/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cats
package object syntax {
object all extends AllSyntax
object apply extends ApplySyntax
object monoidal extends MonoidalSyntax
object bifunctor extends BifunctorSyntax
object coflatMap extends CoflatMapSyntax
object comonad extends ComonadSyntax
Expand Down
16 changes: 16 additions & 0 deletions laws/src/main/scala/cats/laws/MonoidalLaws.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package cats.laws

import cats.Monoidal

trait MonoidalLaws[F[_]] {

implicit def F: Monoidal[F]

}

object MonoidalLaws {

def apply[F[_]](implicit ev: Monoidal[F]): MonoidalLaws[F] =
new MonoidalLaws[F] { def F: Monoidal[F] = ev }

}
Loading

0 comments on commit ff5b4c4

Please sign in to comment.