From 02bfbacbe30f52b26487480bf207d808e96eaee8 Mon Sep 17 00:00:00 2001 From: Owen Parry Date: Tue, 16 Jun 2015 22:04:04 -0700 Subject: [PATCH 1/5] make map abstract in applicative/monad/traverse --- core/src/main/scala/cats/Applicative.scala | 2 -- core/src/main/scala/cats/Monad.scala | 5 +---- core/src/main/scala/cats/Traverse.scala | 3 --- core/src/main/scala/cats/data/Const.scala | 4 ++++ core/src/main/scala/cats/data/Ior.scala | 1 + core/src/main/scala/cats/data/Kleisli.scala | 6 ++++++ free/src/main/scala/cats/free/FreeApplicative.scala | 1 + laws/src/main/scala/cats/laws/TraverseLaws.scala | 6 ++++++ std/src/main/scala/cats/std/function.scala | 3 +++ std/src/main/scala/cats/std/option.scala | 2 +- tests/src/test/scala/cats/tests/RegressionTests.scala | 1 + 11 files changed, 24 insertions(+), 10 deletions(-) diff --git a/core/src/main/scala/cats/Applicative.scala b/core/src/main/scala/cats/Applicative.scala index 41dd1a6983..39c20572d1 100644 --- a/core/src/main/scala/cats/Applicative.scala +++ b/core/src/main/scala/cats/Applicative.scala @@ -20,8 +20,6 @@ import simulacrum._ */ def pure[A](x: A): F[A] - override def map[A, B](fa: F[A])(f: A => B): F[B] = ap(fa)(pure(f)) - /** * Two sequentially dependent Applicatives can be composed. * diff --git a/core/src/main/scala/cats/Monad.scala b/core/src/main/scala/cats/Monad.scala index a5a014fe67..dbcceae804 100644 --- a/core/src/main/scala/cats/Monad.scala +++ b/core/src/main/scala/cats/Monad.scala @@ -11,7 +11,4 @@ import simulacrum._ * * Must obey the laws defined in cats.laws.MonadLaws. */ -@typeclass trait Monad[F[_]] extends FlatMap[F] with Applicative[F] { - override def map[A, B](fa: F[A])(f: A => B): F[B] = - flatMap(fa)(a => pure(f(a))) -} +@typeclass trait Monad[F[_]] extends FlatMap[F] with Applicative[F] diff --git a/core/src/main/scala/cats/Traverse.scala b/core/src/main/scala/cats/Traverse.scala index 94c993584c..4a48c870e7 100644 --- a/core/src/main/scala/cats/Traverse.scala +++ b/core/src/main/scala/cats/Traverse.scala @@ -42,9 +42,6 @@ def traverseU[A, GB](fa: F[A])(f: A => GB)(implicit U: Unapply[Applicative, GB]) def sequenceU[GA](fga: F[GA])(implicit U: Unapply[Applicative,GA]): U.M[F[U.A]] = traverse(fga)(U.subst)(U.TC) - override def map[A, B](fa: F[A])(f: A => B): F[B] = - traverse[Id, A, B](fa)(f) - def traversal[G[_]: Applicative]: Traversal[G] = new Traversal[G] diff --git a/core/src/main/scala/cats/data/Const.scala b/core/src/main/scala/cats/data/Const.scala index 2457a8990d..ad51959815 100644 --- a/core/src/main/scala/cats/data/Const.scala +++ b/core/src/main/scala/cats/data/Const.scala @@ -50,6 +50,8 @@ sealed abstract class ConstInstances extends ConstInstances0 { def traverse[G[_]: Applicative, A, B](fa: Const[C, A])(f: A => G[B]): G[Const[C, B]] = fa.traverse(f) + def map[A, B](fa: Const[C, A])(f: A => B): Const[C, B] = fa.retag[B] + def foldLeft[A, B](fa: Const[C, A], b: B)(f: (B, A) => B): B = b override def foldRight[A, B](fa: Const[C, A], b: Lazy[B])(f: A => Fold[B]): Lazy[B] = b @@ -76,6 +78,8 @@ sealed abstract class ConstInstances0 extends ConstInstances1 { def pure[A](x: A): Const[C, A] = Const.empty + def map[A, B](fa: Const[C, A])(f: A => B): Const[C, B] = fa.retag[B] + def ap[A, B](fa: Const[C, A])(f: Const[C, A => B]): Const[C, B] = fa.retag[B] combine f.retag[B] } diff --git a/core/src/main/scala/cats/data/Ior.scala b/core/src/main/scala/cats/data/Ior.scala index 7e67bd396c..933d34ea46 100644 --- a/core/src/main/scala/cats/data/Ior.scala +++ b/core/src/main/scala/cats/data/Ior.scala @@ -137,6 +137,7 @@ sealed abstract class IorInstances extends IorInstances0 { implicit def iorMonad[A: Semigroup]: Monad[A Ior ?] = new Monad[A Ior ?] { def pure[B](b: B): A Ior B = Ior.right(b) + def map[B, C](fa: A Ior B)(f: B => C): A Ior C = fa.map(f) def flatMap[B, C](fa: A Ior B)(f: B => A Ior C): A Ior C = fa.flatMap(f) } } diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index 17318af8dc..6e63d4df34 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -83,6 +83,9 @@ sealed abstract class KleisliInstances extends KleisliInstances0 { def pure[B](x: B): Kleisli[F, A, B] = Kleisli.pure[F, A, B](x) + def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = + fa.map(f) + def flatMap[B, C](fa: Kleisli[F, A, B])(f: B => Kleisli[F, A, C]): Kleisli[F, A, C] = fa.flatMap(f) } @@ -109,6 +112,9 @@ sealed abstract class KleisliInstances1 extends KleisliInstances2 { def pure[B](x: B): Kleisli[F, A, B] = Kleisli.pure[F, A, B](x) + override def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = + fa.map(f) + def ap[B, C](fa: Kleisli[F, A, B])(f: Kleisli[F, A, B => C]): Kleisli[F, A, C] = fa(f) } diff --git a/free/src/main/scala/cats/free/FreeApplicative.scala b/free/src/main/scala/cats/free/FreeApplicative.scala index 4c595bb74d..64456b4b83 100644 --- a/free/src/main/scala/cats/free/FreeApplicative.scala +++ b/free/src/main/scala/cats/free/FreeApplicative.scala @@ -65,6 +65,7 @@ object FreeApplicative { implicit final def freeApplicative[S[_]]: Applicative[FA[S, ?]] = { new Applicative[FA[S, ?]] { def ap[A, B](fa: FA[S, A])(f: FA[S, A => B]): FA[S, B] = fa.ap(f) + override def map[A, B](fa: FA[S, A])(f: A => B): FA[S, B] = fa.map(f) def pure[A](a: A): FA[S, A] = Pure(a) } } diff --git a/laws/src/main/scala/cats/laws/TraverseLaws.scala b/laws/src/main/scala/cats/laws/TraverseLaws.scala index 8ed5a76f6f..891e2e5df8 100644 --- a/laws/src/main/scala/cats/laws/TraverseLaws.scala +++ b/laws/src/main/scala/cats/laws/TraverseLaws.scala @@ -39,6 +39,12 @@ trait TraverseLaws[F[_]] extends FunctorLaws[F] with FoldableLaws[F] { type MN[Z] = (M[Z], N[Z]) implicit val MN = new Applicative[MN] { override def pure[X](x: X): MN[X] = (M.pure(x), N.pure(x)) + + override def map[X, Y](fa: MN[X])(f: X => Y): MN[Y] = { + val (fam, fan) = fa + (M.map(fam)(f), N.map(fan)(f)) + } + override def ap[X, Y](fa: MN[X])(f: MN[X => Y]): MN[Y] = { val (fam, fan) = fa val (fm, fn) = f diff --git a/std/src/main/scala/cats/std/function.scala b/std/src/main/scala/cats/std/function.scala index ef504102c6..d77636e17c 100644 --- a/std/src/main/scala/cats/std/function.scala +++ b/std/src/main/scala/cats/std/function.scala @@ -15,6 +15,9 @@ trait Function0Instances { def pure[A](x: A): () => A = () => x + override def map[A, B](fa: () => A)(f: (A) => B): () => B = + () => f(fa()) + def flatMap[A, B](fa: () => A)(f: A => () => B): () => B = () => f(fa())() } diff --git a/std/src/main/scala/cats/std/option.scala b/std/src/main/scala/cats/std/option.scala index e4903adc35..bf811ae2a2 100644 --- a/std/src/main/scala/cats/std/option.scala +++ b/std/src/main/scala/cats/std/option.scala @@ -13,7 +13,7 @@ trait OptionInstances { def pure[A](x: A): Option[A] = Some(x) - override def map[A, B](fa: Option[A])(f: A => B): Option[B] = + def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa.map(f) def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] = diff --git a/tests/src/test/scala/cats/tests/RegressionTests.scala b/tests/src/test/scala/cats/tests/RegressionTests.scala index c9fa54fece..ea9d8b90dc 100644 --- a/tests/src/test/scala/cats/tests/RegressionTests.scala +++ b/tests/src/test/scala/cats/tests/RegressionTests.scala @@ -17,6 +17,7 @@ class RegressionTests extends CatsSuite { object State { implicit def instance[S]: Monad[State[S, ?]] = new Monad[State[S, ?]] { def pure[A](a: A): State[S, A] = State(s => (a, s)) + def map[A, B](sa: State[S, A])(f: A => B): State[S, B] = sa.map(f) def flatMap[A, B](sa: State[S, A])(f: A => State[S, B]): State[S, B] = sa.flatMap(f) } } From ac993cb6e3caa1a4fbb004f90a3ae58fd7328f49 Mon Sep 17 00:00:00 2001 From: Owen Parry Date: Mon, 22 Jun 2015 08:43:40 -0700 Subject: [PATCH 2/5] ap + canon --- build.sbt | 12 ++++++++---- .../cats/canon/CanonicalApplicativeFromMonad.scala | 14 ++++++++++++++ .../canon/CanonicalFunctorFromApplicative.scala | 13 +++++++++++++ core/src/main/scala/cats/FlatMap.scala | 3 --- core/src/main/scala/cats/data/Cokleisli.scala | 5 +++++ core/src/main/scala/cats/data/Ior.scala | 13 +++++++++++++ core/src/main/scala/cats/data/Kleisli.scala | 11 ++++++++++- core/src/main/scala/cats/data/OneAnd.scala | 3 +++ core/src/main/scala/cats/data/Xor.scala | 12 +++++++++++- core/src/main/scala/cats/data/XorT.scala | 5 +++-- free/src/main/scala/cats/free/Free.scala | 6 +++++- .../src/main/scala/cats/laws/ApplicativeLaws.scala | 5 ++++- state/src/main/scala/cats/state/State.scala | 12 +++++++++++- std/src/main/scala/cats/std/either.scala | 5 ++++- std/src/main/scala/cats/std/function.scala | 10 ++++++++-- std/src/main/scala/cats/std/future.scala | 7 +++++-- std/src/main/scala/cats/std/list.scala | 5 ++++- std/src/main/scala/cats/std/option.scala | 6 ++++++ std/src/main/scala/cats/std/stream.scala | 5 ++++- std/src/main/scala/cats/std/vector.scala | 5 ++++- .../test/scala/cats/tests/RegressionTests.scala | 7 +++++-- 21 files changed, 140 insertions(+), 24 deletions(-) create mode 100644 canon/src/main/scala/cats/canon/CanonicalApplicativeFromMonad.scala create mode 100644 canon/src/main/scala/cats/canon/CanonicalFunctorFromApplicative.scala diff --git a/build.sbt b/build.sbt index 3684532415..7fb0f87a97 100644 --- a/build.sbt +++ b/build.sbt @@ -93,8 +93,8 @@ lazy val docs = project lazy val cats = project.in(file(".")) .settings(catsSettings) .settings(noPublishSettings) - .aggregate(macros, core, laws, tests, docs, free, std, bench, state) - .dependsOn(macros, core, laws, tests, docs, free, std, bench, state) + .aggregate(macros, core, canon, laws, tests, docs, free, std, bench, state) + .dependsOn(macros, core, canon, laws, tests, docs, free, std, bench, state) lazy val macros = project .settings(moduleName := "cats-macros") @@ -107,7 +107,11 @@ lazy val core = project.dependsOn(macros) sourceGenerators in Compile <+= (sourceManaged in Compile).map(Boilerplate.gen) ) -lazy val laws = project.dependsOn(macros, core, free, std) +lazy val canon = project.dependsOn(macros, core) + .settings(moduleName := "cats-canon") + .settings(catsSettings) + +lazy val laws = project.dependsOn(macros, core, canon, free, std) .settings(moduleName := "cats-laws") .settings(catsSettings) .settings( @@ -123,7 +127,7 @@ lazy val std = project.dependsOn(macros, core) libraryDependencies += "org.spire-math" %% "algebra-std" % "0.2.0-SNAPSHOT" ) -lazy val tests = project.dependsOn(macros, core, free, std, laws) +lazy val tests = project.dependsOn(macros, core, canon, free, std, laws) .settings(moduleName := "cats-tests") .settings(catsSettings) .settings(noPublishSettings) diff --git a/canon/src/main/scala/cats/canon/CanonicalApplicativeFromMonad.scala b/canon/src/main/scala/cats/canon/CanonicalApplicativeFromMonad.scala new file mode 100644 index 0000000000..3d21061311 --- /dev/null +++ b/canon/src/main/scala/cats/canon/CanonicalApplicativeFromMonad.scala @@ -0,0 +1,14 @@ +package cats +package canon + +trait CanonicalApplicativeFromMonad[F[_]] extends Applicative[F] with CanonicalFunctorFromApplicative[F] { self: Monad[F] => + def ap[A, B](fa: F[A])(ff: F[A => B]): F[B] = + flatMap(ff)(f => map(fa)(f)) +} + +object CanonicalApplicativeFromMonad { + def apply[F[_]](implicit F: Monad[F]): Applicative[F] = new Monad[F] with CanonicalApplicativeFromMonad[F] { + override def pure[A](x: A): F[A] = F.pure(x) + override def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = F.flatMap(fa)(f) + } +} \ No newline at end of file diff --git a/canon/src/main/scala/cats/canon/CanonicalFunctorFromApplicative.scala b/canon/src/main/scala/cats/canon/CanonicalFunctorFromApplicative.scala new file mode 100644 index 0000000000..c9ef06e865 --- /dev/null +++ b/canon/src/main/scala/cats/canon/CanonicalFunctorFromApplicative.scala @@ -0,0 +1,13 @@ +package cats +package canon + +trait CanonicalFunctorFromApplicative[F[_]] extends Functor[F] { self: Applicative[F] => + def map[A, B](fa: F[A])(f: A => B): F[B] = ap(fa)(pure(f)) +} + +object CanonicalFunctorFromApplicative { + def apply[F[_]](implicit F: Applicative[F]): Functor[F] = new Applicative[F] with CanonicalFunctorFromApplicative[F] { + override def pure[A](x: A): F[A] = F.pure(x) + override def ap[A, B](fa: F[A])(f: F[(A) => B]): F[B] = F.ap(fa)(f) + } +} diff --git a/core/src/main/scala/cats/FlatMap.scala b/core/src/main/scala/cats/FlatMap.scala index aa07cea5a7..67da889862 100644 --- a/core/src/main/scala/cats/FlatMap.scala +++ b/core/src/main/scala/cats/FlatMap.scala @@ -26,9 +26,6 @@ import simulacrum._ def flatten[A](ffa: F[F[A]]): F[A] = flatMap(ffa)(fa => fa) - override def ap[A, B](fa: F[A])(ff: F[A => B]): F[B] = - flatMap(ff)(f => map(fa)(f)) - /** * Pair `A` with the result of function application. */ diff --git a/core/src/main/scala/cats/data/Cokleisli.scala b/core/src/main/scala/cats/data/Cokleisli.scala index 7db6fa72b7..f124f68b64 100644 --- a/core/src/main/scala/cats/data/Cokleisli.scala +++ b/core/src/main/scala/cats/data/Cokleisli.scala @@ -19,6 +19,9 @@ final case class Cokleisli[F[_], A, B](run: F[A] => B) { self => def map[C](f: B => C): Cokleisli[F, A, C] = Cokleisli(f compose run) + def ap[C](f: Cokleisli[F, A, B => C]): Cokleisli[F, A, C] = + Cokleisli(fa => f.run(fa)(self.run(fa))) + def contramapValue[C](f: F[C] => F[A]): Cokleisli[F, C, B] = Cokleisli(run compose f) @@ -62,6 +65,8 @@ sealed abstract class CokleisliInstances extends CokleisliInstances0 { override def map[B, C](fa: Cokleisli[F, A, B])(f: B => C): Cokleisli[F, A, C] = fa.map(f) + + override def ap[B, C](fa: Cokleisli[F, A, B])(f: Cokleisli[F, A, B => C]): Cokleisli[F, A, C] = fa.ap(f) } } diff --git a/core/src/main/scala/cats/data/Ior.scala b/core/src/main/scala/cats/data/Ior.scala index 933d34ea46..c56cbbafb6 100644 --- a/core/src/main/scala/cats/data/Ior.scala +++ b/core/src/main/scala/cats/data/Ior.scala @@ -60,6 +60,18 @@ sealed abstract class Ior[+A, +B] extends Product with Serializable { final def map[D](f: B => D): A Ior D = bimap(identity, f) final def leftMap[C](f: A => C): C Ior B = bimap(f, identity) + final def ap[AA >: A, D](f: AA Ior (B => D))(implicit AA: Semigroup[AA]): AA Ior D = { + (f, this) match { + case (Ior.Left(fl), _) => Ior.Left(fl) + case (Ior.Right(fr), Ior.Left(l)) => Ior.Left(l) + case (Ior.Right(fr), Ior.Right(r)) => Ior.Right(fr(r)) + case (Ior.Right(fr), Ior.Both(l, r)) => Ior.Both(l, fr(r)) + case (Ior.Both(fl, fr), Ior.Left(l)) => Ior.Left(AA.combine(fl, l)) + case (Ior.Both(fl, fr), Ior.Right(r)) => Ior.Both(fl, fr(r)) + case (Ior.Both(fl, fr), Ior.Both(l, r)) => Ior.Both(AA.combine(fl, l), fr(r)) + } + } + final def flatMap[AA >: A, D](f: B => AA Ior D)(implicit AA: Semigroup[AA]): AA Ior D = this match { case l @ Ior.Left(_) => l case Ior.Right(b) => f(b) @@ -138,6 +150,7 @@ sealed abstract class IorInstances extends IorInstances0 { implicit def iorMonad[A: Semigroup]: Monad[A Ior ?] = new Monad[A Ior ?] { def pure[B](b: B): A Ior B = Ior.right(b) def map[B, C](fa: A Ior B)(f: B => C): A Ior C = fa.map(f) + def ap[B, C](fa: A Ior B)(f: A Ior (B => C)): A Ior C = fa.ap(f) def flatMap[B, C](fa: A Ior B)(f: B => A Ior C): A Ior C = fa.flatMap(f) } } diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index 6e63d4df34..a3021c3334 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -24,6 +24,9 @@ final case class Kleisli[F[_], A, B](run: A => F[B]) { self => def mapF[N[_], C](f: F[B] => N[C]): Kleisli[N, A, C] = Kleisli(run andThen f) + def ap[C](f: Kleisli[F, A, B => C])(implicit F: Apply[F]): Kleisli[F, A, C] = + Kleisli(a => F.ap(run(a))(f.run(a))) + def flatMap[C](f: B => Kleisli[F, A, C])(implicit F: FlatMap[F]): Kleisli[F, A, C] = Kleisli((r: A) => F.flatMap[B, C](run(r))((b: B) => f(b).run(r))) @@ -86,6 +89,9 @@ sealed abstract class KleisliInstances extends KleisliInstances0 { def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = fa.map(f) + override def ap[B, C](fa: Kleisli[F, A, B])(f: Kleisli[F, A, B => C]): Kleisli[F, A, C] = + fa.ap(f) + def flatMap[B, C](fa: Kleisli[F, A, B])(f: B => Kleisli[F, A, C]): Kleisli[F, A, C] = fa.flatMap(f) } @@ -102,6 +108,9 @@ sealed abstract class KleisliInstances0 extends KleisliInstances1 { def flatMap[B, C](fa: Kleisli[F, A, B])(f: B => Kleisli[F, A, C]): Kleisli[F, A, C] = fa.flatMap(f) + def ap[B, C](fa: Kleisli[F, A, B])(f: Kleisli[F, A, B => C]): Kleisli[F, A, C] = + fa.ap(f) + def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = fa.map(f) } @@ -112,7 +121,7 @@ sealed abstract class KleisliInstances1 extends KleisliInstances2 { def pure[B](x: B): Kleisli[F, A, B] = Kleisli.pure[F, A, B](x) - override def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = + def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = fa.map(f) def ap[B, C](fa: Kleisli[F, A, B])(f: Kleisli[F, A, B => C]): Kleisli[F, A, C] = diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 3f5c6915c3..23cf42b143 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -126,6 +126,9 @@ trait OneAndInstances { def pure[A](x: A): OneAnd[A, F] = OneAnd(x, monad.empty) + override def ap[A, B](fa: OneAnd[A, F])(f: OneAnd[A => B, F]): OneAnd[B, F] = + flatMap(f)(ff => map(fa)(ff)) + def flatMap[A, B](fa: OneAnd[A, F])(f: A => OneAnd[B, F]): OneAnd[B, F] = { val end = monad.flatMap(fa.tail) { a => val fa = f(a) diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index 204556cba2..352ecb4b41 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -79,6 +79,15 @@ sealed abstract class Xor[+A, +B] extends Product with Serializable { case r @ Xor.Right(_) => r } + def ap[AA >: A, D](f: AA Xor (B => D)): AA Xor D = f match { + case l @ Xor.Left(_) => l + case Xor.Right(ff) => + this match { + case l @ Xor.Left(_) => l + case Xor.Right(b) => Xor.Right(ff(b)) + } + } + def flatMap[AA >: A, D](f: B => AA Xor D): AA Xor D = this match { case l @ Xor.Left(_) => l case Xor.Right(b) => f(b) @@ -153,7 +162,8 @@ sealed abstract class XorInstances extends XorInstances1 { def partialFold[B, C](fa: A Xor B)(f: B => Fold[C]): Fold[C] = fa.partialFold(f) def flatMap[B, C](fa: A Xor B)(f: B => A Xor C): A Xor C = fa.flatMap(f) def pure[B](b: B): A Xor B = Xor.right(b) - override def map[B, C](fa: A Xor B)(f: B => C): A Xor C = fa.map(f) + def map[B, C](fa: A Xor B)(f: B => C): A Xor C = fa.map(f) + def ap[B, C](fa: A Xor B)(f: A Xor (B => C)): A Xor C = fa.ap(f) } } diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index 89de252dfd..a8c959cf7b 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -36,7 +36,7 @@ case class XorT[F[_], A, B](value: F[A Xor B]) { def bimap[C, D](fa: A => C, fb: B => D)(implicit F: Functor[F]): XorT[F, C, D] = XorT(F.map(value)(_.bimap(fa, fb))) - def applyAlt[D](ff: XorT[F, A, B => D])(implicit F: Apply[F]): XorT[F, A, D] = + def ap[D](ff: XorT[F, A, B => D])(implicit F: Apply[F]): XorT[F, A, D] = XorT[F, A, D](F.map2(this.value, ff.value)((xb, xbd) => Apply[A Xor ?].ap(xb)(xbd))) def flatMap[AA >: A, D](f: B => XorT[F, AA, D])(implicit F: Monad[F]): XorT[F, AA, D] = @@ -156,12 +156,13 @@ private[data] abstract class XorTInstances3 { private[data] trait XorTFunctor[F[_], L] extends Functor[XorT[F, L, ?]] { implicit val F: Functor[F] - override def map[A, B](fa: XorT[F, L, A])(f: A => B): XorT[F, L, B] = fa map f + def map[A, B](fa: XorT[F, L, A])(f: A => B): XorT[F, L, B] = fa map f } private[data] trait XorTMonad[F[_], L] extends Monad[XorT[F, L, ?]] with XorTFunctor[F, L] { implicit val F: Monad[F] def pure[A](a: A): XorT[F, L, A] = XorT.pure[F, L, A](a) + def ap[A, B](fa: XorT[F, L, A])(f: XorT[F, L, A => B]): XorT[F, L, B] = fa ap f def flatMap[A, B](fa: XorT[F, L, A])(f: A => XorT[F, L, B]): XorT[F, L, B] = fa flatMap f } diff --git a/free/src/main/scala/cats/free/Free.scala b/free/src/main/scala/cats/free/Free.scala index 0720030e3f..4ccf4722a6 100644 --- a/free/src/main/scala/cats/free/Free.scala +++ b/free/src/main/scala/cats/free/Free.scala @@ -54,7 +54,8 @@ object Free { implicit def freeMonad[S[_]:Functor]: Monad[Free[S, ?]] = new Monad[Free[S, ?]] { def pure[A](a: A): Free[S, A] = Pure(a) - override def map[A, B](fa: Free[S, A])(f: A => B): Free[S, B] = fa map f + def map[A, B](fa: Free[S, A])(f: A => B): Free[S, B] = fa map f + def ap[A, B](fa: Free[S, A])(f: Free[S, A => B]): Free[S, B] = fa ap f def flatMap[A, B](a: Free[S, A])(f: A => Free[S, B]): Free[S, B] = a flatMap f } } @@ -71,6 +72,9 @@ sealed abstract class Free[S[_], A] extends Serializable { final def map[B](f: A => B): Free[S, B] = flatMap(a => Pure(f(a))) + final def ap[B](f: Free[S, A => B]): Free[S, B] = + f.flatMap(ff => map(ff)) + /** * Bind the given continuation to the result of this computation. * All left-associated binds are reassociated to the right. diff --git a/laws/src/main/scala/cats/laws/ApplicativeLaws.scala b/laws/src/main/scala/cats/laws/ApplicativeLaws.scala index 18f5249b81..12791cd5de 100644 --- a/laws/src/main/scala/cats/laws/ApplicativeLaws.scala +++ b/laws/src/main/scala/cats/laws/ApplicativeLaws.scala @@ -1,6 +1,7 @@ package cats package laws +import cats.canon.CanonicalFunctorFromApplicative import cats.syntax.apply._ import cats.syntax.functor._ @@ -19,8 +20,10 @@ trait ApplicativeLaws[F[_]] extends ApplyLaws[F] { def applicativeInterchange[A, B](a: A, ff: F[A => B]): IsEq[F[B]] = F.pure(a).ap(ff) <-> ff.ap(F.pure(f => f(a))) + // Test that implementation matches the canonical one (except for performance) + val canon = CanonicalFunctorFromApplicative[F] def applicativeMap[A, B](fa: F[A], f: A => B): IsEq[F[B]] = - fa.map(f) <-> fa.ap(F.pure(f)) + fa.map(f) <-> canon.map(fa)(f) /** * This law is [[applyComposition]] stated in terms of `pure`. It is a diff --git a/state/src/main/scala/cats/state/State.scala b/state/src/main/scala/cats/state/State.scala index 8f9db0d73c..768f507362 100644 --- a/state/src/main/scala/cats/state/State.scala +++ b/state/src/main/scala/cats/state/State.scala @@ -20,6 +20,13 @@ final class StateT[F[_], S, A](val runF: F[S => F[(S, A)]]) { } }) + def ap[B](sf: StateT[F, S, A => B])(implicit F: Monad[F]): StateT[F, S, B] = + StateT( s => + F.flatMap(sf.run(s)){ case (s1, f) => + F.map(run(s1)){ case (s2, a) => (s2, f(a)) } + } + ) + def map[B](f: A => B)(implicit F: Monad[F]): StateT[F, S, B] = transform { case (s, a) => (s, f(a)) } @@ -100,7 +107,10 @@ sealed abstract class StateTInstances extends StateTInstances0 { def flatMap[A, B](fa: StateT[F, S, A])(f: A => StateT[F, S, B]): StateT[F, S, B] = fa.flatMap(f) - override def map[A, B](fa: StateT[F, S, A])(f: A => B): StateT[F, S, B] = + def ap[A, B](fa: StateT[F, S, A])(f: StateT[F, S, A => B]): StateT[F, S, B] = + fa.ap(f) + + def map[A, B](fa: StateT[F, S, A])(f: A => B): StateT[F, S, B] = fa.map(f) } } diff --git a/std/src/main/scala/cats/std/either.scala b/std/src/main/scala/cats/std/either.scala index bab040a4f0..94176e5185 100644 --- a/std/src/main/scala/cats/std/either.scala +++ b/std/src/main/scala/cats/std/either.scala @@ -9,7 +9,10 @@ trait EitherInstances extends EitherInstances1 { def flatMap[B, C](fa: Either[A, B])(f: B => Either[A, C]): Either[A, C] = fa.right.flatMap(f) - override def map[B, C](fa: Either[A, B])(f: B => C): Either[A, C] = + def ap[B, C](fa: Either[A, B])(f: Either[A, B => C]): Either[A, C] = + f.fold(Left(_), ff => fa.fold(Left(_), b => Right(ff(b)))) + + def map[B, C](fa: Either[A, B])(f: B => C): Either[A, C] = fa.right.map(f) def traverse[F[_], B, C](fa: Either[A, B])(f: B => F[C])(implicit F: Applicative[F]): F[Either[A, C]] = diff --git a/std/src/main/scala/cats/std/function.scala b/std/src/main/scala/cats/std/function.scala index d77636e17c..1cfc919e3e 100644 --- a/std/src/main/scala/cats/std/function.scala +++ b/std/src/main/scala/cats/std/function.scala @@ -15,9 +15,12 @@ trait Function0Instances { def pure[A](x: A): () => A = () => x - override def map[A, B](fa: () => A)(f: (A) => B): () => B = + def map[A, B](fa: () => A)(f: A => B): () => B = () => f(fa()) + def ap[A, B](fa: () => A)(f: () => A => B): () => B = + () => f()(fa()) + def flatMap[A, B](fa: () => A)(f: A => () => B): () => B = () => f(fa())() } @@ -42,7 +45,10 @@ trait Function1Instances { def flatMap[R1, R2](fa: T1 => R1)(f: R1 => T1 => R2): T1 => R2 = t => f(fa(t))(t) - override def map[R1, R2](fa: T1 => R1)(f: R1 => R2): T1 => R2 = + def ap[R1, R2](fa: T1 => R1)(f: T1 => R1 => R2): T1 => R2 = + t => f(t)(fa(t)) + + def map[R1, R2](fa: T1 => R1)(f: R1 => R2): T1 => R2 = f.compose(fa) } diff --git a/std/src/main/scala/cats/std/future.scala b/std/src/main/scala/cats/std/future.scala index 3c9c2faaf6..0fb19cb546 100644 --- a/std/src/main/scala/cats/std/future.scala +++ b/std/src/main/scala/cats/std/future.scala @@ -11,9 +11,12 @@ trait FutureInstances extends FutureInstances1 { def pure[A](x: A): Future[A] = Future.successful(x) - def flatMap[A, B](fa: Future[A])(f: A => Future[B]): Future[B] = fa.flatMap(f) + def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f) - override def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f) + def ap[A, B](fa: Future[A])(f: Future[A => B]): Future[B] = + fa.zip(f).map { case (a, ff) => ff(a) } + + def flatMap[A, B](fa: Future[A])(f: A => Future[B]): Future[B] = fa.flatMap(f) } implicit def futureSemigroup[A](implicit A: Semigroup[A], ec: ExecutionContext): Semigroup[Future[A]] = diff --git a/std/src/main/scala/cats/std/list.scala b/std/src/main/scala/cats/std/list.scala index aab089ede3..ed851b631d 100644 --- a/std/src/main/scala/cats/std/list.scala +++ b/std/src/main/scala/cats/std/list.scala @@ -17,9 +17,12 @@ trait ListInstances { def pure[A](x: A): List[A] = x :: Nil - override def map[A, B](fa: List[A])(f: A => B): List[B] = + def map[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f) + def ap[A, B](fa: List[A])(f: List[A => B]): List[B] = + f.flatMap(ff => fa.map(ff)) + def flatMap[A, B](fa: List[A])(f: A => List[B]): List[B] = fa.flatMap(f) diff --git a/std/src/main/scala/cats/std/option.scala b/std/src/main/scala/cats/std/option.scala index bf811ae2a2..82d5edefa5 100644 --- a/std/src/main/scala/cats/std/option.scala +++ b/std/src/main/scala/cats/std/option.scala @@ -16,6 +16,12 @@ trait OptionInstances { def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa.map(f) + def ap[A, B](fa: Option[A])(f: Option[A => B]): Option[B] = + (fa, f) match { + case (Some(a), Some(ff)) => Some(ff(a)) + case _ => None + } + def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] = fa.flatMap(f) diff --git a/std/src/main/scala/cats/std/stream.scala b/std/src/main/scala/cats/std/stream.scala index 35dd59a414..69acecbc11 100644 --- a/std/src/main/scala/cats/std/stream.scala +++ b/std/src/main/scala/cats/std/stream.scala @@ -13,9 +13,12 @@ trait StreamInstances { def pure[A](x: A): Stream[A] = x #:: Stream.Empty - override def map[A, B](fa: Stream[A])(f: A => B): Stream[B] = + def map[A, B](fa: Stream[A])(f: A => B): Stream[B] = fa.map(f) + def ap[A, B](fa: Stream[A])(f: Stream[A => B]): Stream[B] = + f.flatMap(ff => fa.map(a => ff(a))) + def flatMap[A, B](fa: Stream[A])(f: A => Stream[B]): Stream[B] = fa.flatMap(f) diff --git a/std/src/main/scala/cats/std/vector.scala b/std/src/main/scala/cats/std/vector.scala index 5eeaeb539f..2f74ebd44c 100644 --- a/std/src/main/scala/cats/std/vector.scala +++ b/std/src/main/scala/cats/std/vector.scala @@ -13,9 +13,12 @@ trait VectorInstances { def pure[A](x: A): Vector[A] = Vector(x) - override def map[A, B](fa: Vector[A])(f: A => B): Vector[B] = + def map[A, B](fa: Vector[A])(f: A => B): Vector[B] = fa.map(f) + def ap[A, B](fa: Vector[A])(f: Vector[A => B]): Vector[B] = + f.flatMap(ff => fa.map(a => ff(a))) + def flatMap[A, B](fa: Vector[A])(f: A => Vector[B]): Vector[B] = fa.flatMap(f) diff --git a/tests/src/test/scala/cats/tests/RegressionTests.scala b/tests/src/test/scala/cats/tests/RegressionTests.scala index ea9d8b90dc..c1fa5fa28d 100644 --- a/tests/src/test/scala/cats/tests/RegressionTests.scala +++ b/tests/src/test/scala/cats/tests/RegressionTests.scala @@ -1,6 +1,8 @@ package cats package tests +import cats.canon.{CanonicalApplicativeFromMonad} + import scala.collection.mutable class RegressionTests extends CatsSuite { @@ -15,9 +17,10 @@ class RegressionTests extends CatsSuite { } object State { - implicit def instance[S]: Monad[State[S, ?]] = new Monad[State[S, ?]] { + implicit def instance[S]: Monad[State[S, ?]] = new Monad[State[S, ?]] + with CanonicalApplicativeFromMonad[State[S, ?]] { def pure[A](a: A): State[S, A] = State(s => (a, s)) - def map[A, B](sa: State[S, A])(f: A => B): State[S, B] = sa.map(f) + override def map[A, B](sa: State[S, A])(f: A => B): State[S, B] = sa.map(f) def flatMap[A, B](sa: State[S, A])(f: A => State[S, B]): State[S, B] = sa.flatMap(f) } } From 4271969713b8c1ed9c62ed1b5e46700610b71be2 Mon Sep 17 00:00:00 2001 From: Owen Parry Date: Mon, 22 Jun 2015 19:54:16 -0700 Subject: [PATCH 3/5] ap and map2 tests --- .../scala/cats/canon/CanonicalApplicativeFromMonad.scala | 6 +++--- .../scala/cats/canon/CanonicalFunctorFromApplicative.scala | 2 +- core/src/main/scala/cats/data/XorT.scala | 5 ++++- laws/src/main/scala/cats/laws/ApplicativeLaws.scala | 4 +++- laws/src/main/scala/cats/laws/MonadLaws.scala | 4 ++++ .../main/scala/cats/laws/discipline/ApplicativeTests.scala | 4 +++- laws/src/main/scala/cats/laws/discipline/MonadTests.scala | 4 +++- 7 files changed, 21 insertions(+), 8 deletions(-) diff --git a/canon/src/main/scala/cats/canon/CanonicalApplicativeFromMonad.scala b/canon/src/main/scala/cats/canon/CanonicalApplicativeFromMonad.scala index 3d21061311..aa34c2f927 100644 --- a/canon/src/main/scala/cats/canon/CanonicalApplicativeFromMonad.scala +++ b/canon/src/main/scala/cats/canon/CanonicalApplicativeFromMonad.scala @@ -3,12 +3,12 @@ package canon trait CanonicalApplicativeFromMonad[F[_]] extends Applicative[F] with CanonicalFunctorFromApplicative[F] { self: Monad[F] => def ap[A, B](fa: F[A])(ff: F[A => B]): F[B] = - flatMap(ff)(f => map(fa)(f)) + flatMap(ff)(f => flatMap(fa)(a => pure(f(a)))) } object CanonicalApplicativeFromMonad { - def apply[F[_]](implicit F: Monad[F]): Applicative[F] = new Monad[F] with CanonicalApplicativeFromMonad[F] { + def apply[F[_]](implicit F: Monad[F]): Monad[F] = new Monad[F] with CanonicalApplicativeFromMonad[F] { override def pure[A](x: A): F[A] = F.pure(x) override def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = F.flatMap(fa)(f) } -} \ No newline at end of file +} diff --git a/canon/src/main/scala/cats/canon/CanonicalFunctorFromApplicative.scala b/canon/src/main/scala/cats/canon/CanonicalFunctorFromApplicative.scala index c9ef06e865..e3cb262c36 100644 --- a/canon/src/main/scala/cats/canon/CanonicalFunctorFromApplicative.scala +++ b/canon/src/main/scala/cats/canon/CanonicalFunctorFromApplicative.scala @@ -6,7 +6,7 @@ trait CanonicalFunctorFromApplicative[F[_]] extends Functor[F] { self: Applicati } object CanonicalFunctorFromApplicative { - def apply[F[_]](implicit F: Applicative[F]): Functor[F] = new Applicative[F] with CanonicalFunctorFromApplicative[F] { + def apply[F[_]](implicit F: Applicative[F]): Applicative[F] = new Applicative[F] with CanonicalFunctorFromApplicative[F] { override def pure[A](x: A): F[A] = F.pure(x) override def ap[A, B](fa: F[A])(f: F[(A) => B]): F[B] = F.ap(fa)(f) } diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index a8c959cf7b..70a22d13dd 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -36,9 +36,12 @@ case class XorT[F[_], A, B](value: F[A Xor B]) { def bimap[C, D](fa: A => C, fb: B => D)(implicit F: Functor[F]): XorT[F, C, D] = XorT(F.map(value)(_.bimap(fa, fb))) - def ap[D](ff: XorT[F, A, B => D])(implicit F: Apply[F]): XorT[F, A, D] = + def applyAlt[D](ff: XorT[F, A, B => D])(implicit F: Apply[F]): XorT[F, A, D] = XorT[F, A, D](F.map2(this.value, ff.value)((xb, xbd) => Apply[A Xor ?].ap(xb)(xbd))) + def ap[D](ff: XorT[F, A, B => D])(implicit F: Monad[F]): XorT[F, A, D] = + ff.flatMap(f => map(f)) + def flatMap[AA >: A, D](f: B => XorT[F, AA, D])(implicit F: Monad[F]): XorT[F, AA, D] = XorT(F.flatMap(value) { case l @ Xor.Left(_) => F.pure(l) diff --git a/laws/src/main/scala/cats/laws/ApplicativeLaws.scala b/laws/src/main/scala/cats/laws/ApplicativeLaws.scala index 12791cd5de..a52a461783 100644 --- a/laws/src/main/scala/cats/laws/ApplicativeLaws.scala +++ b/laws/src/main/scala/cats/laws/ApplicativeLaws.scala @@ -21,9 +21,11 @@ trait ApplicativeLaws[F[_]] extends ApplyLaws[F] { F.pure(a).ap(ff) <-> ff.ap(F.pure(f => f(a))) // Test that implementation matches the canonical one (except for performance) - val canon = CanonicalFunctorFromApplicative[F] + private val canon = CanonicalFunctorFromApplicative[F] def applicativeMap[A, B](fa: F[A], f: A => B): IsEq[F[B]] = fa.map(f) <-> canon.map(fa)(f) + def applicativeMap2[A, B, C](fa: F[A], fb: F[B], f: (A, B) => C): IsEq[F[C]] = + F.map2(fa, fb)(f) <-> canon.map2(fa, fb)(f) /** * This law is [[applyComposition]] stated in terms of `pure`. It is a diff --git a/laws/src/main/scala/cats/laws/MonadLaws.scala b/laws/src/main/scala/cats/laws/MonadLaws.scala index cb9c627341..df69dc0b97 100644 --- a/laws/src/main/scala/cats/laws/MonadLaws.scala +++ b/laws/src/main/scala/cats/laws/MonadLaws.scala @@ -1,6 +1,7 @@ package cats package laws +import cats.canon.CanonicalApplicativeFromMonad import cats.data.Kleisli import cats.syntax.flatMap._ @@ -16,6 +17,9 @@ trait MonadLaws[F[_]] extends ApplicativeLaws[F] with FlatMapLaws[F] { def monadRightIdentity[A](fa: F[A]): IsEq[F[A]] = fa.flatMap(F.pure) <-> fa + private val canon = CanonicalApplicativeFromMonad[F] + def monadAp[A, B](fa: F[A], ff: F[A => B]): IsEq[F[B]] = + F.ap(fa)(ff) <-> canon.ap(fa)(ff) /** * `pure` is the left identity element under left-to-right composition of * [[cats.data.Kleisli]] arrows. This is analogous to [[monadLeftIdentity]]. diff --git a/laws/src/main/scala/cats/laws/discipline/ApplicativeTests.scala b/laws/src/main/scala/cats/laws/discipline/ApplicativeTests.scala index 0b8c77acfb..ab36561060 100644 --- a/laws/src/main/scala/cats/laws/discipline/ApplicativeTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/ApplicativeTests.scala @@ -16,6 +16,7 @@ trait ApplicativeTests[F[_]] extends ApplyTests[F] { EqFC: Eq[F[C]] ): RuleSet = { implicit def ArbFA: Arbitrary[F[A]] = ArbF.synthesize[A] + implicit def ARBFB: Arbitrary[F[B]] = ArbF.synthesize[B] implicit def ArbFAB: Arbitrary[F[A => B]] = ArbF.synthesize[A => B] new DefaultRuleSet( @@ -24,7 +25,8 @@ trait ApplicativeTests[F[_]] extends ApplyTests[F] { "applicative identity" -> forAll(laws.applicativeIdentity[A] _), "applicative homomorphism" -> forAll(laws.applicativeHomomorphism[A, B] _), "applicative interchange" -> forAll(laws.applicativeInterchange[A, B] _), - "applicative map" -> forAll(laws.applicativeMap[A, B] _)) + "applicative map" -> forAll(laws.applicativeMap[A, B] _), + "applicative map2" -> forAll(laws.applicativeMap2[A, B, C] _)) } } diff --git a/laws/src/main/scala/cats/laws/discipline/MonadTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadTests.scala index ef26006d9b..1552a4f835 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonadTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonadTests.scala @@ -17,6 +17,7 @@ trait MonadTests[F[_]] extends ApplicativeTests[F] with FlatMapTests[F] { ): RuleSet = { implicit def ArbFA: Arbitrary[F[A]] = ArbF.synthesize[A] implicit def ArbFB: Arbitrary[F[B]] = ArbF.synthesize[B] + implicit def ArbFAB: Arbitrary[F[A => B]] = ArbF.synthesize[A => B] new RuleSet { def name: String = "monad" @@ -24,7 +25,8 @@ trait MonadTests[F[_]] extends ApplicativeTests[F] with FlatMapTests[F] { def parents: Seq[RuleSet] = Seq(applicative[A, B, C], flatMap[A, B, C]) def props: Seq[(String, Prop)] = Seq( "monad left identity" -> forAll(laws.monadLeftIdentity[A, B] _), - "monad right identity" -> forAll(laws.monadRightIdentity[A] _) + "monad right identity" -> forAll(laws.monadRightIdentity[A] _), + "monad ap" -> forAll(laws.monadAp[A,B] _) ) } } From 19c716226bb250aca9c756e81ed788419d21f2dc Mon Sep 17 00:00:00 2001 From: Owen Parry Date: Mon, 22 Jun 2015 20:01:19 -0700 Subject: [PATCH 4/5] refactor canon --- .../scala/cats/canon/ApplicativeFromMonad.scala | 7 +++++++ .../cats/canon/CanonicalApplicativeFromMonad.scala | 14 -------------- .../canon/CanonicalFunctorFromApplicative.scala | 13 ------------- .../scala/cats/canon/FunctorFromApplicative.scala | 7 +++++++ canon/src/main/scala/cats/canon/package.scala | 14 ++++++++++++++ .../src/main/scala/cats/laws/ApplicativeLaws.scala | 4 ++-- laws/src/main/scala/cats/laws/MonadLaws.scala | 5 +++-- .../test/scala/cats/tests/RegressionTests.scala | 4 ++-- 8 files changed, 35 insertions(+), 33 deletions(-) create mode 100644 canon/src/main/scala/cats/canon/ApplicativeFromMonad.scala delete mode 100644 canon/src/main/scala/cats/canon/CanonicalApplicativeFromMonad.scala delete mode 100644 canon/src/main/scala/cats/canon/CanonicalFunctorFromApplicative.scala create mode 100644 canon/src/main/scala/cats/canon/FunctorFromApplicative.scala create mode 100644 canon/src/main/scala/cats/canon/package.scala diff --git a/canon/src/main/scala/cats/canon/ApplicativeFromMonad.scala b/canon/src/main/scala/cats/canon/ApplicativeFromMonad.scala new file mode 100644 index 0000000000..b282657eef --- /dev/null +++ b/canon/src/main/scala/cats/canon/ApplicativeFromMonad.scala @@ -0,0 +1,7 @@ +package cats +package canon + +trait ApplicativeFromMonad[F[_]] extends Applicative[F] with FunctorFromApplicative[F] { self: Monad[F] => + def ap[A, B](fa: F[A])(ff: F[A => B]): F[B] = + flatMap(ff)(f => flatMap(fa)(a => pure(f(a)))) +} diff --git a/canon/src/main/scala/cats/canon/CanonicalApplicativeFromMonad.scala b/canon/src/main/scala/cats/canon/CanonicalApplicativeFromMonad.scala deleted file mode 100644 index aa34c2f927..0000000000 --- a/canon/src/main/scala/cats/canon/CanonicalApplicativeFromMonad.scala +++ /dev/null @@ -1,14 +0,0 @@ -package cats -package canon - -trait CanonicalApplicativeFromMonad[F[_]] extends Applicative[F] with CanonicalFunctorFromApplicative[F] { self: Monad[F] => - def ap[A, B](fa: F[A])(ff: F[A => B]): F[B] = - flatMap(ff)(f => flatMap(fa)(a => pure(f(a)))) -} - -object CanonicalApplicativeFromMonad { - def apply[F[_]](implicit F: Monad[F]): Monad[F] = new Monad[F] with CanonicalApplicativeFromMonad[F] { - override def pure[A](x: A): F[A] = F.pure(x) - override def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = F.flatMap(fa)(f) - } -} diff --git a/canon/src/main/scala/cats/canon/CanonicalFunctorFromApplicative.scala b/canon/src/main/scala/cats/canon/CanonicalFunctorFromApplicative.scala deleted file mode 100644 index e3cb262c36..0000000000 --- a/canon/src/main/scala/cats/canon/CanonicalFunctorFromApplicative.scala +++ /dev/null @@ -1,13 +0,0 @@ -package cats -package canon - -trait CanonicalFunctorFromApplicative[F[_]] extends Functor[F] { self: Applicative[F] => - def map[A, B](fa: F[A])(f: A => B): F[B] = ap(fa)(pure(f)) -} - -object CanonicalFunctorFromApplicative { - def apply[F[_]](implicit F: Applicative[F]): Applicative[F] = new Applicative[F] with CanonicalFunctorFromApplicative[F] { - override def pure[A](x: A): F[A] = F.pure(x) - override def ap[A, B](fa: F[A])(f: F[(A) => B]): F[B] = F.ap(fa)(f) - } -} diff --git a/canon/src/main/scala/cats/canon/FunctorFromApplicative.scala b/canon/src/main/scala/cats/canon/FunctorFromApplicative.scala new file mode 100644 index 0000000000..71d012325b --- /dev/null +++ b/canon/src/main/scala/cats/canon/FunctorFromApplicative.scala @@ -0,0 +1,7 @@ +package cats +package canon + +trait FunctorFromApplicative[F[_]] extends Functor[F] { self: Applicative[F] => + def map[A, B](fa: F[A])(f: A => B): F[B] = ap(fa)(pure(f)) +} + diff --git a/canon/src/main/scala/cats/canon/package.scala b/canon/src/main/scala/cats/canon/package.scala new file mode 100644 index 0000000000..95746ba26c --- /dev/null +++ b/canon/src/main/scala/cats/canon/package.scala @@ -0,0 +1,14 @@ +package cats + +package object canon { + def canonicalApplicative[F[_]](implicit F: Applicative[F]): Applicative[F] = + new Applicative[F] with FunctorFromApplicative[F] { + override def pure[A](x: A): F[A] = F.pure(x) + override def ap[A, B](fa: F[A])(f: F[(A) => B]): F[B] = F.ap(fa)(f) + } + def canonicalMonad[F[_]](implicit F: Monad[F]): Monad[F] = + new Monad[F] with ApplicativeFromMonad[F] { + override def pure[A](x: A): F[A] = F.pure(x) + override def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = F.flatMap(fa)(f) + } +} diff --git a/laws/src/main/scala/cats/laws/ApplicativeLaws.scala b/laws/src/main/scala/cats/laws/ApplicativeLaws.scala index a52a461783..05efd922c6 100644 --- a/laws/src/main/scala/cats/laws/ApplicativeLaws.scala +++ b/laws/src/main/scala/cats/laws/ApplicativeLaws.scala @@ -1,7 +1,7 @@ package cats package laws -import cats.canon.CanonicalFunctorFromApplicative +import cats.canon.canonicalApplicative import cats.syntax.apply._ import cats.syntax.functor._ @@ -21,7 +21,7 @@ trait ApplicativeLaws[F[_]] extends ApplyLaws[F] { F.pure(a).ap(ff) <-> ff.ap(F.pure(f => f(a))) // Test that implementation matches the canonical one (except for performance) - private val canon = CanonicalFunctorFromApplicative[F] + private val canon = canonicalApplicative[F] def applicativeMap[A, B](fa: F[A], f: A => B): IsEq[F[B]] = fa.map(f) <-> canon.map(fa)(f) def applicativeMap2[A, B, C](fa: F[A], fb: F[B], f: (A, B) => C): IsEq[F[C]] = diff --git a/laws/src/main/scala/cats/laws/MonadLaws.scala b/laws/src/main/scala/cats/laws/MonadLaws.scala index df69dc0b97..a8434f8238 100644 --- a/laws/src/main/scala/cats/laws/MonadLaws.scala +++ b/laws/src/main/scala/cats/laws/MonadLaws.scala @@ -1,7 +1,7 @@ package cats package laws -import cats.canon.CanonicalApplicativeFromMonad +import cats.canon.canonicalMonad import cats.data.Kleisli import cats.syntax.flatMap._ @@ -17,9 +17,10 @@ trait MonadLaws[F[_]] extends ApplicativeLaws[F] with FlatMapLaws[F] { def monadRightIdentity[A](fa: F[A]): IsEq[F[A]] = fa.flatMap(F.pure) <-> fa - private val canon = CanonicalApplicativeFromMonad[F] + private val canon = canonicalMonad[F] def monadAp[A, B](fa: F[A], ff: F[A => B]): IsEq[F[B]] = F.ap(fa)(ff) <-> canon.ap(fa)(ff) + /** * `pure` is the left identity element under left-to-right composition of * [[cats.data.Kleisli]] arrows. This is analogous to [[monadLeftIdentity]]. diff --git a/tests/src/test/scala/cats/tests/RegressionTests.scala b/tests/src/test/scala/cats/tests/RegressionTests.scala index c1fa5fa28d..305c5f32a3 100644 --- a/tests/src/test/scala/cats/tests/RegressionTests.scala +++ b/tests/src/test/scala/cats/tests/RegressionTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.canon.{CanonicalApplicativeFromMonad} +import cats.canon.{ApplicativeFromMonad} import scala.collection.mutable @@ -18,7 +18,7 @@ class RegressionTests extends CatsSuite { object State { implicit def instance[S]: Monad[State[S, ?]] = new Monad[State[S, ?]] - with CanonicalApplicativeFromMonad[State[S, ?]] { + with ApplicativeFromMonad[State[S, ?]] { def pure[A](a: A): State[S, A] = State(s => (a, s)) override def map[A, B](sa: State[S, A])(f: A => B): State[S, B] = sa.map(f) def flatMap[A, B](sa: State[S, A])(f: A => State[S, B]): State[S, B] = sa.flatMap(f) From 49f9a39ecd28bd684733044d957668a4d8b997b8 Mon Sep 17 00:00:00 2001 From: Owen Parry Date: Mon, 22 Jun 2015 21:13:48 -0700 Subject: [PATCH 5/5] fix merge --- laws/src/main/scala/cats/laws/ApplicativeLaws.scala | 5 ++--- laws/src/main/scala/cats/laws/MonadLaws.scala | 3 +-- laws/src/main/scala/cats/laws/TraverseLaws.scala | 8 ++------ tests/src/test/scala/cats/tests/ListWrapper.scala | 3 ++- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/laws/src/main/scala/cats/laws/ApplicativeLaws.scala b/laws/src/main/scala/cats/laws/ApplicativeLaws.scala index 05efd922c6..a16ee5758b 100644 --- a/laws/src/main/scala/cats/laws/ApplicativeLaws.scala +++ b/laws/src/main/scala/cats/laws/ApplicativeLaws.scala @@ -21,11 +21,10 @@ trait ApplicativeLaws[F[_]] extends ApplyLaws[F] { F.pure(a).ap(ff) <-> ff.ap(F.pure(f => f(a))) // Test that implementation matches the canonical one (except for performance) - private val canon = canonicalApplicative[F] def applicativeMap[A, B](fa: F[A], f: A => B): IsEq[F[B]] = - fa.map(f) <-> canon.map(fa)(f) + fa.map(f) <-> canonicalApplicative[F].map(fa)(f) def applicativeMap2[A, B, C](fa: F[A], fb: F[B], f: (A, B) => C): IsEq[F[C]] = - F.map2(fa, fb)(f) <-> canon.map2(fa, fb)(f) + F.map2(fa, fb)(f) <-> canonicalApplicative[F].map2(fa, fb)(f) /** * This law is [[applyComposition]] stated in terms of `pure`. It is a diff --git a/laws/src/main/scala/cats/laws/MonadLaws.scala b/laws/src/main/scala/cats/laws/MonadLaws.scala index a8434f8238..00a8065ef8 100644 --- a/laws/src/main/scala/cats/laws/MonadLaws.scala +++ b/laws/src/main/scala/cats/laws/MonadLaws.scala @@ -17,9 +17,8 @@ trait MonadLaws[F[_]] extends ApplicativeLaws[F] with FlatMapLaws[F] { def monadRightIdentity[A](fa: F[A]): IsEq[F[A]] = fa.flatMap(F.pure) <-> fa - private val canon = canonicalMonad[F] def monadAp[A, B](fa: F[A], ff: F[A => B]): IsEq[F[B]] = - F.ap(fa)(ff) <-> canon.ap(fa)(ff) + F.ap(fa)(ff) <-> canonicalMonad[F].ap(fa)(ff) /** * `pure` is the left identity element under left-to-right composition of diff --git a/laws/src/main/scala/cats/laws/TraverseLaws.scala b/laws/src/main/scala/cats/laws/TraverseLaws.scala index 891e2e5df8..799f4bc1f3 100644 --- a/laws/src/main/scala/cats/laws/TraverseLaws.scala +++ b/laws/src/main/scala/cats/laws/TraverseLaws.scala @@ -3,6 +3,7 @@ package laws import cats.Id import cats.arrow.Compose +import cats.canon.FunctorFromApplicative import cats.syntax.functor._ import cats.syntax.traverse._ @@ -37,14 +38,9 @@ trait TraverseLaws[F[_]] extends FunctorLaws[F] with FoldableLaws[F] { M: Applicative[M] ): IsEq[(M[F[B]], N[F[B]])] = { type MN[Z] = (M[Z], N[Z]) - implicit val MN = new Applicative[MN] { + implicit val MN = new Applicative[MN] with FunctorFromApplicative[MN] { override def pure[X](x: X): MN[X] = (M.pure(x), N.pure(x)) - override def map[X, Y](fa: MN[X])(f: X => Y): MN[Y] = { - val (fam, fan) = fa - (M.map(fam)(f), N.map(fan)(f)) - } - override def ap[X, Y](fa: MN[X])(f: MN[X => Y]): MN[Y] = { val (fam, fan) = fa val (fm, fn) = f diff --git a/tests/src/test/scala/cats/tests/ListWrapper.scala b/tests/src/test/scala/cats/tests/ListWrapper.scala index b2008802a5..f707333a38 100644 --- a/tests/src/test/scala/cats/tests/ListWrapper.scala +++ b/tests/src/test/scala/cats/tests/ListWrapper.scala @@ -1,6 +1,7 @@ package cats package tests +import cats.canon.ApplicativeFromMonad import cats.data.OneAnd import cats.std.list._ import cats.laws.discipline.ArbitraryK @@ -70,7 +71,7 @@ object ListWrapper { def monadCombine: MonadCombine[ListWrapper] = { val M = MonadCombine[List] - new MonadCombine[ListWrapper] { + new MonadCombine[ListWrapper] with ApplicativeFromMonad[ListWrapper] { def pure[A](x: A): ListWrapper[A] = ListWrapper(M.pure(x)) def flatMap[A, B](fa: ListWrapper[A])(f: A => ListWrapper[B]): ListWrapper[B] =