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 Split with Commutative Arrow. Continuing #1719 #1766

Merged
Merged
Show file tree
Hide file tree
Changes from 12 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
14 changes: 14 additions & 0 deletions core/src/main/scala/cats/CommutativeCoflatMap.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cats

import simulacrum.typeclass

/**
* Commutative CoflatMap.
*
* Further than a CoflatMap, which just allows composition of dependent effectful functions,
* in a Commutative CoflatMap those functions can be composed in any order, which guarantees
* that their effects do not interfere.
*
* Must obey the laws defined in cats.laws.CommutativeCoflatMapLaws.
*/
@typeclass trait CommutativeCoflatMap[F[_]] extends CoflatMap[F]
14 changes: 14 additions & 0 deletions core/src/main/scala/cats/CommutativeComonad.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cats

import simulacrum.typeclass

/**
* Commutative Comonad.
*
* Further than a Comonad, which just allows composition of dependent effectful functions,
* in a Commutative Comonad those functions can be composed in any order, which guarantees
* that their effects do not interfere.
*
* Must obey the laws defined in cats.laws.CommutativeComonadLaws.
*/
@typeclass trait CommutativeComonad[F[_]] extends Comonad[F] with CommutativeCoflatMap[F]
14 changes: 14 additions & 0 deletions core/src/main/scala/cats/CommutativeFlatMap.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cats

import simulacrum.typeclass

/**
* Commutative FlatMap.
*
* Further than a FlatMap, which just allows composition of dependent effectful functions,
* in a Commutative FlatMap those functions can be composed in any order, which guarantees
* that their effects do not interfere.
*
* Must obey the laws defined in cats.laws.CommutativeFlatMapLaws.
*/
@typeclass trait CommutativeFlatMap[F[_]] extends FlatMap[F]
14 changes: 14 additions & 0 deletions core/src/main/scala/cats/CommutativeMonad.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cats

import simulacrum.typeclass

/**
* Commutative Monad.
*
* Further than a Monad, which just allows composition of dependent effectful functions,
* in a Commutative Monad those functions can be composed in any order, which guarantees
* that their effects do not interfere.
*
* Must obey the laws defined in cats.laws.CommutativeMonadLaws.
*/
@typeclass trait CommutativeMonad[F[_]] extends Monad[F] with CommutativeFlatMap[F]
31 changes: 28 additions & 3 deletions core/src/main/scala/cats/arrow/Arrow.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@ import cats.functor.Strong

import simulacrum.typeclass

@typeclass trait Arrow[F[_, _]] extends Split[F] with Strong[F] with Category[F] { self =>
/**
* Must obey the laws defined in cats.laws.ArrowLaws.
*/
@typeclass trait Arrow[F[_, _]] extends Category[F] with Strong[F] { self =>
Copy link
Contributor

Choose a reason for hiding this comment

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

👍


/**
* Lift a function into the context of an Arrow
* Lift a function into the context of an Arrow.
*
* In the reference articles "Arrows are Promiscuous...", and in the corresponding Haskell
* library `Control.Arrow`, this function is called `arr`.
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

*/
def lift[A, B](f: A => B): F[A, B]

Expand All @@ -20,6 +26,25 @@ import simulacrum.typeclass
compose(swap, compose(first[A, B, C](fa), swap))
}

override def split[A, B, C, D](f: F[A, B], g: F[C, D]): F[(A, C), (B, D)] =
/**
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

* Create a new computation `F` that splits its input between `f` and `g`
* and combines the output of each.
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> import cats.arrow.Arrow
* scala> val toLong: Int => Long = _.toLong
* scala> val toDouble: Float => Double = _.toDouble
* scala> val f: ((Int, Float)) => (Long, Double) = Arrow[Function1].split(toLong, toDouble)
* scala> f((3, 4.0f))
* res0: (Long, Double) = (3,4.0)
* }}}
*
* Note that the arrow laws do not guarantee the non-interference between the _effects_ of
* `f` and `g` in the context of F. This means that `f *** g` may not be equivalent to `g *** f`.
*/
@simulacrum.op("***", alias = true)
def split[A, B, C, D](f: F[A, B], g: F[C, D]): F[(A, C), (B, D)] =
andThen(first(f), second(g))
}
13 changes: 13 additions & 0 deletions core/src/main/scala/cats/arrow/CommutativeArrow.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package cats
package arrow

import simulacrum.typeclass

/**
* In a Commutative Arrow F[_, _], the split operation (or `***`) is commutative,
* which means that there is non-interference between the effect of the paired arrows.
*
* Must obey the laws in CommutativeArrowLaws
*/
@typeclass trait CommutativeArrow[F[_, _]] extends Arrow[F]

