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

[Do not merge] Separate Bitraverse hierarchy default implementations #1194

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 19 additions & 12 deletions core/src/main/scala/cats/Bifoldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,39 @@ package cats
/**
* A type class abstracting over types that give rise to two independent [[cats.Foldable]]s.
*/
trait Bifoldable[F[_, _]] extends Any with Serializable { self =>
trait Bifoldable[F[_, _]] extends Any with Serializable {
/** Collapse the structure with a left-associative function */
def bifoldLeft[A, B, C](fab: F[A, B], c: C)(f: (C, A) => C, g: (C, B) => C): C

/** Collapse the structure with a right-associative function */
def bifoldRight[A, B, C](fab: F[A, B], c: Eval[C])(f: (A, Eval[C]) => Eval[C], g: (B, Eval[C]) => Eval[C]): Eval[C]

/** Collapse the structure by mapping each element to an element of a type that has a [[cats.Monoid]] */
def bifoldMap[A, B, C](fab: F[A, B])(f: A => C, g: B => C)(implicit C: Monoid[C]): C =
bifoldLeft(fab, C.empty)(
(c: C, a: A) => C.combine(c, f(a)),
(c: C, b: B) => C.combine(c, g(b))
)
def bifoldMap[A, B, C](fab: F[A, B])(f: A => C, g: B => C)(implicit C: Monoid[C]): C

def compose[G[_, _]](implicit ev: Bifoldable[G]): Bifoldable[λ[(α, β) => F[G[α, β], G[α, β]]]] =
new ComposedBifoldable[F, G] {
val F = self
val G = ev
}
def compose[G[_, _]](implicit ev: Bifoldable[G]): Bifoldable[λ[(α, β) => F[G[α, β], G[α, β]]]]
}

object Bifoldable {
def apply[F[_, _]](implicit F: Bifoldable[F]): Bifoldable[F] = F

trait Default[F[_, _]] extends Bifoldable[F] { self =>

def bifoldMap[A, B, C](fab: F[A, B])(f: A => C, g: B => C)(implicit C: Monoid[C]): C =
bifoldLeft(fab, C.empty)(
(c: C, a: A) => C.combine(c, f(a)),
(c: C, b: B) => C.combine(c, g(b))
)

def compose[G[_, _]](implicit ev: Bifoldable[G]): Bifoldable[λ[(α, β) => F[G[α, β], G[α, β]]]] =
new ComposedBifoldable[F, G] {
val F = self
val G = ev
}
}
}

private[cats] trait ComposedBifoldable[F[_, _], G[_, _]] extends Bifoldable[λ[(α, β) => F[G[α, β], G[α, β]]]] {
private[cats] trait ComposedBifoldable[F[_, _], G[_, _]] extends Bifoldable.Default[λ[(α, β) => F[G[α, β], G[α, β]]]] {
implicit def F: Bifoldable[F]
implicit def G: Bifoldable[G]

Expand Down
57 changes: 44 additions & 13 deletions core/src/main/scala/cats/Bitraverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,62 @@ import cats.functor.{Bifunctor, ComposedBifunctor}
/**
* A type class abstracting over types that give rise to two independent [[cats.Traverse]]s.
*/
trait Bitraverse[F[_, _]] extends Bifoldable[F] with Bifunctor[F] { self =>
trait Bitraverse[F[_, _]] extends Bifoldable[F] with Bifunctor[F] {
/** Traverse each side of the structure with the given functions */
def bitraverse[G[_]: Applicative, A, B, C, D](fab: F[A, B])(f: A => G[C], g: B => G[D]): G[F[C, D]]

/** Sequence each side of the structure with the given functions */
def bisequence[G[_]: Applicative, A, B](fab: F[G[A], G[B]]): G[F[A, B]] =
bitraverse(fab)(identity, identity)
/**
* Sequence each side of the structure with the given functions.
*
* Example:
* {{{
* scala> import cats.data.Xor
* scala> import cats.implicits._
*
* scala> val rightSome: Option[String] Xor Option[Int] = Xor.right(Some(3))
* scala> rightSome.bisequence
* res0: Option[String Xor Int] = Some(Right(3))
*
* scala> val rightNone: Option[String] Xor Option[Int] = Xor.right(None)
* scala> rightNone.bisequence
* res0: Option[String Xor Int] = None
*
* scala> val leftSome: Option[String] Xor Option[Int] = Xor.left(Some("foo"))
* scala> leftSome.bisequence
* res0: Option[String Xor Int] = Some(Left(foo))
*
* scala> val leftNone: Option[String] Xor Option[Int] = Xor.left(None)
* scala> leftNone.bisequence
* res0: Option[String Xor Int] = None
* }}}
*/
def bisequence[G[_]: Applicative, A, B](fab: F[G[A], G[B]]): G[F[A, B]]

/** If F and G are both [[cats.Bitraverse]] then so is their composition F[G[_, _], G[_, _]] */
def compose[G[_, _]](implicit ev: Bitraverse[G]): Bitraverse[λ[(α, β) => F[G[α, β], G[α, β]]]] =
new ComposedBitraverse[F, G] {
val F = self
val G = ev
}

override def bimap[A, B, C, D](fab: F[A, B])(f: A => C, g: B => D): F[C, D] =
bitraverse[Id, A, B, C, D](fab)(f, g)
def compose[G[_, _]](implicit ev: Bitraverse[G]): Bitraverse[λ[(α, β) => F[G[α, β], G[α, β]]]]
}

object Bitraverse {
def apply[F[_, _]](implicit F: Bitraverse[F]): Bitraverse[F] = F

trait Default[F[_, _]] extends Bitraverse[F] with Bifoldable.Default[F] with Bifunctor.Default[F] { self =>

def bisequence[G[_]: Applicative, A, B](fab: F[G[A], G[B]]): G[F[A, B]] =
bitraverse(fab)(identity, identity)

def compose[G[_, _]](implicit ev: Bitraverse[G]): Bitraverse[λ[(α, β) => F[G[α, β], G[α, β]]]] =
new ComposedBitraverse[F, G] {
val F = self
val G = ev
}

override def bimap[A, B, C, D](fab: F[A, B])(f: A => C, g: B => D): F[C, D] =
bitraverse[Id, A, B, C, D](fab)(f, g)
}
}

private[cats] trait ComposedBitraverse[F[_, _], G[_, _]]
extends Bitraverse[λ[(α, β) => F[G[α, β], G[α, β]]]]
extends Bitraverse.Default[λ[(α, β) => F[G[α, β], G[α, β]]]]
with ComposedBifoldable[F, G]
with ComposedBifunctor[F, G] {
def F: Bitraverse[F]
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/data/Const.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private[data] sealed abstract class ConstInstances extends ConstInstances0 {
}

implicit val catsDataBifoldableForConst: Bifoldable[Const] =
new Bifoldable[Const] {
new Bifoldable.Default[Const] {
def bifoldLeft[A, B, C](fab: Const[A, B], c: C)(f: (C, A) => C, g: (C, B) => C): C =
f(c, fab.getConst)

Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/data/Ior.scala
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ private[data] sealed abstract class IorInstances extends IorInstances0 {
}

implicit def catsDataBifunctorForIor: Bifunctor[Ior] =
new Bifunctor[Ior] {
new Bifunctor.Default[Ior] {
override def bimap[A, B, C, D](fab: A Ior B)(f: A => C, g: B => D): C Ior D = fab.bimap(f, g)
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/data/Validated.scala
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ private[data] sealed abstract class ValidatedInstances extends ValidatedInstance
}

implicit val catsDataBitraverseForValidated: Bitraverse[Validated] =
new Bitraverse[Validated] {
new Bitraverse.Default[Validated] {
def bitraverse[G[_], A, B, C, D](fab: Validated[A, B])(f: A => G[C], g: B => G[D])(implicit G: Applicative[G]): G[Validated[C, D]] =
fab match {
case Invalid(a) => G.map(f(a))(Validated.invalid)
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/data/WriterT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private[data] sealed abstract class WriterTInstances extends WriterTInstances0 {
catsDataEqForWriterT[Id, L, V]

implicit def catsDataBifunctorForWriterT[F[_]:Functor]: Bifunctor[WriterT[F, ?, ?]] =
new Bifunctor[WriterT[F, ?, ?]] {
new Bifunctor.Default[WriterT[F, ?, ?]] {
def bimap[A, B, C, D](fab: WriterT[F, A, B])(f: A => C, g: B => D): WriterT[F, C, D] =
fab.bimap(f, g)
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/data/Xor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ private[data] sealed abstract class XorInstances extends XorInstances1 {
}

implicit val catsDataBitraverseForXor: Bitraverse[Xor] =
new Bitraverse[Xor] {
new Bitraverse.Default[Xor] {
def bitraverse[G[_], A, B, C, D](fab: Xor[A, B])(f: A => G[C], g: B => G[D])(implicit G: Applicative[G]): G[Xor[C, D]] =
fab match {
case Xor.Left(a) => G.map(f(a))(Xor.left)
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/scala/cats/data/XorT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ private[data] abstract class XorTInstances extends XorTInstances1 {
functor.Contravariant[Show].contramap(sh)(_.value)

implicit def catsDataBifunctorForXorT[F[_]](implicit F: Functor[F]): Bifunctor[XorT[F, ?, ?]] =
new Bifunctor[XorT[F, ?, ?]] {
new Bifunctor.Default[XorT[F, ?, ?]] {
override def bimap[A, B, C, D](fab: XorT[F, A, B])(f: A => C, g: B => D): XorT[F, C, D] = fab.bimap(f, g)
}

Expand Down Expand Up @@ -401,7 +401,7 @@ private[data] sealed trait XorTTraverse[F[_], L] extends Traverse[XorT[F, L, ?]]
fa traverse f
}

private[data] sealed trait XorTBifoldable[F[_]] extends Bifoldable[XorT[F, ?, ?]] {
private[data] sealed trait XorTBifoldable[F[_]] extends Bifoldable.Default[XorT[F, ?, ?]] {
implicit def F0: Foldable[F]

def bifoldLeft[A, B, C](fab: XorT[F, A, B], c: C)(f: (C, A) => C, g: (C, B) => C): C =
Expand All @@ -411,7 +411,7 @@ private[data] sealed trait XorTBifoldable[F[_]] extends Bifoldable[XorT[F, ?, ?]
F0.foldRight(fab.value, c)( (axb, acc) => Bifoldable[Xor].bifoldRight(axb, acc)(f, g))
}

private[data] sealed trait XorTBitraverse[F[_]] extends Bitraverse[XorT[F, ?, ?]] with XorTBifoldable[F] {
private[data] sealed trait XorTBitraverse[F[_]] extends Bitraverse.Default[XorT[F, ?, ?]] with XorTBifoldable[F] {
override implicit def F0: Traverse[F]

override def bitraverse[G[_], A, B, C, D](fab: XorT[F, A, B])(f: A => G[C], g: B => G[D])(implicit G: Applicative[G]): G[XorT[F, C, D]] =
Expand Down
37 changes: 27 additions & 10 deletions core/src/main/scala/cats/functor/Bifunctor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,56 @@ package functor
* A type class of types which give rise to two independent, covariant
* functors.
*/
trait Bifunctor[F[_, _]] extends Any with Serializable { self =>
trait Bifunctor[F[_, _]] extends Any with Serializable {

/**
* The quintessential method of the Bifunctor trait, it applies a
* function to each "side" of the bifunctor.
*
* Example:
* {{{
* scala> import cats.implicits._
*
* scala> val x: (List[String], Int) = (List("foo", "bar"), 3)
* scala> x.bimap(_.headOption, _.toLong + 1)
* res0: (Option[String], Long) = (Some(foo),4)
* }}}
*/
def bimap[A, B, C, D](fab: F[A, B])(f: A => C, g: B => D): F[C, D]

// derived methods
/**
* apply a function to the "left" functor
*/
def leftMap[A, B, C](fab: F[A, B])(f: A => C): F[C, B] = bimap(fab)(f, identity)
def leftMap[A, B, C](fab: F[A, B])(f: A => C): F[C, B]

/**
* apply a function ro the "right" functor
*/
def rightMap[A, B, C](fab: F[A, B])(f: B => C): F[A, C] = bimap(fab)(identity, f)
def rightMap[A, B, C](fab: F[A, B])(f: B => C): F[A, C]

/** The composition of two Bifunctors is itself a Bifunctor */
def compose[G[_, _]](implicit G0: Bifunctor[G]): Bifunctor[λ[(α, β) => F[G[α, β], G[α, β]]]] =
new ComposedBifunctor[F, G] {
val F = self
val G = G0
}
def compose[G[_, _]](implicit G0: Bifunctor[G]): Bifunctor[λ[(α, β) => F[G[α, β], G[α, β]]]]
}

object Bifunctor {
def apply[F[_, _]](implicit ev: Bifunctor[F]): Bifunctor[F] = ev

trait Default[F[_, _]] extends Bifunctor[F] { self =>

def leftMap[A, B, C](fab: F[A, B])(f: A => C): F[C, B] = bimap(fab)(f, identity)

def rightMap[A, B, C](fab: F[A, B])(f: B => C): F[A, C] = bimap(fab)(identity, f)

def compose[G[_, _]](implicit G0: Bifunctor[G]): Bifunctor[λ[(α, β) => F[G[α, β], G[α, β]]]] =
new ComposedBifunctor[F, G] {
val F = self
val G = G0
}
}
}

private[cats] trait ComposedBifunctor[F[_, _], G[_, _]]
extends Bifunctor[λ[(A, B) => F[G[A, B], G[A, B]]]] {
extends Bifunctor.Default[λ[(A, B) => F[G[A, B], G[A, B]]]] {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems I missed this in #1079, maybe you can change the type projection to λ[(α, β) => F[G[α, β], G[α, β]]] in this PR ?

def F: Bifunctor[F]
def G: Bifunctor[G]

Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/instances/either.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import cats.data.Xor

trait EitherInstances extends EitherInstances1 {
implicit val catsStdBitraverseForEither: Bitraverse[Either] =
new Bitraverse[Either] {
new Bitraverse.Default[Either] {
def bitraverse[G[_], A, B, C, D](fab: Either[A, B])(f: A => G[C], g: B => G[D])(implicit G: Applicative[G]): G[Either[C, D]] =
fab match {
case Left(a) => G.map(f(a))(Left(_))
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/instances/tuple.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ trait TupleInstances extends Tuple2Instances with cats.kernel.instances.TupleIns

sealed trait Tuple2Instances {
implicit val catsStdBitraverseForTuple2: Bitraverse[Tuple2] =
new Bitraverse[Tuple2] {
new Bitraverse.Default[Tuple2] {
def bitraverse[G[_]: Applicative, A, B, C, D](fab: (A, B))(f: A => G[C], g: B => G[D]): G[(C, D)] =
Applicative[G].tuple2(f(fab._1), g(fab._2))

Expand Down