24 changes: 0 additions & 24 deletions core/src/main/scala/cats/arrow/Split.scala

This file was deleted.

78 changes: 46 additions & 32 deletions core/src/main/scala/cats/data/Cokleisli.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package cats
package data

import cats.arrow.{Arrow, Category, Compose, Split}
import cats.arrow.{Arrow, Category, CommutativeArrow, Compose}
import cats.functor.{Contravariant, Profunctor}
import cats.{CoflatMap, Comonad, Functor, Monad}
import cats.{CoflatMap, Comonad, CommutativeComonad, Functor, Monad}
import scala.annotation.tailrec

/**
Expand Down Expand Up @@ -45,37 +45,27 @@ object Cokleisli extends CokleisliInstances {
}

private[data] sealed abstract class CokleisliInstances extends CokleisliInstances0 {
implicit def catsDataArrowForCokleisli[F[_]](implicit ev: Comonad[F]): Arrow[Cokleisli[F, ?, ?]] =
new CokleisliArrow[F] { def F: Comonad[F] = ev }

implicit def catsDataMonadForCokleisli[F[_], A]: Monad[Cokleisli[F, A, ?]] = new Monad[Cokleisli[F, A, ?]] {
def pure[B](x: B): Cokleisli[F, A, B] =
Cokleisli.pure(x)
implicit def catsDataCommutativeArrowForCokleisli[F[_]](implicit ev: CommutativeComonad[F]): CommutativeArrow[Cokleisli[F, ?, ?]] =
new CokleisliCommutativeArrow[F] { def F: CommutativeComonad[F] = ev }

def flatMap[B, C](fa: Cokleisli[F, A, B])(f: B => Cokleisli[F, A, C]): Cokleisli[F, A, C] =
fa.flatMap(f)
implicit val catsDataCommutativeArrowForCokleisliId: CommutativeArrow[Cokleisli[Id, ?, ?]] =
catsDataCommutativeArrowForCokleisli[Id]

override def map[B, C](fa: Cokleisli[F, A, B])(f: B => C): Cokleisli[F, A, C] =
fa.map(f)

def tailRecM[B, C](b: B)(fn: B => Cokleisli[F, A, Either[B, C]]): Cokleisli[F, A, C] =
Cokleisli({ (fa: F[A]) =>
@tailrec
def loop(c: Cokleisli[F, A, Either[B, C]]): C = c.run(fa) match {
case Right(c) => c
case Left(bb) => loop(fn(bb))
}
loop(fn(b))
})
}
implicit def catsDataMonadForCokleisli[F[_], A]: Monad[Cokleisli[F, A, ?]] =
new CokleisliMonad[F, A]

implicit def catsDataMonoidKForCokleisli[F[_]](implicit ev: Comonad[F]): MonoidK[λ[α => Cokleisli[F, α, α]]] =
Category[Cokleisli[F, ?, ?]].algebraK
}

private[data] sealed abstract class CokleisliInstances0 {
implicit def catsDataSplitForCokleisli[F[_]](implicit ev: CoflatMap[F]): Split[Cokleisli[F, ?, ?]] =
new CokleisliSplit[F] { def F: CoflatMap[F] = ev }
private[data] sealed abstract class CokleisliInstances0 extends CokleisliInstances1 {
implicit def catsDataArrowForCokleisli[F[_]](implicit ev: Comonad[F]): Arrow[Cokleisli[F, ?, ?]] =
new CokleisliArrow[F] { def F: Comonad[F] = ev }
}

private[data] sealed abstract class CokleisliInstances1 {
implicit def catsDataComposeForCokleisli[F[_]](implicit ev: CoflatMap[F]): Compose[Cokleisli[F, ?, ?]] =
new CokleisliCompose[F] { def F: CoflatMap[F] = ev }

implicit def catsDataProfunctorForCokleisli[F[_]](implicit ev: Functor[F]): Profunctor[Cokleisli[F, ?, ?]] =
new CokleisliProfunctor[F] { def F: Functor[F] = ev }
Expand All @@ -89,7 +79,34 @@ private[data] sealed abstract class CokleisliInstances0 {
}
}

private trait CokleisliArrow[F[_]] extends Arrow[Cokleisli[F, ?, ?]] with CokleisliSplit[F] with CokleisliProfunctor[F] {
private[data] trait CokleisliCommutativeArrow[F[_]] extends CommutativeArrow[Cokleisli[F, ?, ?]] with CokleisliArrow[F] {
implicit def F: CommutativeComonad[F]
}

private[data] class CokleisliMonad[F[_], A] extends Monad[Cokleisli[F, A, ?]] {

def pure[B](x: B): Cokleisli[F, A, B] =
Cokleisli.pure(x)

def flatMap[B, C](fa: Cokleisli[F, A, B])(f: B => Cokleisli[F, A, C]): Cokleisli[F, A, C] =
fa.flatMap(f)

override def map[B, C](fa: Cokleisli[F, A, B])(f: B => C): Cokleisli[F, A, C] =
fa.map(f)

def tailRecM[B, C](b: B)(fn: B => Cokleisli[F, A, Either[B, C]]): Cokleisli[F, A, C] =
Cokleisli({ (fa: F[A]) =>
@tailrec
def loop(c: Cokleisli[F, A, Either[B, C]]): C = c.run(fa) match {
case Right(c) => c
case Left(bb) => loop(fn(bb))
}
loop(fn(b))
})

}

private trait CokleisliArrow[F[_]] extends Arrow[Cokleisli[F, ?, ?]] with CokleisliCompose[F] with CokleisliProfunctor[F] {
implicit def F: Comonad[F]

def lift[A, B](f: A => B): Cokleisli[F, A, B] =
Expand All @@ -108,17 +125,14 @@ private trait CokleisliArrow[F[_]] extends Arrow[Cokleisli[F, ?, ?]] with Coklei
super[CokleisliProfunctor].dimap(fab)(f)(g)

override def split[A, B, C, D](f: Cokleisli[F, A, B], g: Cokleisli[F, C, D]): Cokleisli[F, (A, C), (B, D)] =
super[CokleisliSplit].split(f, g)
Cokleisli(fac => f.run(F.map(fac)(_._1)) -> g.run(F.map(fac)(_._2)))
}

private trait CokleisliSplit[F[_]] extends Split[Cokleisli[F, ?, ?]] {
private trait CokleisliCompose[F[_]] extends Compose[Cokleisli[F, ?, ?]] {
implicit def F: CoflatMap[F]

def compose[A, B, C](f: Cokleisli[F, B, C], g: Cokleisli[F, A, B]): Cokleisli[F, A, C] =
f.compose(g)

def split[A, B, C, D](f: Cokleisli[F, A, B], g: Cokleisli[F, C, D]): Cokleisli[F, (A, C), (B, D)] =
Cokleisli(fac => f.run(F.map(fac)(_._1)) -> g.run(F.map(fac)(_._2)))
}

private trait CokleisliProfunctor[F[_]] extends Profunctor[Cokleisli[F, ?, ?]] {
Expand Down
45 changes: 27 additions & 18 deletions core/src/main/scala/cats/data/Kleisli.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats
package data

import cats.arrow.{Arrow, Category, Choice, Compose, Split, FunctionK}
import cats.arrow.{Arrow, Category, Choice, CommutativeArrow, Compose, FunctionK}
import cats.functor.{Contravariant, Strong}

/**
Expand Down Expand Up @@ -81,7 +81,13 @@ private[data] sealed trait KleisliFunctions {
}

private[data] sealed abstract class KleisliInstances extends KleisliInstances0 {
implicit def catsDataCommutativeMonadForKleisli[F[_], A, B](implicit F0: CommutativeMonad[F]): CommutativeMonad[Kleisli[F, A, ?]] =
new KleisliMonad[F, A] with CommutativeMonad[Kleisli[F, A, ?]] {
implicit def F: Monad[F] = F0
}
}

private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 {
implicit def catsDataMonoidForKleisli[F[_], A, B](implicit FB0: Monoid[F[B]]): Monoid[Kleisli[F, A, B]] =
new KleisliMonoid[F, A, B] { def FB: Monoid[F[B]] = FB0 }

Expand All @@ -91,11 +97,11 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 {
implicit val catsDataMonoidKForKleisliId: MonoidK[λ[α => Kleisli[Id, α, α]]] =
catsDataMonoidKForKleisli[Id]

implicit def catsDataArrowForKleisli[F[_]](implicit M: Monad[F]): Arrow[Kleisli[F, ?, ?]] =
new KleisliArrow[F] { def F: Monad[F] = M }
implicit def catsDataCommutativeArrowForKleisli[F[_]](implicit M: CommutativeMonad[F]): CommutativeArrow[Kleisli[F, ?, ?]] =
new KleisliCommutativeArrow[F] {def F: CommutativeMonad[F] = M }

implicit val catsDataArrowForKleisliId: Arrow[Kleisli[Id, ?, ?]] =
catsDataArrowForKleisli[Id]
implicit val catsDataCommutativeArrowForKleisliId: CommutativeArrow[Kleisli[Id, ?, ?]] =
catsDataCommutativeArrowForKleisli[Id]

implicit def catsDataMonadReaderForKleisliId[A]: MonadReader[Kleisli[Id, A, ?], A] =
Copy link
Contributor

@edmundnoble edmundnoble Jul 14, 2017

Choose a reason for hiding this comment

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

Kleisli[F, A, B] is seemingly a commutative monad now too, given F[_]: CommutativeMonad. ReaderT has no added effects, so I think this has to be the case.

catsDataMonadReaderForKleisli[Id, A]
Expand All @@ -115,25 +121,28 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 {
new KleisliApplicativeError[F, A, E] { def F: ApplicativeError[F, E] = AE }
}

private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 {
private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 {
implicit def catsDataArrowForKleisli[F[_]](implicit M: Monad[F]): Arrow[Kleisli[F, ?, ?]] =
new KleisliArrow[F] { def F: Monad[F] = M }

implicit def catsDataMonadErrorForKleisli[F[_], A, E](implicit ME: MonadError[F, E]): MonadError[Kleisli[F, A, ?], E] =
new KleisliMonadError[F, A, E] { def F: MonadError[F, E] = ME }
}

private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 {
private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3 {
implicit def catsDataMonadReaderForKleisli[F[_], A](implicit M: Monad[F]): MonadReader[Kleisli[F, A, ?], A] =
new KleisliMonadReader[F, A] { def F: Monad[F] = M }
}

private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3 {
private[data] sealed abstract class KleisliInstances3 extends KleisliInstances4 {
implicit def catsDataChoiceForKleisli[F[_]](implicit M: Monad[F]): Choice[Kleisli[F, ?, ?]] =
new KleisliChoice[F] { def F: Monad[F] = M }

implicit val catsDataChoiceForKleisliId: Choice[Kleisli[Id, ?, ?]] =
catsDataChoiceForKleisli[Id]

implicit def catsDataSplitForKleisli[F[_]](implicit FM: FlatMap[F]): Split[Kleisli[F, ?, ?]] =
new KleisliSplit[F] { def F: FlatMap[F] = FM }
implicit def catsDataComposeForKleisli[F[_]](implicit FM: FlatMap[F]): Compose[Kleisli[F, ?, ?]] =
new KleisliCompose[F] { def F: FlatMap[F] = FM }

implicit def catsDataStrongForKleisli[F[_]](implicit F0: Functor[F]): Strong[Kleisli[F, ?, ?]] =
new KleisliStrong[F] { def F: Functor[F] = F0 }
Expand All @@ -148,30 +157,30 @@ private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3
Compose[Kleisli[F, ?, ?]].algebraK
}

private[data] sealed abstract class KleisliInstances3 extends KleisliInstances4 {
private[data] sealed abstract class KleisliInstances4 extends KleisliInstances5 {
implicit def catsDataApplicativeForKleisli[F[_], A](implicit A: Applicative[F]): Applicative[Kleisli[F, A, ?]] =
new KleisliApplicative[F, A] { def F: Applicative[F] = A }
}

private[data] sealed abstract class KleisliInstances4 extends KleisliInstances5 {
private[data] sealed abstract class KleisliInstances5 extends KleisliInstances6 {
implicit def catsDataApplyForKleisli[F[_], A](implicit A: Apply[F]): Apply[Kleisli[F, A, ?]] =
new KleisliApply[F, A] { def F: Apply[F] = A }
}

private[data] sealed abstract class KleisliInstances5 {
private[data] sealed abstract class KleisliInstances6 {
implicit def catsDataFunctorForKleisli[F[_], A](implicit F0: Functor[F]): Functor[Kleisli[F, A, ?]] =
new KleisliFunctor[F, A] { def F: Functor[F] = F0 }
}

private trait KleisliArrow[F[_]] extends Arrow[Kleisli[F, ?, ?]] with KleisliSplit[F] with KleisliStrong[F] with KleisliCategory[F] {
private trait KleisliCommutativeArrow[F[_]] extends CommutativeArrow[Kleisli[F, ?, ?]] with KleisliArrow[F] {
implicit def F: CommutativeMonad[F]
}

private trait KleisliArrow[F[_]] extends Arrow[Kleisli[F, ?, ?]] with KleisliCategory[F] with KleisliStrong[F] {
implicit def F: Monad[F]

def lift[A, B](f: A => B): Kleisli[F, A, B] =
Kleisli(a => F.pure(f(a)))
}

private trait KleisliSplit[F[_]] extends Split[Kleisli[F, ?, ?]] with KleisliCompose[F] {
implicit def F: FlatMap[F]

override def split[A, B, C, D](f: Kleisli[F, A, B], g: Kleisli[F, C, D]): Kleisli[F, (A, C), (B, D)] =
Kleisli{ case (a, c) => F.flatMap(f.run(a))(b => F.map(g.run(c))(d => (b, d))) }
Expand Down
Loading