From b40cc923d6d6823f9959c8b70958442ee19a7432 Mon Sep 17 00:00:00 2001 From: Vikraman Choudhury Date: Tue, 27 Oct 2015 18:46:32 -0400 Subject: [PATCH 001/185] Add IdT, the identity monad transformer --- core/src/main/scala/cats/data/IdT.scala | 107 ++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 core/src/main/scala/cats/data/IdT.scala diff --git a/core/src/main/scala/cats/data/IdT.scala b/core/src/main/scala/cats/data/IdT.scala new file mode 100644 index 0000000000..bed9c19440 --- /dev/null +++ b/core/src/main/scala/cats/data/IdT.scala @@ -0,0 +1,107 @@ +package cats +package data + +/** + * `IdT[F[_], A]` is the identity monad transformer. + */ +final case class IdT[F[_], A](value: F[A]) { + + def map[B](f: A => B)(implicit F: Functor[F]): IdT[F, B] = + IdT(F.map(value)(f)) + + def flatMap[B](f: A => IdT[F, B])(implicit F: FlatMap[F]): IdT[F, B] = + IdT(F.flatMap(value)(f.andThen(_.value))) + + def flatMapF[B](f: A => F[B])(implicit F: FlatMap[F]): IdT[F, B] = + IdT(F.flatMap(value)(f)) + + def foldLeft[B](b: B)(f: (B, A) => B)(implicit F: Foldable[F]): B = + F.foldLeft(value, b)(f) + + def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B])(implicit F: Foldable[F]): Eval[B] = + F.foldRight(value, lb)(f) + + def traverse[G[_], B](f: A => G[B])(implicit F: Traverse[F], G: Applicative[G]): G[IdT[F, B]] = + G.map(F.traverse(value)(f))(IdT(_)) + + def ap[B](f: IdT[F, A => B])(implicit F: Apply[F]): IdT[F, B] = + IdT(F.ap(value)(f.value)) + +} + +object IdT extends IdTInstances { + + def pure[F[_], A](a: A)(implicit F: Applicative[F]): IdT[F, A] = + IdT(F.pure(a)) +} + +private[data] sealed trait IdTFunctor[F[_]] extends Functor[IdT[F, ?]] { + implicit val F0: Functor[F] + + def map[A, B](fa: IdT[F, A])(f: A => B): IdT[F, B] = + fa.map(f) +} + +private[data] sealed trait IdTMonad[F[_]] extends Monad[IdT[F, ?]] { + implicit val F0: Monad[F] + + def pure[A](a: A): IdT[F, A] = + IdT.pure(a) + + def flatMap[A, B](fa: IdT[F, A])(f: A => IdT[F, B]): IdT[F, B] = + fa.flatMap(f) +} + +private[data] sealed trait IdTFoldable[F[_]] extends Foldable[IdT[F, ?]] { + implicit val F0: Foldable[F] + + def foldLeft[A, B](fa: IdT[F, A], b: B)(f: (B, A) => B): B = + fa.foldLeft(b)(f) + + def foldRight[A, B](fa: IdT[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + fa.foldRight(lb)(f) +} + +private[data] sealed trait IdTTraverse[F[_]] extends Traverse[IdT[F, ?]] with IdTFoldable[F] { + implicit val F0: Traverse[F] + + def traverse[G[_]: Applicative, A, B](fa: IdT[F, A])(f: A => G[B]): G[IdT[F, B]] = + fa.traverse(f) +} + +private[data] sealed abstract class IdTInstances1 { + implicit def idTFunctor[F[_]](implicit F: Functor[F]): Functor[IdT[F, ?]] = + new IdTFunctor[F] { + implicit val F0: Functor[F] = F + } +} + +private[data] sealed abstract class IdTInstances0 extends IdTInstances1 { + + implicit def idTMonad[F[_]](implicit F: Monad[F]): Monad[IdT[F, ?]] = + new IdTMonad[F] { + implicit val F0: Monad[F] = F + } + + implicit def idTFoldable[F[_]](implicit F: Foldable[F]): Foldable[IdT[F, ?]] = + new IdTFoldable[F] { + implicit val F0: Foldable[F] = F + } + + implicit def idTOrder[F[_], A](implicit F: Order[F[A]]): Order[IdT[F, A]] = + F.on(_.value) +} + +private[data] sealed abstract class IdTInstances extends IdTInstances0 { + + implicit def idTTraverse[F[_]](implicit F: Traverse[F]): Traverse[IdT[F, ?]] = + new IdTTraverse[F] { + implicit val F0: Traverse[F] = F + } + + implicit def idTEq[F[_], A](implicit F: Eq[F[A]]): Eq[IdT[F, A]] = + F.on(_.value) + + implicit def idTShow[F[_], A](implicit F: Show[F[A]]): Show[IdT[F, A]] = + functor.Contravariant[Show].contramap(F)(_.value) +} From 17a94ca435de326fb47ec35ea1efa851fef0c5ab Mon Sep 17 00:00:00 2001 From: Vikraman Choudhury Date: Tue, 24 Nov 2015 04:26:21 -0500 Subject: [PATCH 002/185] Add tests for IdT --- .../cats/laws/discipline/Arbitrary.scala | 3 +++ .../src/test/scala/cats/tests/IdTTests.scala | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/src/test/scala/cats/tests/IdTTests.scala diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index ee71f8d6aa..a5cabdb8df 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -38,6 +38,9 @@ object arbitrary extends ArbitraryInstances0 { implicit def optionTArbitrary[F[_], A](implicit F: Arbitrary[F[Option[A]]]): Arbitrary[OptionT[F, A]] = Arbitrary(F.arbitrary.map(OptionT.apply)) + implicit def idTArbitrary[F[_], A](implicit F: Arbitrary[F[A]]): Arbitrary[IdT[F, A]] = + Arbitrary(F.arbitrary.map(IdT.apply)) + implicit def evalArbitrary[A: Arbitrary]: Arbitrary[Eval[A]] = Arbitrary(Gen.oneOf( getArbitrary[A].map(Eval.now(_)), diff --git a/tests/src/test/scala/cats/tests/IdTTests.scala b/tests/src/test/scala/cats/tests/IdTTests.scala new file mode 100644 index 0000000000..393bf66f15 --- /dev/null +++ b/tests/src/test/scala/cats/tests/IdTTests.scala @@ -0,0 +1,22 @@ +package cats.tests + +import cats.{Foldable, Functor, Monad, Traverse} +import cats.data.IdT +import cats.laws.discipline.{FoldableTests, FunctorTests, MonadTests, SerializableTests, TraverseTests} +import cats.laws.discipline.arbitrary._ + +class IdTTests extends CatsSuite { + + checkAll("IdT[Functor, Int]", FunctorTests[IdT[List, ?]].functor[Int, Int, Int]) + checkAll("Functor[IdT[List, ?]]", SerializableTests.serializable(Functor[IdT[List, ?]])) + + checkAll("IdT[List, Int]", MonadTests[IdT[List, ?]].monad[Int, Int, Int]) + checkAll("Monad[IdT[List, ?]]", SerializableTests.serializable(Monad[IdT[List, ?]])) + + checkAll("IdT[Option, Int]", FoldableTests[IdT[Option, ?]].foldable[Int, Int]) + checkAll("Foldable[IdT[Option, ?]]", SerializableTests.serializable(Foldable[IdT[Option, ?]])) + + checkAll("IdT[Option, Int]", TraverseTests[IdT[Option, ?]].traverse[Int, Int, Int, Int, Option, Option]) + checkAll("Traverse[IdT[Option, ?]]", SerializableTests.serializable(Traverse[IdT[Option, ?]])) + +} From e6cde963dad567fe5fb647acede6ece93b51c957 Mon Sep 17 00:00:00 2001 From: Luke Wyman Date: Fri, 29 Jan 2016 15:42:51 -0700 Subject: [PATCH 003/185] added replicateA to Applicative --- core/src/main/scala/cats/Applicative.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/Applicative.scala b/core/src/main/scala/cats/Applicative.scala index 70bdbf40d6..725d6b3f51 100644 --- a/core/src/main/scala/cats/Applicative.scala +++ b/core/src/main/scala/cats/Applicative.scala @@ -1,6 +1,7 @@ package cats import simulacrum.typeclass +import cats.std.list._ /** * Applicative functor. @@ -28,6 +29,12 @@ import simulacrum.typeclass */ def pureEval[A](x: Eval[A]): F[A] = pure(x.value) + /** + * `replicateA` creates a list of the provided Applicative Functor `fa` repeated `n` times + */ + def replicateA[A](n: Int, fa: F[A]): F[List[A]] = + sequence(List.fill(n)(fa)) + /** * Two sequentially dependent Applicatives can be composed. * @@ -47,7 +54,6 @@ import simulacrum.typeclass def sequence[G[_]: Traverse, A](as: G[F[A]]): F[G[A]] = traverse(as)(a => a) - } trait CompositeApplicative[F[_],G[_]] From 216ad9b15adb8c86706941d1fb7c27dcce383791 Mon Sep 17 00:00:00 2001 From: Adelbert Chang Date: Fri, 12 Feb 2016 23:39:11 -0800 Subject: [PATCH 004/185] Remove required laziness in Prod, fixes #615 --- core/src/main/scala/cats/data/Prod.scala | 27 +++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/core/src/main/scala/cats/data/Prod.scala b/core/src/main/scala/cats/data/Prod.scala index f368e1abef..ab5c6036dd 100644 --- a/core/src/main/scala/cats/data/Prod.scala +++ b/core/src/main/scala/cats/data/Prod.scala @@ -5,20 +5,23 @@ package data * [[Prod]] is a product to two independent functor values. * * See: [[https://www.cs.ox.ac.uk/jeremy.gibbons/publications/iterator.pdf The Essence of the Iterator Pattern]] - */ -sealed trait Prod[F[_], G[_], A] { - def first: F[A] - def second: G[A] -} + */ +final case class Prod[F[_], G[_], A](first: F[A], second: G[A]) + object Prod extends ProdInstances { - def apply[F[_], G[_], A](first0: => F[A], second0: => G[A]): Prod[F, G, A] = new Prod[F, G, A] { - val firstThunk: Eval[F[A]] = Later(first0) - val secondThunk: Eval[G[A]] = Later(second0) - def first: F[A] = firstThunk.value - def second: G[A] = secondThunk.value + type EvalLifted[F[_], A] = Eval[F[A]] + + def always[F[_], G[_], A](first: => F[A], second: => G[A]): Prod[EvalLifted[F, ?], EvalLifted[G, ?], A] = { + val firstThunk: Eval[F[A]] = Always(first) + val secondThunk: Eval[G[A]] = Always(second) + Prod[EvalLifted[F, ?], EvalLifted[G, ?], A](firstThunk, secondThunk) + } + + def later[F[_], G[_], A](first: => F[A], second: => G[A]): Prod[EvalLifted[F, ?], EvalLifted[G, ?], A] = { + val firstThunk: Eval[F[A]] = Later(first) + val secondThunk: Eval[G[A]] = Later(second) + Prod[EvalLifted[F, ?], EvalLifted[G, ?], A](firstThunk, secondThunk) } - def unapply[F[_], G[_], A](x: Prod[F, G, A]): Option[(F[A], G[A])] = - Some((x.first, x.second)) } private[data] sealed abstract class ProdInstances extends ProdInstances0 { From 5d9d2d6f53cd22710a2c70a49469e7439d1f38d1 Mon Sep 17 00:00:00 2001 From: Luke Wyman Date: Fri, 29 Jan 2016 15:42:51 -0700 Subject: [PATCH 005/185] added replicateA to Applicative --- core/src/main/scala/cats/Applicative.scala | 8 +++++++- .../src/test/scala/cats/tests/ApplicativeTests.scala | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/src/test/scala/cats/tests/ApplicativeTests.scala diff --git a/core/src/main/scala/cats/Applicative.scala b/core/src/main/scala/cats/Applicative.scala index 70bdbf40d6..725d6b3f51 100644 --- a/core/src/main/scala/cats/Applicative.scala +++ b/core/src/main/scala/cats/Applicative.scala @@ -1,6 +1,7 @@ package cats import simulacrum.typeclass +import cats.std.list._ /** * Applicative functor. @@ -28,6 +29,12 @@ import simulacrum.typeclass */ def pureEval[A](x: Eval[A]): F[A] = pure(x.value) + /** + * `replicateA` creates a list of the provided Applicative Functor `fa` repeated `n` times + */ + def replicateA[A](n: Int, fa: F[A]): F[List[A]] = + sequence(List.fill(n)(fa)) + /** * Two sequentially dependent Applicatives can be composed. * @@ -47,7 +54,6 @@ import simulacrum.typeclass def sequence[G[_]: Traverse, A](as: G[F[A]]): F[G[A]] = traverse(as)(a => a) - } trait CompositeApplicative[F[_],G[_]] diff --git a/tests/src/test/scala/cats/tests/ApplicativeTests.scala b/tests/src/test/scala/cats/tests/ApplicativeTests.scala new file mode 100644 index 0000000000..d227b681a7 --- /dev/null +++ b/tests/src/test/scala/cats/tests/ApplicativeTests.scala @@ -0,0 +1,12 @@ +package cats +package tests + +import cats.Applicative + + +class ApplicativeTests extends CatsSuite { + + test("replicateA creates a List of 'n' copies of given Applicative 'fa'") { + + } +} \ No newline at end of file From 3a8519e3332202182cd01f266bf432f609d79d19 Mon Sep 17 00:00:00 2001 From: Luke Wyman Date: Mon, 15 Feb 2016 14:25:37 -0700 Subject: [PATCH 006/185] added test for replicateA --- tests/src/test/scala/cats/tests/ApplicativeTests.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/src/test/scala/cats/tests/ApplicativeTests.scala b/tests/src/test/scala/cats/tests/ApplicativeTests.scala index d227b681a7..f93ac4e318 100644 --- a/tests/src/test/scala/cats/tests/ApplicativeTests.scala +++ b/tests/src/test/scala/cats/tests/ApplicativeTests.scala @@ -4,9 +4,13 @@ package tests import cats.Applicative -class ApplicativeTests extends CatsSuite { +class ApplicativeCheck extends CatsSuite { test("replicateA creates a List of 'n' copies of given Applicative 'fa'") { + val A = Applicative[Option] + val fa = A.pure(1) + A.replicateA(5, fa) should === (Some(List(1,1,1,1,1))) + } } \ No newline at end of file From f84f35b7dc4b7c2b6aa46d5ded1f26507c400047 Mon Sep 17 00:00:00 2001 From: Luke Wyman Date: Mon, 15 Feb 2016 15:22:14 -0700 Subject: [PATCH 007/185] modified scaladoc comment on replicateA --- core/src/main/scala/cats/Applicative.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/Applicative.scala b/core/src/main/scala/cats/Applicative.scala index 725d6b3f51..7fc0c37437 100644 --- a/core/src/main/scala/cats/Applicative.scala +++ b/core/src/main/scala/cats/Applicative.scala @@ -30,7 +30,7 @@ import cats.std.list._ def pureEval[A](x: Eval[A]): F[A] = pure(x.value) /** - * `replicateA` creates a list of the provided Applicative Functor `fa` repeated `n` times + * Given fa and n, apply fa n times to construct an F[List[A]] value. */ def replicateA[A](n: Int, fa: F[A]): F[List[A]] = sequence(List.fill(n)(fa)) From 72e6f3de1a7c2b6935b3b06e5e7b3be6c24db70f Mon Sep 17 00:00:00 2001 From: Luke Wyman Date: Tue, 16 Feb 2016 10:59:41 -0700 Subject: [PATCH 008/185] cleaned up scaladoc comment --- core/src/main/scala/cats/Applicative.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/Applicative.scala b/core/src/main/scala/cats/Applicative.scala index 7fc0c37437..b5c97001bc 100644 --- a/core/src/main/scala/cats/Applicative.scala +++ b/core/src/main/scala/cats/Applicative.scala @@ -30,7 +30,7 @@ import cats.std.list._ def pureEval[A](x: Eval[A]): F[A] = pure(x.value) /** - * Given fa and n, apply fa n times to construct an F[List[A]] value. + * Given `fa` and `n`, apply `fa` `n` times to construct an `F[List[A]]` value. */ def replicateA[A](n: Int, fa: F[A]): F[List[A]] = sequence(List.fill(n)(fa)) From 818e029128f40b8670f257db7e51d9ed66ac2f3e Mon Sep 17 00:00:00 2001 From: Luke Wyman Date: Mon, 22 Feb 2016 15:26:30 -0700 Subject: [PATCH 009/185] added scaladocs for CoflatMap and Comonad --- core/src/main/scala/cats/CoflatMap.scala | 40 ++++++++++++++++++++++-- core/src/main/scala/cats/Comonad.scala | 24 ++++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/cats/CoflatMap.scala b/core/src/main/scala/cats/CoflatMap.scala index 4db0e50668..ff26802c13 100644 --- a/core/src/main/scala/cats/CoflatMap.scala +++ b/core/src/main/scala/cats/CoflatMap.scala @@ -3,11 +3,47 @@ package cats import simulacrum.typeclass /** - * Must obey the laws defined in cats.laws.CoflatMapLaws. - */ + * `CoflatMap` is the dual of `FlatMap`. + * + * Must obey the laws in cats.laws.CoflatMapLaws + */ @typeclass trait CoflatMap[F[_]] extends Functor[F] { + + /** + * `coflatMap` is the dual of `flatMap` on `FlatMap`. It applies + * a value in a Monadic context to a function that takes a value + * in a context and returns a normal value. + * + * Example: + * {{{ + * scala> import cats.std.option._ + * scala> import cats.Monad + * scala> import cats.CoflatMap + * scala> val fa = Monad[Option].pure(3) + * scala> def f(a: Option[Int]): Int = a match { + * | case Some(x) => 2 * x + * | case None => 0 } + * scala> CoflatMap[Option].coflatMap(fa)(f) + * res0: Option[Int] = Some(6) + * }}} + */ def coflatMap[A, B](fa: F[A])(f: F[A] => B): F[B] + /** + * `coflatten` is the dual of `flatten` on `FlatMap`. Whereas flatten removes + * a layer of `F`, coflatten adds a layer of `F` + * + * Example: + * {{{ + * scala> import cats.std.option._ + * scala> import cats.Monad + * scala> import cats.CoflatMap + * scala> val fa = Monad[Option].pure(3) + * fa: Option[Int] = Some(3) + * scala> CoflatMap[Option].coflatten(fa) + * res0: Option[Option[Int]] = Some(Some(3)) + * }}} + */ def coflatten[A](fa: F[A]): F[F[A]] = coflatMap(fa)(fa => fa) } diff --git a/core/src/main/scala/cats/Comonad.scala b/core/src/main/scala/cats/Comonad.scala index b29a17596a..95ac7715e7 100644 --- a/core/src/main/scala/cats/Comonad.scala +++ b/core/src/main/scala/cats/Comonad.scala @@ -2,9 +2,29 @@ package cats import simulacrum.typeclass + /** - * Must obey the laws defined in cats.laws.ComonadLaws. - */ + * Comonad + * + * Comonad is the dual of Monad. Whereas Monads allow for the composition of effectful functions, + * Comonads allow for composition of functions that extract the value from their context. + * + * Must obey the laws defined in cats.laws.ComonadLaws. + */ @typeclass trait Comonad[F[_]] extends CoflatMap[F] { + + /** + * `extract` is the dual of `pure` on Monad (via `Applicative`) + * and extracts the value from its context + * + * Example: + * {{{ + * scala> import cats.Id + * scala> import cats.Comonad + * scala> val id = Id.pure(3) + * scala> Comonad[Id].extract(id) + * res0: cats.Id[Int] = 3 + * }}} + */ def extract[A](x: F[A]): A } From b194bed2c4a3bb75eb63ccb08e2195d2a8dd6d07 Mon Sep 17 00:00:00 2001 From: Adelbert Chang Date: Tue, 1 Mar 2016 23:58:14 -0500 Subject: [PATCH 010/185] Remove Prod helpers for Lazy and Always --- core/src/main/scala/cats/data/Prod.scala | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/core/src/main/scala/cats/data/Prod.scala b/core/src/main/scala/cats/data/Prod.scala index ab5c6036dd..4846217368 100644 --- a/core/src/main/scala/cats/data/Prod.scala +++ b/core/src/main/scala/cats/data/Prod.scala @@ -8,21 +8,7 @@ package data */ final case class Prod[F[_], G[_], A](first: F[A], second: G[A]) -object Prod extends ProdInstances { - type EvalLifted[F[_], A] = Eval[F[A]] - - def always[F[_], G[_], A](first: => F[A], second: => G[A]): Prod[EvalLifted[F, ?], EvalLifted[G, ?], A] = { - val firstThunk: Eval[F[A]] = Always(first) - val secondThunk: Eval[G[A]] = Always(second) - Prod[EvalLifted[F, ?], EvalLifted[G, ?], A](firstThunk, secondThunk) - } - - def later[F[_], G[_], A](first: => F[A], second: => G[A]): Prod[EvalLifted[F, ?], EvalLifted[G, ?], A] = { - val firstThunk: Eval[F[A]] = Later(first) - val secondThunk: Eval[G[A]] = Later(second) - Prod[EvalLifted[F, ?], EvalLifted[G, ?], A](firstThunk, secondThunk) - } -} +object Prod extends ProdInstances private[data] sealed abstract class ProdInstances extends ProdInstances0 { implicit def prodAlternative[F[_], G[_]](implicit FF: Alternative[F], GG: Alternative[G]): Alternative[Lambda[X => Prod[F, G, X]]] = new ProdAlternative[F, G] { From 049007508e822fcd61d22f569ab4f24666bac04e Mon Sep 17 00:00:00 2001 From: Luke Wyman Date: Wed, 2 Mar 2016 19:07:44 -0700 Subject: [PATCH 011/185] corrected typo --- core/src/main/scala/cats/Applicative.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/Applicative.scala b/core/src/main/scala/cats/Applicative.scala index 9d88ecf773..3c3eb683f0 100644 --- a/core/src/main/scala/cats/Applicative.scala +++ b/core/src/main/scala/cats/Applicative.scala @@ -54,7 +54,7 @@ import cats.std.list._ def sequence[G[_], A](as: G[F[A]])(implicit G: Traverse[G]): F[G[A]] = G.sequence(as)(this) - selese + } trait CompositeApplicative[F[_],G[_]] From 97644d7989a9102e907b39c7e7442b41dc57e5d2 Mon Sep 17 00:00:00 2001 From: Adelbert Chang Date: Wed, 2 Mar 2016 21:41:56 -0500 Subject: [PATCH 012/185] Make Bifunctor universal, fixes #887 --- core/src/main/scala/cats/functor/Bifunctor.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/functor/Bifunctor.scala b/core/src/main/scala/cats/functor/Bifunctor.scala index 0001161d14..af9a082945 100644 --- a/core/src/main/scala/cats/functor/Bifunctor.scala +++ b/core/src/main/scala/cats/functor/Bifunctor.scala @@ -5,7 +5,7 @@ package functor * A type class of types which give rise to two independent, covariant * functors. */ -trait Bifunctor[F[_, _]] extends Serializable { self => +trait Bifunctor[F[_, _]] extends Any with Serializable { self => /** * The quintessential method of the Bifunctor trait, it applies a From ce3f67e2b5b79871af22e47b3f79720c81f040a8 Mon Sep 17 00:00:00 2001 From: Adelbert Chang Date: Wed, 2 Mar 2016 22:56:27 -0500 Subject: [PATCH 013/185] Add MonadWriter --- core/src/main/scala/cats/MonadWriter.scala | 20 ++++++++ core/src/main/scala/cats/data/WriterT.scala | 18 ++++++-- .../scala/cats/laws/MonadWriterLaws.scala | 23 ++++++++++ .../laws/discipline/MonadWriterTests.scala | 46 +++++++++++++++++++ .../test/scala/cats/tests/WriterTTests.scala | 4 +- 5 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 core/src/main/scala/cats/MonadWriter.scala create mode 100644 laws/src/main/scala/cats/laws/MonadWriterLaws.scala create mode 100644 laws/src/main/scala/cats/laws/discipline/MonadWriterTests.scala diff --git a/core/src/main/scala/cats/MonadWriter.scala b/core/src/main/scala/cats/MonadWriter.scala new file mode 100644 index 0000000000..7529117c84 --- /dev/null +++ b/core/src/main/scala/cats/MonadWriter.scala @@ -0,0 +1,20 @@ +package cats + +/** A monad that support monoidal accumulation (e.g. logging List[String]) */ +trait MonadWriter[F[_], W] extends Monad[F] { + /** Lift a writer action into the effect */ + def writer[A](aw: (A, W)): F[A] + + /** Run the effect and pair the accumulator with the result */ + def listen[A](fa: F[A]): F[(A, W)] + + /** Apply the effectful function to the accumulator */ + def pass[A](fa: F[(A, W => W)]): F[A] + + /** An effect that when run, logs w */ + def tell(w: W): F[Unit] = writer(((), w)) +} + +object MonadWriter { + def apply[F[_], W](implicit F: MonadWriter[F, W]): MonadWriter[F, W] = F +} diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 515d63f5a5..55770196f1 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -48,9 +48,8 @@ final case class WriterT[F[_], L, V](run: F[(L, V)]) { object WriterT extends WriterTInstances with WriterTFunctions private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { - implicit def writerTIdMonad[L:Monoid]: Monad[WriterT[Id, L, ?]] = - writerTMonad[Id, L] + writerTMonadWriter[Id, L] // The Eq[(L, V)] can be derived from an Eq[L] and Eq[V], but we are waiting // on an algebra release that includes https://github.com/non/algebra/pull/82 @@ -95,8 +94,8 @@ private[data] sealed abstract class WriterTInstances1 extends WriterTInstances2 } } private[data] sealed abstract class WriterTInstances2 extends WriterTInstances3 { - implicit def writerTMonad[F[_], L](implicit F: Monad[F], L: Monoid[L]): Monad[WriterT[F, L, ?]] = - new WriterTMonad[F, L] { + implicit def writerTMonadWriter[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadWriter[WriterT[F, L, ?], L] = + new WriterTMonadWriter[F, L] { implicit val F0: Monad[F] = F implicit val L0: Monoid[L] = L } @@ -191,6 +190,17 @@ private[data] sealed trait WriterTMonad[F[_], L] extends WriterTApplicative[F, L fa.flatMap(f) } +private[data] sealed trait WriterTMonadWriter[F[_], L] extends MonadWriter[WriterT[F, L, ?], L] with WriterTMonad[F, L] { + def writer[A](aw: (A, L)): WriterT[F, L, A] = + WriterT.put(aw._1)(aw._2) + + def listen[A](fa: WriterT[F, L, A]): WriterT[F, L, (A, L)] = + WriterT(F0.flatMap(fa.value)(a => F0.map(fa.written)(l => (l, (a, l))))) + + def pass[A](fa: WriterT[F, L, (A, L => L)]): WriterT[F, L, A] = + WriterT(F0.flatMap(fa.value) { case (a, f) => F0.map(fa.written)(l => (f(l), a)) }) +} + private[data] sealed trait WriterTSemigroupK[F[_], L] extends SemigroupK[WriterT[F, L, ?]] { implicit def F0: SemigroupK[F] diff --git a/laws/src/main/scala/cats/laws/MonadWriterLaws.scala b/laws/src/main/scala/cats/laws/MonadWriterLaws.scala new file mode 100644 index 0000000000..c4022a0c88 --- /dev/null +++ b/laws/src/main/scala/cats/laws/MonadWriterLaws.scala @@ -0,0 +1,23 @@ +package cats +package laws + +trait MonadWriterLaws[F[_], W] extends MonadLaws[F] { + implicit override def F: MonadWriter[F, W] + + def monadWriterWriterPure[A](a: A)(implicit W: Monoid[W]): IsEq[F[A]] = + F.writer((a, W.empty)) <-> F.pure(a) + + def monadWriterTellFusion(x: W, y: W)(implicit W: Monoid[W]): IsEq[F[Unit]] = + F.flatMap(F.tell(x))(_ => F.tell(y)) <-> F.tell(W.combine(x, y)) + + def monadWriterListenPure[A](a: A)(implicit W: Monoid[W]): IsEq[F[(A, W)]] = + F.listen(F.pure(a)) <-> F.pure((a, W.empty)) + + def monadWriterListenWriter[A](aw: (A, W)): IsEq[F[(A, W)]] = + F.listen(F.writer(aw)) <-> F.map(F.tell(aw._2))(_ => aw) +} + +object MonadWriterLaws { + def apply[F[_], W](implicit FW: MonadWriter[F, W]): MonadWriterLaws[F, W] = + new MonadWriterLaws[F, W] { def F: MonadWriter[F, W] = FW } +} diff --git a/laws/src/main/scala/cats/laws/discipline/MonadWriterTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadWriterTests.scala new file mode 100644 index 0000000000..19317e8bd3 --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/MonadWriterTests.scala @@ -0,0 +1,46 @@ +package cats +package laws +package discipline + +import cats.laws.discipline.CartesianTests.Isomorphisms +import org.scalacheck.Arbitrary +import org.scalacheck.Prop.forAll + +trait MonadWriterTests[F[_], W] extends MonadTests[F] { + def laws: MonadWriterLaws[F, W] + + def monadWriter[A: Arbitrary: Eq, B: Arbitrary: Eq, C: Arbitrary: Eq](implicit + ArbFA: Arbitrary[F[A]], + ArbFB: Arbitrary[F[B]], + ArbFC: Arbitrary[F[C]], + ArbFAtoB: Arbitrary[F[A => B]], + ArbFBtoC: Arbitrary[F[B => C]], + EqFA: Eq[F[A]], + EqFAW: Eq[F[(A, W)]], + EqFB: Eq[F[B]], + EqFC: Eq[F[C]], + EqFU: Eq[F[Unit]], + EqFABC: Eq[F[(A, B, C)]], + WA: Arbitrary[W], + WM: Monoid[W], + iso: Isomorphisms[F] + ): RuleSet = + new RuleSet { + def name = "monadWriter" + def bases = Nil + def parents = Seq(monad[A, B, C]) + def props = Seq( + "monadWriter writer pure" -> forAll(laws.monadWriterWriterPure[A] _), + "monadWriter tell fusion" -> forAll(laws.monadWriterTellFusion _), + "monadWriter listen pure" -> forAll(laws.monadWriterListenPure[A] _), + "monadWriter listen writer" -> forAll(laws.monadWriterListenWriter[A] _) + ) + } +} + +object MonadWriterTests { + def apply[F[_], W](implicit FW: MonadWriter[F, W]): MonadWriterTests[F, W] = + new MonadWriterTests[F, W] { + def laws: MonadWriterLaws[F, W] = MonadWriterLaws[F, W] + } +} diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index 7d8e874f9b..60ef44d7fb 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -178,8 +178,8 @@ class WriterTTests extends CatsSuite { Apply[WriterT[ListWrapper, ListWrapper[Int], ?]] Applicative[WriterT[ListWrapper, ListWrapper[Int], ?]] FlatMap[WriterT[ListWrapper, ListWrapper[Int], ?]] - checkAll("WriterT[ListWrapper, ListWrapper[Int], ?]", MonadTests[WriterT[ListWrapper, ListWrapper[Int], ?]].monad[Int, Int, Int]) - checkAll("Monad[WriterT[ListWrapper, ListWrapper[Int], ?]]", SerializableTests.serializable(Monad[WriterT[ListWrapper, ListWrapper[Int], ?]])) + checkAll("WriterT[ListWrapper, ListWrapper[Int], ?]", MonadWriterTests[WriterT[ListWrapper, ListWrapper[Int], ?], ListWrapper[Int]].monadWriter[Int, Int, Int]) + checkAll("MonadWriter[WriterT[ListWrapper, ListWrapper[Int], ?], List[String]]", SerializableTests.serializable(MonadWriter[WriterT[ListWrapper, ListWrapper[Int], ?], ListWrapper[Int]])) Functor[WriterT[Id, ListWrapper[Int], ?]] Apply[WriterT[Id, ListWrapper[Int], ?]] From 20bca3f2e35aeeb11773b44103f2219c4f936cca Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Thu, 3 Mar 2016 06:35:26 -0500 Subject: [PATCH 014/185] Remove StreamingT This is based on #858 and a discussion at dinner after the first day of the typelevel summit. --- .../src/main/scala/cats/data/StreamingT.scala | 482 ------------------ .../cats/laws/discipline/Arbitrary.scala | 27 - .../scala/cats/tests/StreamingTTests.scala | 250 --------- .../scala/cats/tests/TransLiftTests.scala | 7 +- 4 files changed, 2 insertions(+), 764 deletions(-) delete mode 100644 core/src/main/scala/cats/data/StreamingT.scala delete mode 100644 tests/src/test/scala/cats/tests/StreamingTTests.scala diff --git a/core/src/main/scala/cats/data/StreamingT.scala b/core/src/main/scala/cats/data/StreamingT.scala deleted file mode 100644 index ec768fa6be..0000000000 --- a/core/src/main/scala/cats/data/StreamingT.scala +++ /dev/null @@ -1,482 +0,0 @@ -package cats -package data - -import cats.syntax.all._ - -/** - * StreamingT[F, A] is a monad transformer which parallels Streaming[A]. - * - * However, there are a few key differences. `StreamingT[F, A]` only - * supports lazy evaluation if `F` does, and if the `F[_]` values are - * constructed lazily. Also, monadic recursion on `StreamingT[F, A]` - * is stack-safe only if monadic recursion on `F` is stack-safe. - * Finally, since `F` is not guaranteed to have a `Comonad`, it does - * not support many methods on `Streaming[A]` which return immediate - * values. - */ -sealed abstract class StreamingT[F[_], A] extends Product with Serializable { lhs => - - import StreamingT.{Empty, Wait, Cons} - - /** - * Deconstruct a stream into a head and tail (if available). - * - * This method will evaluate the stream until it finds a head and - * tail, or until the stream is exhausted. - */ - def uncons(implicit ev: Monad[F]): F[Option[(A, F[StreamingT[F, A]])]] = - this match { - case Cons(a, ft) => ev.pure(Some((a, ft))) - case Wait(ft) => ft.flatMap(_.uncons) - case Empty() => ev.pure(None) - } - - /** - * Lazily transform the stream given a function `f`. - */ - def map[B](f: A => B)(implicit ev: Functor[F]): StreamingT[F, B] = - this match { - case Cons(a, ft) => Cons(f(a), ft.map(_.map(f))) - case Wait(ft) => Wait(ft.map(_.map(f))) - case Empty() => Empty() - } - - /** - * Lazily transform the stream given a function `f`. - */ - def flatMap[B](f: A => StreamingT[F, B])(implicit ev: Monad[F]): StreamingT[F, B] = { - this match { - case Cons(a, ft) => - Wait(f(a) fconcat ft.map(_.flatMap(f))) - case Wait(ft) => - Wait(ft.map(_.flatMap(f))) - case Empty() => - Empty() - } - } - - /** - * xyz - */ - def coflatMap[B](f: StreamingT[F, A] => B)(implicit ev: Functor[F]): StreamingT[F, B] = - this match { - case Cons(a, ft) => Cons(f(this), ft.map(_.coflatMap(f))) - case Wait(ft) => Wait(ft.map(_.coflatMap(f))) - case Empty() => Empty() - } - - /** - * Lazily filter the stream given the predicate `f`. - */ - def filter(f: A => Boolean)(implicit ev: Functor[F]): StreamingT[F, A] = - this match { - case Cons(a, ft) => - val tail = ft.map(_.filter(f)) - if (f(a)) Cons(a, tail) else Wait(tail) - case Wait(ft) => Wait(ft.map(_.filter(f))) - case Empty() => this - } - - /** - * Eagerly fold the stream to a single value from the left. - */ - def foldLeft[B](b: B)(f: (B, A) => B)(implicit ev: Monad[F]): F[B] = - this match { - case Cons(a, ft) => ft.flatMap(_.foldLeft(f(b, a))(f)) - case Wait(ft) => ft.flatMap(_.foldLeft(b)(f)) - case Empty() => ev.pure(b) - } - - /** - * Eagerly search the stream from the left. The search ends when f - * returns true for some a, or the stream is exhausted. Some(a) - * signals a match, None means no matching items were found. - */ - def find(f: A => Boolean)(implicit ev: Monad[F]): F[Option[A]] = - this match { - case Cons(a, ft) => - if (f(a)) ev.pure(Some(a)) else ft.flatMap(_.find(f)) - case Wait(ft) => - ft.flatMap(_.find(f)) - case Empty() => - ev.pure(None) - } - - /** - * Return true if the stream is empty, false otherwise. - * - * In this case of deferred streams this will force the first - * element to be calculated. - */ - def isEmpty(implicit ev: Monad[F]): F[Boolean] = - uncons.map(_.isEmpty) - - /** - * Return true if the stream is non-empty, false otherwise. - * - * In this case of deferred streams this will force the first - * element to be calculated. - */ - def nonEmpty(implicit ev: Monad[F]): F[Boolean] = - uncons.map(_.isDefined) - - /** - * Prepend an A value to the current stream. - */ - def %::(a: A)(implicit ev: Applicative[F]): StreamingT[F, A] = - Cons(a, ev.pure(this)) - - /** - * Prepend a StreamingT[F, A] value to the current stream. - */ - def %:::(lhs: StreamingT[F, A])(implicit ev: Functor[F]): StreamingT[F, A] = - lhs match { - case Cons(a, ft) => Cons(a, ft.map(_ %::: this)) - case Wait(ft) => Wait(ft.map(_ %::: this)) - case Empty() => this - } - - /** - * Concatenate streaming values within F[_]. - * - * This method is useful when calling .flatMap over a - * F[StreamingT[F, A]] value. - */ - def concat(rhs: F[StreamingT[F, A]])(implicit ev: Monad[F]): StreamingT[F, A] = - this match { - case Cons(a, ft) => Cons(a, ft.flatMap(_ fconcat rhs)) - case Wait(ft) => Wait(ft.flatMap(_ fconcat rhs)) - case Empty() => Wait(rhs) - } - - /** - * Concatenate streaming values within F[_]. - * - * This method is useful when calling .flatMap over a - * F[StreamingT[F, A]] value. - */ - def fconcat(rhs: F[StreamingT[F, A]])(implicit ev: Monad[F]): F[StreamingT[F, A]] = - this match { - case Cons(a, ft) => ev.pure(Cons(a, ft.flatMap(_ fconcat rhs))) - case Wait(ft) => ft.flatMap(_ fconcat rhs) - case Empty() => rhs - } - - /** - * Return true if some element of the stream satisfies the - * predicate, false otherwise. - */ - def exists(f: A => Boolean)(implicit ev: Monad[F]): F[Boolean] = - this match { - case Cons(a, ft) => if (f(a)) ev.pure(true) else ft.flatMap(_.exists(f)) - case Wait(ft) => ft.flatMap(_.exists(f)) - case Empty() => ev.pure(false) - } - - /** - * Return true if every element of the stream satisfies the - * predicate, false otherwise. - */ - def forall(f: A => Boolean)(implicit ev: Monad[F]): F[Boolean] = - this match { - case Cons(a, ft) => if (!f(a)) ev.pure(false) else ft.flatMap(_.forall(f)) - case Wait(ft) => ft.flatMap(_.forall(f)) - case Empty() => ev.pure(true) - } - - /** - * Return a stream consisting only of the first `n` elements of this - * stream. - * - * If the current stream has `n` or fewer elements, the entire - * stream will be returned. - */ - def take(n: Int)(implicit ev: Functor[F]): StreamingT[F, A] = - if (n <= 0) Empty() else this match { - case Cons(a, ft) => Cons(a, ft.map(_.take(n - 1))) - case Wait(ft) => Wait(ft.map(_.take(n))) - case Empty() => Empty() - } - - /** - * Return a stream consisting of all but the first `n` elements of - * this stream. - * - * If the current stream has `n` or fewer elements, an empty stream - * will be returned. - */ - def drop(n: Int)(implicit ev: Functor[F]): StreamingT[F, A] = - if (n <= 0) this else this match { - case Cons(a, ft) => Wait(ft.map(_.drop(n - 1))) - case Wait(ft) => Wait(ft.map(_.drop(n))) - case Empty() => Empty() - } - - /** - * From the beginning of this stream, create a new stream which - * takes only those elements that fulfill the predicate `f`. Once an - * element is found which does not fulfill the predicate, no further - * elements will be returned. - * - * If all elements satisfy `f`, the current stream will be returned. - * If no elements satisfy `f`, an empty stream will be returned. - * - * For example: - * {{{ - * scala> import cats.std.list._ - * scala> val s = StreamingT[List, Int](1, 2, 3, 4, 5, 6, 7) - * scala> s.takeWhile(n => n != 4).toList.flatten - * res0: List[Int] = List(1, 2, 3) - * }}} - */ - def takeWhile(f: A => Boolean)(implicit ev: Functor[F]): StreamingT[F, A] = - this match { - case Cons(a, ft) => if (f(a)) Cons(a, ft.map(_.takeWhile(f))) else Empty() - case Wait(ft) => Wait(ft.map(_.takeWhile(f))) - case Empty() => Empty() - } - - /** - * From the beginning of this stream, create a new stream which - * removes all elements that fulfill the predicate `f`. Once an - * element is found which does not fulfill the predicate, that - * element and all subsequent elements will be returned. - * - * If all elements satisfy `f`, an empty stream will be returned. - * If no elements satisfy `f`, the current stream will be returned. - * - * For example: - * {{{ - * scala> import cats.std.list._ - * scala> val s = StreamingT[List, Int](1, 2, 3, 4, 5, 6, 7) - * scala> s.dropWhile(n => n != 4).toList.flatten - * res0: List[Int] = List(4, 5, 6, 7) - * }}} - */ - def dropWhile(f: A => Boolean)(implicit ev: Functor[F]): StreamingT[F, A] = - this match { - case s @ Cons(a, ft) => if (f(a)) Wait(ft.map(_.dropWhile(f))) else s - case Wait(ft) => Wait(ft.map(_.dropWhile(f))) - case Empty() => Empty() - } - - /** - * Provide a list of elements in the stream. - * - * This will evaluate the stream immediately, and will hang in the - * case of infinite streams. - */ - def toList(implicit ev: Monad[F]): F[List[A]] = - this match { - case Cons(a, ft) => ft.flatMap(_.toList).map(a :: _) - case Wait(ft) => ft.flatMap(_.toList) - case Empty() => ev.pure(Nil) - } - - /** - * Basic string representation of a stream. - * - * This method will not force evaluation of any lazy part of a - * stream. As a result, you will see at most one element (the first - * one). - */ - override def toString: String = this match { - case Cons(a, _) => s"StreamingT($a, ...)" - case Wait(_) => "StreamingT(...)" - case Empty() => "StreamingT()" - } -} - -object StreamingT extends StreamingTInstances { - - /** - * Concrete StreamingT[A] types: - * - * - Empty(): an empty stream. - * - Cons(a, tail): a non-empty stream containing (at least) `a`. - * - Wait(tail): a deferred stream. - * - * Cons represents a lazy, possibly infinite stream of values. - * Eval[_] is used to represent possible laziness (via Now, Later, - * and Always). The head of `Cons` is eager -- a lazy head can be - * represented using `Wait(Always(...))` or `Wait(Later(...))`. - */ - private[cats] final case class Empty[F[_], A]() extends StreamingT[F, A] - private[cats] final case class Wait[F[_], A](next: F[StreamingT[F, A]]) extends StreamingT[F, A] - private[cats] final case class Cons[F[_], A](a: A, tail: F[StreamingT[F, A]]) extends StreamingT[F, A] - - /** - * Create an empty stream of type A. - */ - def empty[F[_], A]: StreamingT[F, A] = - Empty() - - /** - * Create a stream consisting of a single `A` value. - */ - def apply[F[_], A](a: A)(implicit ev: Applicative[F]): StreamingT[F, A] = - Cons(a, ev.pure(Empty())) - - /** - * Create a stream from two or more values. - */ - def apply[F[_], A](a1: A, a2: A, as: A*)(implicit ev: Applicative[F]): StreamingT[F, A] = - Cons(a1, ev.pure(Cons(a2, ev.pure(StreamingT.fromVector[F, A](as.toVector))))) - - /** - * Create a stream from a vector. - */ - def fromVector[F[_], A](as: Vector[A])(implicit ev: Applicative[F]): StreamingT[F, A] = { - def loop(s: StreamingT[F, A], i: Int): StreamingT[F, A] = - if (i < 0) s else loop(Cons(as(i), ev.pure(s)), i - 1) - loop(Empty(), as.length - 1) - } - - /** - * Create a stream from a list. - */ - def fromList[F[_], A](as: List[A])(implicit ev: Applicative[F]): StreamingT[F, A] = { - def loop(s: StreamingT[F, A], ras: List[A]): StreamingT[F, A] = - ras match { - case Nil => s - case a :: rt => loop(Cons(a, ev.pure(s)), rt) - } - loop(Empty(), as.reverse) - } - - /** - * Create a stream consisting of a single `F[A]`. - */ - def single[F[_]: Applicative, A](a: F[A]): StreamingT[F, A] = - Wait(a.map(apply(_))) - - /** - * Create a stream from `A` and `F[StreamingT[F, A]]` values. - */ - def cons[F[_], A](a: A, fs: F[StreamingT[F, A]]): StreamingT[F, A] = - Cons(a, fs) - - /** - * Create a stream from a deferred `StreamingT[F, A]` value. - * Note: the extent to which this defers the value depends on the `pureEval` - * implementation of the `Applicative[F]` instance. - */ - def defer[F[_], A](s: => StreamingT[F, A])(implicit ev: Applicative[F]): StreamingT[F, A] = - Wait(ev.pureEval(Always(s))) - - /** - * Create a stream from an `F[StreamingT[F, A]]` value. - */ - def wait[F[_], A](fs: F[StreamingT[F, A]]): StreamingT[F, A] = - Wait(fs) - - /** - * Produce a stream given an "unfolding" function. - * - * None represents an empty stream. Some(a) reprsents an initial - * element, and we can compute the tail (if any) via f(a). - */ - def unfold[F[_]: Functor, A](o: Option[A])(f: A => F[Option[A]]): StreamingT[F, A] = - o match { - case Some(a) => - Cons(a, f(a).map(o => unfold(o)(f))) - case None => - Empty() - } - - /** - * Contains syntax for F[Streaming[F, A]]. - * - * To eanble this, say: - * - * import cats.data.StreamingT.syntax._ - * - * This provides the %:: and %::: operators for prepending to an - * F[Streaming[F, A]] value, as well as a lazy Streaming[F, A] - * value. This mirrors the methods of the same name which can be - * used to prepend to a Streaming[F, A] value. - * - * In order to support laziness when using F[Streaming[F, A]] - * values, the type constructor F[_] must support laziness, and the - * F[Streaming[F, A]] value must be constructed lazily. - * - * For example, `StreamingT[Option, ?]` cannot support laziness, - * because Option[_] is eager. - * - * Additionally, `x %:: Future.successful(xs)` will not produce a - * lazy StreamT[Future, ?], since `xs` will already have been - * evaluated. - */ - object syntax { - implicit final class StreamingTOps[F[_], A](rhs: => StreamingT[F, A]) { - def %::(a: A)(implicit ev: Applicative[F]): StreamingT[F, A] = - Cons(a, ev.pureEval(Always(rhs))) - def %:::(s: StreamingT[F, A])(implicit ev: Monad[F]): StreamingT[F, A] = - s concat ev.pureEval(Always(rhs)) - def %::(fa: F[A])(implicit ev: Monad[F]): StreamingT[F, A] = - Wait(fa.map(a => Cons(a, ev.pureEval(Always(rhs))))) - def %:::(fs: F[StreamingT[F, A]])(implicit ev: Monad[F]): StreamingT[F, A] = - Wait(fs.map(_ concat ev.pureEval(Always(rhs)))) - } - - implicit final class FStreamingTOps[F[_], A](rhs: F[StreamingT[F, A]]) { - def %::(a: A): StreamingT[F, A] = - Cons(a, rhs) - def %:::(s: StreamingT[F, A])(implicit ev: Monad[F]): StreamingT[F, A] = - s concat rhs - def %::(fa: F[A])(implicit ev: Functor[F]): StreamingT[F, A] = - Wait(fa.map(a => Cons(a, rhs))) - def %:::(fs: F[StreamingT[F, A]])(implicit ev: Monad[F]): StreamingT[F, A] = - Wait(fs.map(_ concat rhs)) - } - } -} - -private[data] sealed trait StreamingTInstances extends StreamingTInstances1 { - - implicit def streamingTInstance[F[_]: Monad]: MonadCombine[StreamingT[F, ?]] with CoflatMap[StreamingT[F, ?]] = - new MonadCombine[StreamingT[F, ?]] with CoflatMap[StreamingT[F, ?]] { - def pure[A](a: A): StreamingT[F, A] = - StreamingT(a) - def flatMap[A, B](fa: StreamingT[F, A])(f: A => StreamingT[F, B]): StreamingT[F, B] = - fa.flatMap(f) - def empty[A]: StreamingT[F, A] = - StreamingT.empty - def combineK[A](xs: StreamingT[F, A], ys: StreamingT[F, A]): StreamingT[F, A] = - xs %::: ys - override def filter[A](fa: StreamingT[F, A])(f: A => Boolean): StreamingT[F, A] = - fa.filter(f) - def coflatMap[A, B](fa: StreamingT[F, A])(f: StreamingT[F, A] => B): StreamingT[F, B] = - fa.coflatMap(f) - - override def map[A, B](fa: StreamingT[F, A])(f: A => B): StreamingT[F, B] = - fa.map(f) - } - - implicit def streamingTOrder[F[_], A](implicit ev: Monad[F], eva: Order[F[List[A]]]): Order[StreamingT[F, A]] = - new Order[StreamingT[F, A]] { - def compare(x: StreamingT[F, A], y: StreamingT[F, A]): Int = - x.toList compare y.toList - } - - implicit def streamingTTransLift[M[_]: Applicative]: TransLift[StreamingT, M] = - new TransLift[StreamingT, M] { - def liftT[A](ma: M[A]): StreamingT[M, A] = StreamingT.single(ma) - } -} - -private[data] sealed trait StreamingTInstances1 extends StreamingTInstances2 { - implicit def streamingTPartialOrder[F[_], A](implicit ev: Monad[F], eva: PartialOrder[F[List[A]]]): PartialOrder[StreamingT[F, A]] = - new PartialOrder[StreamingT[F, A]] { - def partialCompare(x: StreamingT[F, A], y: StreamingT[F, A]): Double = - x.toList partialCompare y.toList - } -} - -private[data] sealed trait StreamingTInstances2 { - implicit def streamingTEq[F[_], A](implicit ev: Monad[F], eva: Eq[F[List[A]]]): Eq[StreamingT[F, A]] = - new Eq[StreamingT[F, A]] { - def eqv(x: StreamingT[F, A], y: StreamingT[F, A]): Boolean = - x.toList === y.toList - } -} diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index cb69e6964d..e9307b87f7 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -80,33 +80,6 @@ object arbitrary extends ArbitraryInstances0 { implicit def streamingArbitrary[A:Arbitrary]: Arbitrary[Streaming[A]] = Arbitrary(streamingGen[A](8)) - def emptyStreamingTGen[F[_], A]: Gen[StreamingT[F, A]] = - Gen.const(StreamingT.empty[F, A]) - - def streamingTGen[F[_], A](maxDepth: Int)(implicit F: Monad[F], A: Arbitrary[A]): Gen[StreamingT[F, A]] = { - if (maxDepth <= 1) - emptyStreamingTGen[F, A] - else Gen.frequency( - // Empty - 1 -> emptyStreamingTGen[F, A], - // Wait - 2 -> streamingTGen[F, A](maxDepth - 1).map(s => - StreamingT.wait(F.pure(s))), - // Cons - 6 -> (for { - a <- A.arbitrary - s <- streamingTGen[F, A](maxDepth - 1) - } yield StreamingT.cons(a, F.pure(s)))) - } - - // The max possible size of a StreamingT instance (n) will result in - // instances of up to n^3 in length when testing flatMap - // composition. The current value (8) could result in streams of up - // to 512 elements in length. Thus, since F may not be stack-safe, - // we want to keep n relatively small. - implicit def streamingTArbitrary[F[_], A](implicit F: Monad[F], A: Arbitrary[A]): Arbitrary[StreamingT[F, A]] = - Arbitrary(streamingTGen[F, A](8)) - implicit def writerArbitrary[L:Arbitrary, V:Arbitrary]: Arbitrary[Writer[L, V]] = writerTArbitrary[Id, L, V] diff --git a/tests/src/test/scala/cats/tests/StreamingTTests.scala b/tests/src/test/scala/cats/tests/StreamingTTests.scala deleted file mode 100644 index d36860d934..0000000000 --- a/tests/src/test/scala/cats/tests/StreamingTTests.scala +++ /dev/null @@ -1,250 +0,0 @@ -package cats -package tests - -import algebra.laws.OrderLaws - -import cats.data.{Streaming, StreamingT} -import cats.laws.discipline.{CartesianTests, CoflatMapTests, MonadCombineTests, SerializableTests} -import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq._ - -class StreamingTTests extends CatsSuite { - - { - implicit val iso = CartesianTests.Isomorphisms.invariant[StreamingT[Eval, ?]] - checkAll("StreamingT[Eval, ?]", MonadCombineTests[StreamingT[Eval, ?]].monadCombine[Int, Int, Int]) - checkAll("StreamingT[Eval, ?]", CoflatMapTests[StreamingT[Eval, ?]].coflatMap[Int, Int, Int]) - checkAll("StreamingT[Eval, Int]", OrderLaws[StreamingT[Eval, Int]].order) - checkAll("Monad[StreamingT[Eval, ?]]", SerializableTests.serializable(Monad[StreamingT[Eval, ?]])) - } - - { - implicit val iso = CartesianTests.Isomorphisms.invariant[StreamingT[Option, ?]] - checkAll("StreamingT[Option, ?]", MonadCombineTests[StreamingT[Option, ?]].monadCombine[Int, Int, Int]) - checkAll("StreamingT[Option, ?]", CoflatMapTests[StreamingT[Option, ?]].coflatMap[Int, Int, Int]) - checkAll("StreamingT[Option, Int]", OrderLaws[StreamingT[Option, Int]].order) - checkAll("Monad[StreamingT[Option, ?]]", SerializableTests.serializable(Monad[StreamingT[Option, ?]])) - } - - { - implicit val iso = CartesianTests.Isomorphisms.invariant[StreamingT[List, ?]] - checkAll("StreamingT[List, ?]", MonadCombineTests[StreamingT[List, ?]].monadCombine[Int, Int, Int]) - checkAll("StreamingT[List, ?]", CoflatMapTests[StreamingT[List, ?]].coflatMap[Int, Int, Int]) - checkAll("StreamingT[List, Int]", OrderLaws[StreamingT[List, Int]].order) - checkAll("Monad[StreamingT[List, ?]]", SerializableTests.serializable(Monad[StreamingT[List, ?]])) - } - - { - implicit val F = ListWrapper.monad - implicit val O = ListWrapper.partialOrder[List[Int]] - checkAll("StreamingT[ListWrapper, Int]", OrderLaws[StreamingT[ListWrapper, Int]].partialOrder) - checkAll("PartialOrder[StreamingT[ListWrapper, Int]]", SerializableTests.serializable(PartialOrder[StreamingT[ListWrapper, Int]])) - } - - { - implicit val F = ListWrapper.monad - implicit val E = ListWrapper.eqv[List[Int]] - checkAll("StreamingT[ListWrapper, Int]", OrderLaws[StreamingT[ListWrapper, Int]].eqv) - checkAll("Eq[StreamingT[ListWrapper, Int]]", SerializableTests.serializable(Eq[StreamingT[ListWrapper, Int]])) - } - - - test("uncons with Id consistent with List headOption/tail") { - forAll { (s: StreamingT[Id, Int]) => - val sList = s.toList - s.uncons.map{ case (h, t) => - (h, t.toList) - } should === (sList.headOption.map{ h => - (h, sList.tail) - }) - } - } - - test("map with Id consistent with List.map") { - forAll { (s: StreamingT[Id, Int], f: Int => Long) => - s.map(f).toList should === (s.toList.map(f)) - } - } - - test("flatMap with Id consistent with List.flatMap") { - forAll { (s: StreamingT[Id, Int], f: Int => StreamingT[Id, Long]) => - s.flatMap(f).toList should === (s.toList.flatMap(f(_).toList)) - } - } - - test("filter with Id consistent with List.filter") { - forAll { (s: StreamingT[Id, Int], f: Int => Boolean) => - s.filter(f).toList should === (s.toList.filter(f)) - } - } - - test("filter - check regression") { - val s = StreamingT[Option, Int](1, 2, 1) - s.filter(_ > 1).toList should === (Some(List(2))) - } - - test("foldLeft with Id consistent with List.foldLeft") { - forAll { (s: StreamingT[Id, Int], l: Long, f: (Long, Int) => Long) => - s.foldLeft(l)(f) should === (s.toList.foldLeft(l)(f)) - } - } - - test("find with Id consistent with List.find") { - forAll { (s: StreamingT[Id, Int], f: Int => Boolean) => - s.find(f) should === (s.toList.find(f)) - } - } - - test("isEmpty with Id consistent with List.isEmpty") { - forAll { (s: StreamingT[Id, Int]) => - s.isEmpty should === (s.toList.isEmpty) - } - } - - test("nonEmpty with Id consistent with List.nonEmpty") { - forAll { (s: StreamingT[Id, Int]) => - s.nonEmpty should === (s.toList.nonEmpty) - } - } - - test("%:: with Id consistent with List.::") { - forAll { (i: Int, s: StreamingT[Id, Int]) => - (i %:: s).toList should === (i :: s.toList) - } - } - - test("%::: with Id consistent with List.:::") { - forAll { (s1: StreamingT[Id, Int], s2: StreamingT[Id, Int]) => - (s1 %::: s2).toList should === (s1.toList ::: s2.toList) - } - } - - test("concat with Id consistent with List.++") { - forAll { (s1: StreamingT[Id, Int], s2: StreamingT[Id, Int]) => - (s1 concat s2).toList should === (s1.toList ++ s2.toList) - } - } - - test("exists with Id consistent with List.exists") { - forAll { (s: StreamingT[Id, Int], f: Int => Boolean) => - s.exists(f) should === (s.toList.exists(f)) - } - } - - test("forall with Id consistent with List.forall") { - forAll { (s: StreamingT[Id, Int], f: Int => Boolean) => - s.forall(f) should === (s.toList.forall(f)) - } - } - - test("takeWhile with Id consistent with List.takeWhile") { - forAll { (s: StreamingT[Id, Int], f: Int => Boolean) => - s.takeWhile(f).toList should === (s.toList.takeWhile(f)) - } - } - - test("dropWhile with Id consistent with List.dropWhile") { - forAll { (s: StreamingT[Id, Int], f: Int => Boolean) => - s.dropWhile(f).toList should === (s.toList.dropWhile(f)) - } - } - - test("take with Id consistent with List.take") { - forAll { (s: StreamingT[Id, Int], i: Int) => - s.take(i).toList should === (s.toList.take(i)) - } - } - - test("drop with Id consistent with List.drop") { - forAll { (s: StreamingT[Id, Int], i: Int) => - s.drop(i).toList should === (s.toList.drop(i)) - } - } - - test("unfold with Id consistent with Streaming.unfold") { - forAll { (o: Option[Long]) => - val f: Long => Option[Long] = { x => - val rng = new scala.util.Random(x) - if (rng.nextBoolean) Some(rng.nextLong) - else None - } - - StreamingT.unfold[Id, Long](o)(f).toList should === (Streaming.unfold(o)(f).toList) - } - } - - test("defer produces the same values") { - forAll { (xs: StreamingT[Option, Int]) => - StreamingT.defer(xs) should === (xs) - } - } - - test("defer isn't eager if the pureEval impl isn't") { - def s: StreamingT[Eval, Int] = throw new RuntimeException("blargh") - val x = StreamingT.defer[Eval, Int](s) - } - - test("fromVector") { - forAll { (xs: Vector[Int]) => - StreamingT.fromVector[Id, Int](xs).toList.toVector should === (xs) - } - } - - test("fromList") { - forAll { (xs: List[Int]) => - StreamingT.fromList[Id, Int](xs).toList should === (xs) - } - } - - test("single consistent with apply") { - forAll { (i: Int) => - StreamingT[Id, Int](i) should === (StreamingT.single[Id, Int](i)) - } - } - - test("var-arg apply") { - forAll { (x1: Int, x2: Int, x3: Int, x4: Int) => - val fromList = StreamingT.fromList[Id, Int](x1 :: x2 :: x3 :: x4 :: Nil) - StreamingT[Id, Int](x1, x2, x3, x4) should === (fromList) - } - - forAll { (x1: Int, x2: Int, tail: List[Int]) => - val fromList = StreamingT.fromList[Id, Int](x1 :: x2 :: tail) - StreamingT[Id, Int](x1, x2, tail: _*) should === (fromList) - } - } - - test("toString is wrapped in StreamingT()"){ - forAll { (xs: StreamingT[Option, Int]) => - val s = xs.toString - s.take(11) should === ("StreamingT(") - s.last should === (')') - } - } -} - -class SpecificStreamingTTests extends CatsSuite { - - type S[A] = StreamingT[List, A] - - def cons[A](a: A, fs: List[S[A]]): S[A] = StreamingT.cons(a, fs) - def wait[A](fs: List[S[A]]): S[A] = StreamingT.wait(fs) - def empty[A]: S[A] = StreamingT.empty[List, A] - - test("counter-example #1"){ - val fa: S[Boolean] = - cons(true, List(cons(true, List(empty)), empty)) - - def f(b: Boolean): S[Boolean] = - if (b) cons(false, List(cons(true, List(empty)))) - else empty - - def g(b: Boolean): S[Boolean] = - if (b) empty - else cons(true, List(cons(false, List(empty)), cons(true, List(empty)))) - - val x = fa.flatMap(f).flatMap(g) - val y = fa.flatMap(a => f(a).flatMap(g)) - x should === (y) - } -} diff --git a/tests/src/test/scala/cats/tests/TransLiftTests.scala b/tests/src/test/scala/cats/tests/TransLiftTests.scala index 6b17eea83b..1661941745 100644 --- a/tests/src/test/scala/cats/tests/TransLiftTests.scala +++ b/tests/src/test/scala/cats/tests/TransLiftTests.scala @@ -1,7 +1,7 @@ package cats package tests -import data.{OptionT,XorT,WriterT,StreamingT, Kleisli, StateT} +import data.{OptionT,XorT,WriterT,Kleisli, StateT} class TransLiftTests extends CatsSuite { @@ -29,10 +29,7 @@ class TransLiftTests extends CatsSuite { } - test("transLift for StreamingT, StateT require Applicative Functor") { - import StreamingT._ - - val b: StreamingT[JustAp, Int] = JustAp(1).liftT[StreamingT] + test("transLift for StateT requires Applicative Functor") { val f: StateT[JustAp, Int, Int] = JustAp(1).liftT[({type λ[α[_], β] = StateT[α, Int, β]})#λ] } From 32822090303c2a8e1aee87aeccaeab9f7403ac1a Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Thu, 3 Mar 2016 06:47:15 -0500 Subject: [PATCH 015/185] Remove Streaming This is based on #858 and a discussion at dinner after the first day of the typelevel summit. Per the discussion, Streaming will be moved to the dogs project. StreamingT will be removed for now, and we can consider adding it to dogs in the future if people find themselves wanting it. --- core/src/main/scala/cats/Foldable.scala | 7 - core/src/main/scala/cats/data/Streaming.scala | 938 ------------------ core/src/main/scala/cats/std/list.scala | 4 - core/src/main/scala/cats/std/vector.scala | 4 - core/src/main/scala/cats/syntax/all.scala | 1 - .../main/scala/cats/syntax/monadCombine.scala | 4 +- core/src/main/scala/cats/syntax/package.scala | 1 - .../main/scala/cats/syntax/streaming.scala | 32 - docs/src/main/tut/streaming.md | 432 -------- .../cats/laws/discipline/Arbitrary.scala | 21 - .../test/scala/cats/tests/FoldableTests.scala | 10 - .../scala/cats/tests/StreamingTests.scala | 438 -------- 12 files changed, 2 insertions(+), 1890 deletions(-) delete mode 100644 core/src/main/scala/cats/data/Streaming.scala delete mode 100644 core/src/main/scala/cats/syntax/streaming.scala delete mode 100644 docs/src/main/tut/streaming.md delete mode 100644 tests/src/test/scala/cats/tests/StreamingTests.scala diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index 63bc0a0d33..676c80f5ac 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -1,7 +1,5 @@ package cats -import cats.data.Streaming - import scala.collection.mutable import simulacrum.typeclass @@ -274,11 +272,6 @@ import simulacrum.typeclass val F = self val G = ev } - - def toStreaming[A](fa: F[A]): Streaming[A] = - foldRight(fa, Now(Streaming.empty[A])){ (a, ls) => - Now(Streaming.cons(a, ls)) - }.value } /** diff --git a/core/src/main/scala/cats/data/Streaming.scala b/core/src/main/scala/cats/data/Streaming.scala deleted file mode 100644 index bb61a2e5a3..0000000000 --- a/core/src/main/scala/cats/data/Streaming.scala +++ /dev/null @@ -1,938 +0,0 @@ -package cats -package data - -import cats.syntax.all._ - -import scala.reflect.ClassTag -import scala.annotation.tailrec -import scala.collection.mutable - -/** - * `Streaming[A]` represents a stream of values. A stream can be - * thought of as a collection, with two key differences: - * - * 1. It may be infinite; it does not necessarily have a finite - * length. For this reason, there is no `.length` method. - * - * 2. It may be lazy. In other words, the entire stream may not be in - * memory. In this case, each "step" of the stream has - * instructions for producing the next step. - * - * Streams are not necessarily lazy: they use `Eval[Streaming[A]]` to - * represent a tail that may (or may not be) lazy. If `Now[A]` is used - * for each tail, then `Streaming[A]` will behave similarly to - * `List[A]`. If `Later[A]` is used for each tail, then `Streaming[A]` - * will behave similarly to `scala.Stream[A]` (i.e. it will - * lazily-compute the tail, and will memoize the result to improve the - * performance of repeated traversals). If `Always[A]` is used for - * each tail, the result will be a lazy stream which does not memoize - * results (saving space at the cost of potentially-repeated - * calculations). - * - * Since `Streaming[A]` has been compared to `scala.Stream[A]` it is - * worth noting some key differences between the two types: - * - * 1. When the entire stream is known ahead of time, `Streaming[A]` - * can represent it more efficiencly, using `Now[A]`, rather than - * allocating a list of closures. - * - * 2. `Streaming[A]` does not memoize by default. This protects - * against cases where a reference to head will prevent the entire - * stream from being garbage collected, and is a better default. - * A stream can be memoized later using the `.memoize` method. - * - * 3. `Streaming[A]` does not inherit from the standard collections, - * meaning a wide variety of methods which are dangerous on - * streams (`.length`, `.apply`, etc.) are not present. - * - * 4. `scala.Stream[A]` requires an immediate value for `.head`. This - * means that operations like `.filter` will block until a - * matching value is found, or the stream is exhausted (which - * could be never in the case of an infinite stream). By contrast, - * `Streaming[A]` values can be totally lazy (and can be - * lazily-constructed using `Streaming.defer()`), so methods like - * `.filter` are completely lazy. - * - * 5. The use of `Eval[Streaming[A]]` to represent the "tail" of the - * stream means that streams can be lazily (and safely) - * constructed with `Foldable#foldRight`, and that `.map` and - * `.flatMap` operations over the tail will be safely trampolined. - */ -sealed abstract class Streaming[A] extends Product with Serializable { lhs => - - import Streaming.{Empty, Wait, Cons} - - /** - * The stream's catamorphism. - * - * This method allows the stream to be transformed into an arbitrary - * value by handling two cases: - * - * 1. empty stream: return b - * 2. non-empty stream: apply the function to the head and tail - * - * This method can be more convenient than pattern-matching, since - * it includes support for handling deferred streams (i.e. Wait(_)), - * these nodes will be evaluated until an empty or non-empty stream - * is found (i.e. until Empty() or Cons() is found). - */ - def fold[B](b: Eval[B], f: (A, Eval[Streaming[A]]) => B): B = { - @tailrec def unroll(s: Streaming[A]): B = - s match { - case Empty() => b.value - case Wait(lt) => unroll(lt.value) - case Cons(a, lt) => f(a, lt) - } - unroll(this) - } - - /** - * A variant of fold, used for constructing streams. - * - * The only difference is that foldStreaming will preserve deferred - * streams. This makes it more appropriate to use in situations - * where the stream's laziness must be preserved. - */ - def foldStreaming[B](bs: => Streaming[B], f: (A, Eval[Streaming[A]]) => Streaming[B]): Streaming[B] = - this match { - case Empty() => bs - case Wait(lt) => Wait(lt.map(_.foldStreaming(bs, f))) - case Cons(a, lt) => f(a, lt) - } - - /** - * Deconstruct a stream into a head and tail (if available). - * - * This method will evaluate the stream until it finds a head and - * tail, or until the stream is exhausted. The head will be - * evaluated, whereas the tail will remain (potentially) lazy within - * Eval. - */ - def uncons: Option[(A, Eval[Streaming[A]])] = { - @tailrec def unroll(s: Streaming[A]): Option[(A, Eval[Streaming[A]])] = - s match { - case Empty() => None - case Wait(lt) => unroll(lt.value) - case Cons(a, lt) => Some((a, lt)) - } - unroll(this) - } - - /** - * Lazily transform the stream given a function `f`. - */ - def map[B](f: A => B): Streaming[B] = - this match { - case Empty() => Empty() - case Wait(lt) => Wait(lt.map(_.map(f))) - case Cons(a, lt) => Cons(f(a), lt.map(_.map(f))) - } - - /** - * Lazily transform the stream given a function `f`. - */ - def flatMap[B](f: A => Streaming[B]): Streaming[B] = - this match { - case Empty() => Empty() - case Wait(lt) => Wait(lt.map(_.flatMap(f))) - case Cons(a, lt) => f(a) ++ lt.map(_.flatMap(f)) - } - - /** - * Lazily filter the stream given the predicate `f`. - */ - def filter(f: A => Boolean): Streaming[A] = - this match { - case Empty() => - this - case Wait(lt) => - Wait(lt.map(_.filter(f))) - case Cons(a, lt) => - val ft = lt.map(_.filter(f)) - if (f(a)) Cons(a, ft) else Wait(ft) - } - - /** - * Eagerly fold the stream to a single value from the left. - */ - def foldLeft[B](b: B)(f: (B, A) => B): B = { - @tailrec def unroll(s: Streaming[A], b: B): B = - s match { - case Empty() => b - case Wait(lt) => unroll(lt.value, b) - case Cons(a, lt) => unroll(lt.value, f(b, a)) - } - unroll(this, b) - } - - /** - * Lazily fold the stream to a single value from the right. - */ - def foldRight[B](b: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - this match { - case Empty() => b - case Wait(lt) => lt.flatMap(_.foldRight(b)(f)) - case Cons(a, lt) => f(a, lt.flatMap(_.foldRight(b)(f))) - } - - /** - * Eagerly search the stream from the left. The search ends when f - * returns true for some a, or the stream is exhausted. Some(a) - * signals a match, None means no matching items were found. - */ - def find(f: A => Boolean): Option[A] = { - @tailrec def loop(s: Streaming[A]): Option[A] = - s match { - case Cons(a, lt) => if (f(a)) Some(a) else loop(lt.value) - case Wait(lt) => loop(lt.value) - case Empty() => None - } - loop(this) - } - - /** - * Return true if the stream is empty, false otherwise. - * - * In this case of deferred streams this will force the first - * element to be calculated. - */ - def isEmpty: Boolean = { - @tailrec def unroll(s: Streaming[A]): Boolean = - s match { - case Cons(_, _) => false - case Empty() => true - case Wait(lt) => unroll(lt.value) - } - unroll(this) - } - - /** - * Return true if the stream is non-empty, false otherwise. - * - * In this case of deferred streams this will force the first - * element to be calculated. - */ - def nonEmpty: Boolean = - !isEmpty - - /** - * Peek at the start of the stream to see whether we know if it is - * empty. - * - * Unlike .isEmpty/.nonEmpty, this method will not force the - * calculationg of a deferred stream. Instead, None will be - * returned. - */ - def peekEmpty: Option[Boolean] = - this match { - case Empty() => Some(true) - case Wait(_) => None - case Cons(a, lt) => Some(false) - } - - /** - * Lazily concatenate two streams. - */ - def ++(rhs: Streaming[A]): Streaming[A] = - this match { - case Empty() => rhs - case Wait(lt) => Wait(lt.map(_ ++ rhs)) - case Cons(a, lt) => Cons(a, lt.map(_ ++ rhs)) - } - - /** - * Lazily concatenate two streams. - * - * In this case the evaluation of the second stream may be deferred. - */ - def ++(rhs: Eval[Streaming[A]]): Streaming[A] = - this match { - case Empty() => Wait(rhs) - case Wait(lt) => Wait(lt.map(_ ++ rhs)) - case Cons(a, lt) => Cons(a, lt.map(_ ++ rhs)) - } - - /** - * Lazily zip two streams together. - * - * The length of the result will be the shorter of the two - * arguments. - */ - def zip[B](rhs: Streaming[B]): Streaming[(A, B)] = - (lhs zipMap rhs)((a, b) => (a, b)) - - /** - * Lazily zip two streams together, using the given function `f` to - * produce output values. - * - * The length of the result will be the shorter of the two - * arguments. - * - * The expression: - * - * (lhs zipMap rhs)(f) - * - * is equivalent to (but more efficient than): - * - * (lhs zip rhs).map { case (a, b) => f(a, b) } - */ - def zipMap[B, C](rhs: Streaming[B])(f: (A, B) => C): Streaming[C] = - (lhs, rhs) match { - case (Cons(a, lta), Cons(b, ltb)) => - Cons(f(a, b), for { ta <- lta; tb <- ltb } yield (ta zipMap tb)(f)) - case (Empty(), _) => - Empty() - case (_, Empty()) => - Empty() - case (Wait(lta), s) => - Wait(lta.map(_.zipMap(s)(f))) - case (s, Wait(ltb)) => - Wait(ltb.map(s.zipMap(_)(f))) - } - - /** - * Lazily zip two streams together using Ior. - * - * Unlike `zip`, the length of the result will be the longer of the - * two arguments. - */ - def izip[B](rhs: Streaming[B]): Streaming[Ior[A, B]] = - izipMap(rhs)(Ior.both, Ior.left, Ior.right) - - /** - * Zip two streams together, using the given function `f` to produce - * the output values. - * - * Unlike zipMap, the length of the result will be the *longer* of - * the two input streams. The functions `g` and `h` will be used in - * this case to produce valid `C` values. - * - * The expression: - * - * (lhs izipMap rhs)(f, g, h) - * - * is equivalent to (but more efficient than): - * - * (lhs izip rhs).map { - * case Ior.Both(a, b) => f(a, b) - * case Ior.Left(a) => g(a) - * case Ior.Right(b) => h(b) - * } - */ - def izipMap[B, C](rhs: Streaming[B])(f: (A, B) => C, g: A => C, h: B => C): Streaming[C] = - (lhs, rhs) match { - case (Cons(a, lta), Cons(b, ltb)) => - Cons(f(a, b), for { ta <- lta; tb <- ltb } yield (ta izipMap tb)(f, g, h)) - case (Wait(lta), tb) => - Wait(lta.map(_.izipMap(tb)(f, g, h))) - case (ta, Wait(ltb)) => - Wait(ltb.map(ta.izipMap(_)(f, g, h))) - case (Empty(), tb) => - tb.map(h) - case (ta, Empty()) => - ta.map(g) - } - - /** - * Zip the items of the stream with their position. - * - * Indices start at 0, so - * - * Streaming('x, 'y, 'z).zipWithIndex - * - * lazily produces: - * - * Streaming(('x, 0), ('y, 1), ('z, 2)) - */ - def zipWithIndex: Streaming[(A, Int)] = { - def loop(s: Streaming[A], i: Int): Streaming[(A, Int)] = - s match { - case Empty() => Empty() - case Wait(lt) => Wait(lt.map(s => loop(s, i))) - case Cons(a, lt) => Cons((a, i), lt.map(s => loop(s, i + 1))) - } - loop(this, 0) - } - - /** - * Unzip this stream of tuples into two distinct streams. - */ - def unzip[B, C](implicit ev: A =:= (B, C)): (Streaming[B], Streaming[C]) = - (this.map(_._1), this.map(_._2)) - - /** - * Merge two sorted streams into a new stream. - * - * The streams are assumed to already be sorted. If they are not, - * the resulting order is not defined. - */ - def merge(rhs: Streaming[A])(implicit ev: Order[A]): Streaming[A] = - (lhs, rhs) match { - case (Cons(a0, lt0), Cons(a1, lt1)) => - if (a0 < a1) Cons(a0, lt0.map(_ merge rhs)) else Cons(a1, lt1.map(lhs merge _)) - case (Wait(lta), s) => - Wait(lta.map(_ merge s)) - case (s, Wait(ltb)) => - Wait(ltb.map(s merge _)) - case (s, Empty()) => - s - case (Empty(), s) => - s - } - - /** - * Interleave the elements of two streams. - * - * Given x = [x0, x1, x2, ...] and y = [y0, y1, y2, ...] this method - * will return the stream [x0, y0, x1, y1, x2, ...] - * - * If one stream is longer than the other, the rest of its elements - * will appear after the other stream is exhausted. - */ - def interleave(rhs: Streaming[A]): Streaming[A] = - lhs match { - case Cons(a, lt) => Cons(a, lt.map(rhs interleave _)) - case Wait(lt) => Wait(lt.map(_ interleave rhs)) - case Empty() => rhs - } - - /** - * Produce the Cartestian product of two streams. - * - * Given x = [x0, x1, x2, ...] and y = [y0, y1, y2, ...] this method - * will return the stream: - * - * [(x0, y0), (x0, y1), (x1, y0), (x0, y2), (x1, y1), (x2, y0), ...] - * - * This is the diagonalized product of both streams. Every possible - * combination will (eventually) be reached. - * - * This is true even for infinite streams, at least in theory -- - * time and space limitations of evaluating an infinite stream may - * make it impossible to reach very distant elements. - * - * This method lazily evaluates the input streams, but due to the - * diagonalization method may read ahead more than is strictly - * necessary. - */ - def product[B](rhs: Streaming[B]): Streaming[(A, B)] = { - def loop(i: Int): Streaming[(A, B)] = { - val xs = lhs.take(i + 1).asInstanceOf[Streaming[AnyRef]].toArray - val ys = rhs.take(i + 1).asInstanceOf[Streaming[AnyRef]].toArray - def build(j: Int): Streaming[(A, B)] = - if (j > i) Empty() else { - val k = i - j - if (j >= xs.length || k >= ys.length) build(j + 1) else { - val tpl = (xs(j).asInstanceOf[A], ys(k).asInstanceOf[B]) - Cons(tpl, Always(build(j + 1))) - } - } - if (i > xs.length + ys.length - 2) Empty() else { - build(0) ++ Always(loop(i + 1)) - } - } - Wait(Always(loop(0))) - } - - /** - * Return true if some element of the stream satisfies the - * predicate, false otherwise. - */ - def exists(f: A => Boolean): Boolean = { - @tailrec def unroll(s: Streaming[A]): Boolean = - s match { - case Empty() => false - case Wait(lt) => unroll(lt.value) - case Cons(a, lt) => if (f(a)) true else unroll(lt.value) - } - unroll(this) - } - - /** - * Return true if every element of the stream satisfies the - * predicate, false otherwise. - */ - def forall(f: A => Boolean): Boolean = { - @tailrec def unroll(s: Streaming[A]): Boolean = - s match { - case Empty() => true - case Wait(lt) => unroll(lt.value) - case Cons(a, lt) => if (f(a)) unroll(lt.value) else false - } - unroll(this) - } - - /** - * Return a stream consisting only of the first `n` elements of this - * stream. - * - * If the current stream has `n` or fewer elements, the entire - * stream will be returned. - */ - def take(n: Int): Streaming[A] = - if (n <= 0) Empty() else this match { - case Empty() => Empty() - case Wait(lt) => Wait(lt.map(_.take(n))) - case Cons(a, lt) => - Cons(a, if (n == 1) Now(Empty()) else lt.map(_.take(n - 1))) - } - - /** - * Return a stream consisting of all but the first `n` elements of - * this stream. - * - * If the current stream has `n` or fewer elements, an empty stream - * will be returned. - */ - def drop(n: Int): Streaming[A] = - if (n <= 0) this else this match { - case Empty() => Empty() - case Wait(lt) => Wait(lt.map(_.drop(n))) - case Cons(a, lt) => Wait(lt.map(_.drop(n - 1))) - } - - /** - * From the beginning of this stream, create a new stream which - * takes only those elements that fulfill the predicate `f`. Once an - * element is found which does not fulfill the predicate, no further - * elements will be returned. - * - * If all elements satisfy `f`, the current stream will be returned. - * If no elements satisfy `f`, an empty stream will be returned. - * - * For example: - * {{{ - * scala> val s = Streaming(1, 2, 3, 4, 5, 6, 7) - * scala> s.takeWhile(n => n != 4).toList - * res0: List[Int] = List(1, 2, 3) - * }}} - */ - def takeWhile(f: A => Boolean): Streaming[A] = - this match { - case Empty() => Empty() - case Wait(lt) => Wait(lt.map(_.takeWhile(f))) - case Cons(a, lt) => if (f(a)) Cons(a, lt.map(_.takeWhile(f))) else Empty() - } - - /** - * From the beginning of this stream, create a new stream which - * removes all elements that fulfill the predicate `f`. Once an - * element is found which does not fulfill the predicate, that - * element and all subsequent elements will be returned. - * - * If all elements satisfy `f`, an empty stream will be returned. - * If no elements satisfy `f`, the current stream will be returned. - * - * For example: - * {{{ - * scala> val s = Streaming(1, 2, 3, 4, 5, 6, 7) - * scala> s.dropWhile(n => n != 4).toList - * res0: List[Int] = List(4, 5, 6, 7) - * }}} - */ - def dropWhile(f: A => Boolean): Streaming[A] = - this match { - case Empty() => Empty() - case Wait(lt) => Wait(lt.map(_.dropWhile(f))) - case s @ Cons(a, lt) => if (f(a)) Wait(lt.map(_.dropWhile(f))) else s - } - - /** - * Provide a stream of all the tails of a stream (including itself). - * - * For example, Streaming(1, 2).tails is equivalent to: - * - * Streaming(Streaming(1, 2), Streaming(1), Streaming.empty) - */ - def tails: Streaming[Streaming[A]] = - this match { - case Cons(a, lt) => Cons(this, lt.map(_.tails)) - case Wait(lt) => Wait(lt.map(_.tails)) - case Empty() => Cons(this, Always(Streaming.empty)) - } - - /** - * Provide an iterator over the elements in the stream. - */ - def iterator: Iterator[A] = - new Iterator[A] { - var ls: Eval[Streaming[A]] = null - var s: Streaming[A] = lhs - - def hasNext: Boolean = - { if (s == null) { s = ls.value; ls = null }; s.nonEmpty } - - val emptyCase: Eval[A] = - Always(throw new NoSuchElementException("next on empty iterator")) - val consCase: (A, Eval[Streaming[A]]) => A = - (a, lt) => { ls = lt; s = null; a } - - def next: A = { - if (s == null) s = ls.value - s.fold(emptyCase, consCase) - } - } - - /** - * Provide a list of elements in the stream. - * - * This will evaluate the stream immediately, and will hang in the - * case of infinite streams. - */ - def toList: List[A] = { - @tailrec def unroll(buf: mutable.ListBuffer[A], s: Streaming[A]): List[A] = - s match { - case Empty() => buf.toList - case Wait(lt) => unroll(buf, lt.value) - case Cons(a, lt) => unroll(buf += a, lt.value) - } - unroll(mutable.ListBuffer.empty[A], this) - } - - /** - * Basic string representation of a stream. - * - * This method will not force evaluation of any lazy part of a - * stream. As a result, you will see at most one element (the first - * one). - * - * Use .toString(n) to see the first n elements of the stream. - */ - override def toString: String = - this match { - case Cons(a, _) => "Streaming(" + a + ", ...)" - case Empty() => "Streaming()" - case Wait(_) => "Streaming(...)" - } - - /** - * String representation of the first n elements of a stream. - */ - def toString(limit: Int = 10): String = { - @tailrec def unroll(n: Int, sb: StringBuffer, s: Streaming[A]): String = - if (n <= 0) sb.append(", ...)").toString else s match { - case Empty() => sb.append(")").toString - case Wait(lt) => unroll(n, sb, lt.value) - case Cons(a, lt) => unroll(n - 1, sb.append(", " + a.toString), lt.value) - } - uncons match { - case None => - "Streaming()" - case Some((a, lt)) => - val sb = new StringBuffer().append("Streaming(" + a.toString) - unroll(limit - 1, sb, lt.value) - } - } - - /** - * Provide an array of elements in the stream. - * - * This will evaluate the stream immediately, and will hang in the - * case of infinite streams. - */ - def toArray(implicit ct: ClassTag[A]): Array[A] = { - @tailrec def unroll(buf: mutable.ArrayBuffer[A], s: Streaming[A]): Array[A] = - s match { - case Empty() => buf.toArray - case Wait(lt) => unroll(buf, lt.value) - case Cons(a, lt) => unroll(buf += a, lt.value) - } - unroll(mutable.ArrayBuffer.empty[A], this) - } - - /** - * Ensure that repeated traversals of the stream will not cause - * repeated tail computations. - * - * By default this structure does not memoize to avoid memory leaks - * when the head of the stream is retained. However, the user - * ultimately has control of the memoization approach based on what - * kinds of Eval instances they use. - * - * There are two calls to .memoize here -- one is a recursive call - * to this method (on the tail) and the other is a call to memoize - * the Eval instance holding the tail. For more information on how - * this works see [[cats.Eval.memoize]]. - */ - def memoize: Streaming[A] = - this match { - case Empty() => Empty() - case Wait(lt) => Wait(lt.map(_.memoize).memoize) - case Cons(a, lt) => Cons(a, lt.map(_.memoize).memoize) - } - - /** - * Compact removes "pauses" in the stream (represented as Wait(_) - * nodes). - * - * Normally, Wait(_) values are used to defer tail computation in - * cases where it is convenient to return a stream value where - * neither the head or tail are computed yet. - * - * In some cases (particularly if the stream is to be memoized) it - * may be desirable to ensure that these values are not retained. - */ - def compact: Streaming[A] = { - @tailrec def unroll(s: Streaming[A]): Streaming[A] = - s match { - case Cons(a, lt) => Cons(a, lt.map(_.compact)) - case Wait(lt) => unroll(lt.value) - case Empty() => Empty() - } - unroll(this) - } -} - -object Streaming extends StreamingInstances { - - /** - * Concrete Streaming[A] types: - * - * - Empty(): an empty stream. - * - Cons(a, tail): a non-empty stream containing (at least) `a`. - * - Wait(tail): a deferred stream. - * - * Cons represents a lazy, possibly infinite stream of values. - * Eval[_] is used to represent possible laziness (via Now, Later, - * and Always). The head of `Cons` is eager -- a lazy head can be - * represented using `Wait(Always(...))` or `Wait(Later(...))`. - */ - final case class Empty[A]() extends Streaming[A] - final case class Wait[A](next: Eval[Streaming[A]]) extends Streaming[A] - final case class Cons[A](a: A, tail: Eval[Streaming[A]]) extends Streaming[A] - - /** - * Create an empty stream of type A. - */ - def empty[A]: Streaming[A] = - Empty() - - /** - * Create a stream consisting of a single value. - */ - def apply[A](a: A): Streaming[A] = - Cons(a, Now(Empty())) - - /** - * Prepend a value to a stream. - */ - def cons[A](a: A, s: Streaming[A]): Streaming[A] = - Cons(a, Now(s)) - - /** - * Prepend a value to an Eval[Streaming[A]]. - */ - def cons[A](a: A, ls: Eval[Streaming[A]]): Streaming[A] = - Cons(a, ls) - - /** - * Create a stream from two or more values. - */ - def apply[A](a1: A, a2: A, as: A*): Streaming[A] = - cons(a1, cons(a2, fromVector(as.toVector))) - - /** - * Defer stream creation. - * - * Given an expression which creates a stream, this method defers - * that creation, allowing the head (if any) to be lazy. - */ - def defer[A](s: => Streaming[A]): Streaming[A] = - wait(Always(s)) - - /** - * Create a stream from an `Eval[Streaming[A]]` value. - * - * Given an expression which creates a stream, this method defers - * that creation, allowing the head (if any) to be lazy. - */ - def wait[A](ls: Eval[Streaming[A]]): Streaming[A] = - Wait(ls) - - /** - * Create a stream from a vector. - * - * The stream will be eagerly evaluated. - */ - def fromVector[A](as: Vector[A]): Streaming[A] = { - def loop(s: Streaming[A], i: Int): Streaming[A] = - if (i < 0) s else loop(Cons(as(i), Now(s)), i - 1) - loop(Empty(), as.length - 1) - } - - /** - * Create a stream from a list. - * - * The stream will be eagerly evaluated. - */ - def fromList[A](as: List[A]): Streaming[A] = { - def loop(s: Streaming[A], ras: List[A]): Streaming[A] = - ras match { - case Nil => s - case a :: rt => loop(Cons(a, Now(s)), rt) - } - loop(Empty(), as.reverse) - } - - def fromFoldable[F[_], A](fa: F[A])(implicit F: Foldable[F]): Streaming[A] = - F.toStreaming(fa) - - /** - * Create a stream from an iterable. - * - * The stream will be eagerly evaluated. - */ - def fromIterable[A](as: Iterable[A]): Streaming[A] = - fromIteratorUnsafe(as.iterator) - - /** - * Create a stream from an iterator. - * - * The stream will be created lazily, to support potentially large - * (or infinite) iterators. Iterators passed to this method should - * not be used elsewhere -- doing so will result in problems. - * - * The use case for this method is code like .fromIterable, which - * creates an iterator for the express purpose of calling this - * method. - */ - def fromIteratorUnsafe[A](it: Iterator[A]): Streaming[A] = - if (it.hasNext) Cons(it.next, Later(fromIteratorUnsafe(it))) else Empty() - - /** - * Create a self-referential stream. - */ - def knot[A](f: Eval[Streaming[A]] => Streaming[A], memo: Boolean = false): Streaming[A] = { - lazy val s: Eval[Streaming[A]] = if (memo) Later(f(s)) else Always(f(s)) - s.value - } - - /** - * Continually return a constant value. - */ - def continually[A](a: A): Streaming[A] = - knot(s => Cons(a, s), memo = true) - - /** - * Continually return the result of a thunk. - * - * This method only differs from `continually` in that the thunk may - * not be pure. Thus, repeated traversals may produce different - * results. - */ - def thunk[A](f: () => A): Streaming[A] = - knot(s => Cons(f(), s), memo = false) - - /** - * Produce an infinite stream of values given an initial value and a - * tranformation function. - */ - def infinite[A](a: A)(f: A => A): Streaming[A] = - Cons(a, Always(infinite(f(a))(f))) - - /** - * Stream of integers starting at n. - */ - def from(n: Int): Streaming[Int] = - infinite(n)(_ + 1) - - /** - * Provide a stream of integers starting with `start` and ending - * with `end` (i.e. inclusive). - */ - def interval(start: Int, end: Int): Streaming[Int] = - if (start > end) Empty() else Cons(start, Always(interval(start + 1, end))) - - /** - * Produce a stream given an "unfolding" function. - * - * None represents an empty stream. Some(a) reprsents an initial - * element, and we can compute the tail (if any) via f(a). - */ - def unfold[A](o: Option[A])(f: A => Option[A]): Streaming[A] = - o match { - case None => Empty() - case Some(a) => Cons(a, Always(unfold(f(a))(f))) - } -} - -private[data] sealed trait StreamingInstances extends StreamingInstances1 { - - implicit val streamInstance: Traverse[Streaming] with MonadCombine[Streaming] with CoflatMap[Streaming] = - new Traverse[Streaming] with MonadCombine[Streaming] with CoflatMap[Streaming] { - def pure[A](a: A): Streaming[A] = - Streaming(a) - override def map[A, B](as: Streaming[A])(f: A => B): Streaming[B] = - as.map(f) - def flatMap[A, B](as: Streaming[A])(f: A => Streaming[B]): Streaming[B] = - as.flatMap(f) - def empty[A]: Streaming[A] = - Streaming.empty - def combineK[A](xs: Streaming[A], ys: Streaming[A]): Streaming[A] = - xs ++ ys - - override def map2[A, B, Z](fa: Streaming[A], fb: Streaming[B])(f: (A, B) => Z): Streaming[Z] = - fa.flatMap(a => fb.map(b => f(a, b))) - - def coflatMap[A, B](fa: Streaming[A])(f: Streaming[A] => B): Streaming[B] = - fa.tails.filter(_.nonEmpty).map(f) - - def foldLeft[A, B](fa: Streaming[A], b: B)(f: (B, A) => B): B = - fa.foldLeft(b)(f) - - def foldRight[A, B](fa: Streaming[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - fa.foldRight(lb)(f) - - def traverse[G[_]: Applicative, A, B](fa: Streaming[A])(f: A => G[B]): G[Streaming[B]] = { - val G = Applicative[G] - def init: G[Streaming[B]] = G.pure(Streaming.empty[B]) - - // We use foldRight to avoid possible stack overflows. Since - // we don't want to return a Eval[_] instance, we call .value - // at the end. - // - // (We don't worry about internal laziness because traverse - // has to evaluate the entire stream anyway.) - foldRight(fa, Later(init)) { (a, lgsb) => - lgsb.map(gsb => G.map2(f(a), gsb) { (a, s) => Streaming.cons(a, s) }) - }.value - } - - override def exists[A](fa: Streaming[A])(p: A => Boolean): Boolean = - fa.exists(p) - - override def forall[A](fa: Streaming[A])(p: A => Boolean): Boolean = - fa.forall(p) - - override def isEmpty[A](fa: Streaming[A]): Boolean = - fa.isEmpty - - override def toStreaming[A](fa: Streaming[A]): Streaming[A] = - fa - } - - implicit def streamOrder[A: Order]: Order[Streaming[A]] = - new Order[Streaming[A]] { - def compare(x: Streaming[A], y: Streaming[A]): Int = - (x izipMap y)(_ compare _, _ => 1, _ => -1) - .find(_ != 0).getOrElse(0) - } -} - -private[data] sealed trait StreamingInstances1 extends StreamingInstances2 { - implicit def streamPartialOrder[A: PartialOrder]: PartialOrder[Streaming[A]] = - new PartialOrder[Streaming[A]] { - def partialCompare(x: Streaming[A], y: Streaming[A]): Double = - (x izipMap y)(_ partialCompare _, _ => 1.0, _ => -1.0) - .find(_ != 0.0).getOrElse(0.0) - } -} - -private[data] sealed trait StreamingInstances2 { - implicit def streamEq[A: Eq]: Eq[Streaming[A]] = - new Eq[Streaming[A]] { - def eqv(x: Streaming[A], y: Streaming[A]): Boolean = - (x izipMap y)(_ === _, _ => false, _ => false) - .forall(_ == true) - } -} diff --git a/core/src/main/scala/cats/std/list.scala b/core/src/main/scala/cats/std/list.scala index b7790f9d77..c79a3d006b 100644 --- a/core/src/main/scala/cats/std/list.scala +++ b/core/src/main/scala/cats/std/list.scala @@ -4,7 +4,6 @@ package std import algebra.Eq import algebra.std.{ListMonoid, ListOrder} -import cats.data.Streaming import cats.syntax.order._ import cats.syntax.show._ @@ -64,9 +63,6 @@ trait ListInstances extends ListInstances1 { fa.forall(p) override def isEmpty[A](fa: List[A]): Boolean = fa.isEmpty - - override def toStreaming[A](fa: List[A]): Streaming[A] = - Streaming.fromList(fa) } implicit def listAlgebra[A]: Monoid[List[A]] = new ListMonoid[A] diff --git a/core/src/main/scala/cats/std/vector.scala b/core/src/main/scala/cats/std/vector.scala index 4ef0a404d4..4df0044bdb 100644 --- a/core/src/main/scala/cats/std/vector.scala +++ b/core/src/main/scala/cats/std/vector.scala @@ -1,7 +1,6 @@ package cats package std -import cats.data.Streaming import cats.syntax.show._ import scala.annotation.tailrec @@ -52,9 +51,6 @@ trait VectorInstances { fa.exists(p) override def isEmpty[A](fa: Vector[A]): Boolean = fa.isEmpty - - override def toStreaming[A](fa: Vector[A]): Streaming[A] = - Streaming.fromVector(fa) } implicit def vectorShow[A:Show]: Show[Vector[A]] = diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index 9c943232e3..2a2f68faef 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -31,7 +31,6 @@ trait AllSyntax with SemigroupKSyntax with Show.ToShowOps with SplitSyntax - with StreamingSyntax with StrongSyntax with TransLiftSyntax with TraverseSyntax diff --git a/core/src/main/scala/cats/syntax/monadCombine.scala b/core/src/main/scala/cats/syntax/monadCombine.scala index 8490b0de5b..6d44699e33 100644 --- a/core/src/main/scala/cats/syntax/monadCombine.scala +++ b/core/src/main/scala/cats/syntax/monadCombine.scala @@ -14,10 +14,10 @@ final class NestedMonadCombineOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Monad * * Example: * {{{ - * scala> import cats.data.Streaming * scala> import cats.std.list._ + * scala> import cats.std.vector._ * scala> import cats.syntax.monadCombine._ - * scala> val x: List[Streaming[Int]] = List(Streaming(1, 2), Streaming(3, 4)) + * scala> val x: List[Vector[Int]] = List(Vector(1, 2), Vector(3, 4)) * scala> x.unite * res0: List[Int] = List(1, 2, 3, 4) * }}} diff --git a/core/src/main/scala/cats/syntax/package.scala b/core/src/main/scala/cats/syntax/package.scala index bd09c018d5..154a9e94cf 100644 --- a/core/src/main/scala/cats/syntax/package.scala +++ b/core/src/main/scala/cats/syntax/package.scala @@ -30,7 +30,6 @@ package object syntax { object semigroupk extends SemigroupKSyntax object show extends Show.ToShowOps object split extends SplitSyntax - object streaming extends StreamingSyntax object strong extends StrongSyntax object transLift extends TransLiftSyntax object traverse extends TraverseSyntax diff --git a/core/src/main/scala/cats/syntax/streaming.scala b/core/src/main/scala/cats/syntax/streaming.scala deleted file mode 100644 index 34180f8774..0000000000 --- a/core/src/main/scala/cats/syntax/streaming.scala +++ /dev/null @@ -1,32 +0,0 @@ -package cats -package syntax - -import cats.data.Streaming -import cats.data.Streaming.Cons - -/** - * Contains various Stream-specific syntax. - * - * To eanble this, do one of the following: - * - * import cats.implicits._ - * import cats.syntax.all._ - * import cats.syntax.streaming._ - * - * This provides the %:: and %::: operators for constructing Streams - * lazily, and the %:: extract to use when pattern matching on - * Streams. - */ -trait StreamingSyntax { - object %:: { - def unapply[A](s: Streaming[A]): Option[(A, Eval[Streaming[A]])] = s.uncons - } - - implicit def streamingOps[A](as: => Streaming[A]): StreamingOps[A] = - new StreamingOps(Always(as)) - - final class StreamingOps[A](rhs: Eval[Streaming[A]]) { - def %::(lhs: A): Streaming[A] = Cons(lhs, rhs) - def %:::(lhs: Streaming[A]): Streaming[A] = lhs ++ rhs - } -} diff --git a/docs/src/main/tut/streaming.md b/docs/src/main/tut/streaming.md deleted file mode 100644 index 00ffebc330..0000000000 --- a/docs/src/main/tut/streaming.md +++ /dev/null @@ -1,432 +0,0 @@ ---- -layout: default -title: "Streaming" -section: "data" -source: "core/src/main/scala/cats/data/Streaming.scala" -scaladoc: "#cats.data.Streaming" ---- - -# Streaming - -The `Streaming` data type provides support for sequences of values -which can be computed on demand. It is immutable (meaning its -contents will never change), and supports optional memoization. - -The data type which `Streaming` implements is often called a *stream*. -In fact, `Streaming` is similar to an existing Scala data type called -`Stream` (the name `Streaming` was chosen to avoid a conflict with the -standard library). - -Sometimes the `Streaming` documentation will refer to a *stream*. In -these cases, we will use lowercase *stream* as a general term, -distinguished from `Stream` (the specific type from the standard -library) or `Streaming` (the specific type from Cats). - -## Introduction - -A non-empty `Streaming` instance is structured like a `List`: it has a -*cons* cell containing a single value, as well as a reference to a -tail which will calculate the subsequent values (if any). This means -that adding values to the beginning is very efficient, whereas adding -values to the end is potentially expensive. - -The major difference between `List` and `Streaming` is evaluation. -`List` is strict: this means that if you have an instance of `List` -the entire thing has been calculated and constructed in memory for -you to access, even if you never access any of the list's elements. - -Unlike `List`, a `Streaming` instance can lazily-compute its tail. This -means that until the tail is needed, it will not be constructed (and -only the part that is accessed will be constructed). This is very -useful for sequences which are potentially large (or infinite) but are -easily computed. - -This laziness might remind you of another familiar type: `Iterator`. -Like `Streaming`, `Iterator` also computes values on-demand using a -`.next` method (along with `.hasNext`). However, the resemblance is -only skin deep, since `Iterator` is a mutable data type. This means -that `Iterator` can only be traversed once, it cannot be safely shared, -and is often more difficult to reason about. - -By contrast, `Streaming` can be safely shared (and safely accessed from -many threads simultaneously). It can be traversed as many times as -needed, and has all the other advantages of an immutable data -structure. - -## Using Streaming - -The `Streaming` type does not extend `Seq[_]`, `Iterable[_]`, or any of -the other Scala collection types. Despite this, you can use `Streaming` -in most of the ways you use other Scala collections: - -```tut -import cats.data.Streaming - -val ns = Streaming(1, 10, 100, 1000) -val xs = ns.filter(_ < 50) -val ys = ns.map(_ * 2 + 1) -val zs = xs ++ ys - -zs.toList -``` - -The first thing you probably noticed was that `.toString` does not give -the complete contents of a `Streaming` instance. This is intentional: -since the contents may be lazy, we won't print the entire stream by -default. Ellipses (`...`) are used to indicate the possibility of more -elements. - -Instead, you can pass an argument to `.toString()` which will specify -the maximum number of terms to be evaluated: - -```tut -val ns = Streaming(1, 2, 3, 4, 5) -ns.toString(3) -ns.toString(5) -ns.toString(100) -``` - -In our previous examples we've been using `Streaming#apply` to -construct our instances. This means these instances are not lazy (since -we provided all the elements to the constructor). - -However, we can also construct lazy instances: - -```tut -import cats.implicits._ - -def int(n: Int): Int = { println(s"int($n)"); n } -val now = Streaming(int(1), int(2), int(3)) -val later = int(1) %:: int(2) %:: int(3) %:: Streaming.empty[Int] -``` - -Notice the difference between the `now` and `later` instances. In the -*now* case, we print messages for `1`, `2`, and `3`. In the *later* -case, we only print a message for `1`. This indicates that the tail of -the stream (containing `2` and `3`) will be constructed lazily, i.e. it -has not been constructed yet. We can take advantage of this feature to -write interesting definitions: - -```tut -lazy val fives: Streaming[Int] = 5 %:: fives -fives.take(5).toList -``` - -## Fibonaci Sequence - -The Fibonacci sequence starts with `0, 1, 1, 2, 3, 5, 8, 13, ...` and -continues on forever: it is an infinite sequence. Each term is -calculated by adding the two previous terms (the starting values `0` -and `1` are fixed). - -Since the sequence grows forever, lets set up an unbounded integer type -(`Z`) as well as some useful values: - -```tut:silent -type Z = BigInt - -val Z0 = BigInt(0) -val Z1 = BigInt(1) -``` - -Our first implementation will be a simple recursive method. While this -doesn't use `Streaming`, its definition is very close to one a -mathematian might write. - -```tut:silent -def fib(n: Int): Z = - n match { - case 0 => Z0 - case 1 => Z1 - case x if x > 1 => fib(x - 1) + fib(x - 2) - case x => sys.error(s"invalid x ($x)") - } -``` - -However, we can also model the Fibonacci sequence as an infinite stream -of values, where each value depends upon the two previous values. Our -first implementation uses a `lazy val` to set up a self-referential -structure which captures this relationship. - -```tut:silent -lazy val fibs: Streaming[Z] = - Z0 %:: Z1 %:: (fibs zipMap fibs.drop(1))(_ + _) -fibs.take(6).toList -``` - -You might be surprised that you can zip a stream with itself. But -because we are dealing with immutable values, there is no problem! -There is no way that referencing the same stream multiple times can go -wrong, or corrupt internal state. - -We can also be a bit more explicit about the recursive nature of the -Fibonacci sequence, while still modeling it with `Streaming[Z]`. Since -each "step" of the sequence generates the next term given the current -state (and sets up subsequent steps by changing the state) we can use -an inner method (`term`) which acts exactly the same way: - -```tut:silent -val fibs: Streaming[Z] = { - def term(x: Z, y: Z): Streaming[Z] = x %:: term(y, x + y) - term(Z0, Z1) -} -``` - -In this formulation, `x` is the "current" term and `y` is the "next" -term. The term after `y` will be `x + y`, which we calculate after -emitting `x` (and which is saved along with `y` in our suspended call -to `term`). - -One thing to keep in mind is that the `%::` syntax defers the call to -`term` (it is interpreted as a by-name parameter through some -syntactic trickery). This means that `term` is not a recursive method -in the usual sense: the method does not call itself directly, but -instead builds a `Streaming[Z]` instance which will potentially call -`term` itself when the stream's tail is needed. - -This technique of defining streams (using an inner `term` method) is -quite powerful and general. Our third example uses a built-in method -for producing infinite streams that can be defined in the same way (a -current *state* and a way to get the next *state* from the current -one): - -```tut:silent -val fibs: Streaming[Z] = - Streaming.infinite((Z0, Z1)) { case (x, y) => (y, x + y) } map (_._1) -``` - -The first argument to `.infinite` is the starting state (`(Z0, Z1)`, -i.e. zero and one). The next argument is a method from the current -state (`(x, y)`) to the next one (`(y, x + y)`). Ignoring the call to -`.map` for right now, this is the sequence that would be produced: - -``` -start: (0, 1) -step1: (1, 1) -step2: (1, 2) -step3: (2, 3) -step4: (3, 5) -... -``` - -If you look at the first column of numbers you can see the familiar -Fibonacci sequence. However, our sequence needs to return values of -type `Z`, not tuples of `(Z, Z)` pairs. The call to `map (_._1)` is -just to take the first of the two numbers, to produce the sequence we -want. - -If we had a sequence that only relied on one previous number (e.g. `1, -2, 3, ...`) we would not need this step. If we had a sequence that -used a much more complicated state we could model it similarly: create -an infinite stream of states, then map the states to the values we -want to emit. - -## Counting the rational numbers - -To prove these kinds of constructions work for examples which are more -complex, let's model an infinite stream of all rational numbers. This -infinite stream should generate every rational number (no matter how -big or small) exactly once at some point. (In practice we have limited -time and space, so we will have to make do with the numbers we have -the patience to compute.) - -First let's review what a rational number is. A rational number is any -number which can be written as a fraction. For example 3 is a rational -number (3/1), so is 0.2 (1/5), so is 117/113, etc. - -To simplify the example, we're going to use the type `(Z, Z)` to -represent a rational number (which we will alias as `Q`). The first -value of the tuple is the numerator, and the second is the -denominator. We'll also limit ourselves to *canonical* rational -numbers: numbers whose denominator is positive, and which are in -simplest form. For example, 2/5, 3/1, and -9/1 are canonical, whereas -6/-5, -1/-1, and 4/10 are not. - -```tut:silent -type Q = (Z, Z) - -// Some useful rational constants for zero and one. -val Q0 = (Z0, Z1) -val Q1 = (Z1, Z1) -``` - -The actual code for defining this stream is very concise, but needs -some explanation. The basic approach is that we want to generate all -possible pairs of integers, and then filter out anything that is not -canonical. This will leave us with all the rational numbers: - -```tut:silent -val zs = Streaming.infinite(Z1)(_ + Z1) -val pairs = (zs product zs) -val qs = pairs.filter { case (n, d) => (n gcd d) == Z1 } -val rats = Q0 %:: qs.flatMap { case q @ (n, d) => Streaming(q, (-n, d)) } -``` - -First we define `zs`, an infinte stream of all the positive numbers -(beginning with one). Next we use the `product` method to create the -Cartesian product of `zs` with itself (every possible pairing of two -values from `zs`). Here are the first few terms of `pairs` to give you -the idea: - -```tut -pairs.toString(12) -``` - -As you can see, this sequence contains some pairs which are not -canonical rational numbers (e.g. `(2, 2)`). The next step filters -these out using the `gcd` method (which finds the greatest common -divisor between two numbers). Unless the GCD is one, the numbers share -a divisor, and are not in the simplest form. In the case of `(2, 2)`, -the simplest form would be `(1, 1)` which we already saw. (In fact, -every non-canonical rational number has a canonical equivalent which -will have already appeared in the stream.) Here are the first few -terms of `qs`: - -```tut -qs.toString(12) -``` - -Finally, we have to do two things to produce a correct -sequence. First, we have to include zero (since we started `zs` with -one, we won't have any zeros in `qs`). Second, we need to produce -negative numbers (the values in `qs` are all positive). Once we've -accomplished these things, we're done. Here are the first few terms of `rats`: - -```tut -rats.toString(12) -``` - -## pseudo-random numbers - -We can also use `Streaming` to implement streams of pseudo-random -values. Every pseudo-random number generator (PRNG) is a combination -of a state value, and a transition function to produce the next state -from the current one. This is just like our Fibonacci example from -earlier. We will use the same PRNG we do in the documentation for -`StateT`, a linear-congruent generator devised by Donald Knuth call -MMIX. - -The basic idea behind the PRNG is that multiplying two large `Long` -values will cause the value to "wrap around" to another valid `Long` -value. Using addition and multiplication, we can create a stream of -`Long` values which appear random: - -```tut -def createMmix(seed: Long): Streaming[Long] = - Streaming.infinite(seed)(n => n * 6364136223846793005L + 1442695040888963407L) - -val longs: Streaming[Long] = createMmix(0x741f2d2eea7e5c70L) - -longs.toString(5) -``` - -Our stream (`longs`) produces an infinite sequence of arbitrary `Long` -values, based on the seed (`0x741f2d2eea7e5c70`) which was provided to -`createMmix`. - -We can transform the `longs` stream into any kind of stream we -want. For example, if we want a `Streaming[Int]` instead we can split -each `Long` value in two. We'll use `.flatMap` to produce a -`Streaming[Int]` containing two 32-bit `Int` values for each 64-bit -`Long`: - -```tut -val ints: Streaming[Int] = - longs.flatMap { (n: Long) => - val n0 = ((n >>> 32) & 0xffffffff).toInt - val n1 = (n & 0xffffffff).toInt - Streaming(n0, n1) - } -``` - -It's worth noting that while `longs` is infinite, there are only a -finite number of `Long` values. More worryingly, the same input value -will also produce the same output value, meaning that eventually our -sequence will hit a cycle and repeat itself. (In the case of MMIX the -period of the sequence is 2^64.) - -PRNGs which need to provide a large amount of random entropy will use -much larger state variables, and have correspondingly larger periods. - -## Digits of Pi - -The final example is more complex than the previous examples: we will -compute the digits of Pi as a `Streaming[Z]`, where each digit is one -element in the stream. Since Pi has an infinite decimal expansion, the -stream of digits is also infinite. - -The algorithm used is ported from this [python code](http://www.cs.utsa.edu/~wagner/pi/pi_cont.html). -It uses a [continued fraction](https://en.wikipedia.org/wiki/Continued_fraction) -to get ever-closer approximations of Pi, which it uses to produce decimal digits. - -First we will need to define some more numerical constants: - -```tut:silent -val Z2 = BigInt(2) -val Z4 = BigInt(4) -val Z10 = BigInt(10) -val Z12 = BigInt(12) -``` - -To make the algorithms' structure a bit more amenable to defining a -stream, we are going to break it up into two parts: - - * `narrow`: the outer loop, refines the approximation of Pi. - * `emit`: the inner loop, outputs decimal terms as available. - -You might notice that these methods are corecursive (they call each -other). In some cases, corecursive methods will lead to stack -overflows (if the call chain becomes too "deep"). However, the details -of this algorithms ensure that there won't be very many `narrow` and -`emit` calls before a term of the stream is output. - -Also, since we are outputting decimal digits (0-9) you might wonder -why our calculations are done using `Z`, rather than the much less -expensive `Int` which we return. The reason we do this is that -internally our calculation needs to use very large numbers to -represent increasingly precise fractional approximations of Pi. - -```tut:silent -val pi: Streaming[Int] = { - - def narrow(k: Z, a: Z, b: Z, a1: Z, b1: Z): Streaming[Int] = { - val (p, q) = (k * k, k * Z2 + Z1) - val (aa, bb, aa1, bb1) = (a1, b1, p * a + q * a1, p * b + q * b1) - val (d, d1) = (aa / bb, aa1 / bb1) - emit(k + Z1, aa, bb, aa1, bb1, p, q, d, d1) - } - - def emit(k: Z, a: Z, b: Z, a1: Z, b1: Z, p: Z, q: Z, d: Z, d1: Z): Streaming[Int] = - if (d != d1) narrow(k, a, b, a1, b1) else { - val (aa, aa1) = ((a % b) * Z10, (a1 % b1) * Z10) - val (dd, dd1) = (aa / b, aa1 / b1) - d.toInt %:: emit(k, aa, b, aa1, b1, p, q, dd, dd1) - } - - // Starting parameters needed to calculate Pi. - narrow(Z2, Z4, Z1, Z12, Z4) -} -``` - -Once again, we have our starting state (the five inputs to `narrow`) -and our transition function (`narrow` and `emit` in tandem), so we can -build a stream. - -Does it work? Let's find out! - -```tut -def str(xs: Streaming[Int]): String = - xs.foldLeft("")(_ + _.toString) - -val h = pi.take(1) -val t = pi.drop(1).take(40) -str(h) + "." + str(t) + "..." -``` - -## Conclusion - -Lazy, immutable streams are a powerful way to model an in-progress -calculation, especially when those sequences are potentially -unbounded. While these examples were based on mathematical problems, -streams are a great way to model any case where waiting to collect all -the elements of a sequence would be inefficient or prohibitive. diff --git a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala index e9307b87f7..d22eabb378 100644 --- a/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/Arbitrary.scala @@ -59,27 +59,6 @@ object arbitrary extends ArbitraryInstances0 { implicit def appFuncArbitrary[F[_], A, B](implicit F: Arbitrary[F[B]], FF: Applicative[F]): Arbitrary[AppFunc[F, A, B]] = Arbitrary(F.arbitrary.map(fb => Func.appFunc[F, A, B](_ => fb))) - def streamingGen[A:Arbitrary](maxDepth: Int): Gen[Streaming[A]] = - if (maxDepth <= 1) - Gen.const(Streaming.empty[A]) - else { - // the arbitrary instance for the next layer of the stream - implicit val A = Arbitrary(streamingGen[A](maxDepth - 1)) - Gen.frequency( - // Empty - 1 -> Gen.const(Streaming.empty[A]), - // Wait - 2 -> getArbitrary[Eval[Streaming[A]]].map(Streaming.wait(_)), - // Cons - 6 -> (for { - a <- getArbitrary[A] - tail <- getArbitrary[Eval[Streaming[A]]] - } yield Streaming.cons(a, tail))) - } - - implicit def streamingArbitrary[A:Arbitrary]: Arbitrary[Streaming[A]] = - Arbitrary(streamingGen[A](8)) - implicit def writerArbitrary[L:Arbitrary, V:Arbitrary]: Arbitrary[Writer[L, V]] = writerTArbitrary[Id, L, V] diff --git a/tests/src/test/scala/cats/tests/FoldableTests.scala b/tests/src/test/scala/cats/tests/FoldableTests.scala index 1b0871755f..8d4efefd24 100644 --- a/tests/src/test/scala/cats/tests/FoldableTests.scala +++ b/tests/src/test/scala/cats/tests/FoldableTests.scala @@ -5,9 +5,7 @@ import org.scalatest.prop.PropertyChecks import org.scalacheck.{Arbitrary, Gen} import org.scalacheck.Arbitrary.arbitrary -import cats.data.Streaming import cats.std.all._ -import cats.laws.discipline.arbitrary._ abstract class FoldableCheck[F[_]: Foldable](name: String)(implicit ArbFInt: Arbitrary[F[Int]]) extends CatsSuite with PropertyChecks { @@ -37,7 +35,6 @@ abstract class FoldableCheck[F[_]: Foldable](name: String)(implicit ArbFInt: Arb test("toList/isEmpty/nonEmpty") { forAll { (fa: F[Int]) => fa.toList should === (iterator(fa).toList) - fa.toStreaming.toList should === (iterator(fa).toList) fa.isEmpty should === (iterator(fa).isEmpty) fa.nonEmpty should === (iterator(fa).nonEmpty) } @@ -100,9 +97,6 @@ class FoldableTestsAdditional extends CatsSuite { // test trampolining val large = Stream((1 to 10000): _*) assert(contains(large, 10000).value) - - // toStreaming should be lazy - assert(dangerous.toStreaming.take(3).toList == List(0, 1, 2)) } } @@ -118,10 +112,6 @@ class FoldableStreamCheck extends FoldableCheck[Stream]("stream") { def iterator[T](stream: Stream[T]): Iterator[T] = stream.iterator } -class FoldableStreamingCheck extends FoldableCheck[Streaming]("streaming") { - def iterator[T](streaming: Streaming[T]): Iterator[T] = streaming.iterator -} - class FoldableMapCheck extends FoldableCheck[Map[Int, ?]]("map") { def iterator[T](map: Map[Int, T]): Iterator[T] = map.iterator.map(_._2) } diff --git a/tests/src/test/scala/cats/tests/StreamingTests.scala b/tests/src/test/scala/cats/tests/StreamingTests.scala deleted file mode 100644 index 00c5c12194..0000000000 --- a/tests/src/test/scala/cats/tests/StreamingTests.scala +++ /dev/null @@ -1,438 +0,0 @@ -package cats -package tests - -import algebra.laws.OrderLaws - -import cats.data.Streaming -import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq._ -import cats.laws.discipline.{TraverseTests, CoflatMapTests, MonadCombineTests, SerializableTests, CartesianTests} -import org.scalacheck.{Arbitrary, Gen} - -class StreamingTests extends CatsSuite { - checkAll("Streaming[Int]", CartesianTests[Streaming].cartesian[Int, Int, Int]) - checkAll("Cartesian[Streaming]", SerializableTests.serializable(Cartesian[Streaming])) - - checkAll("Streaming[Int]", CoflatMapTests[Streaming].coflatMap[Int, Int, Int]) - checkAll("CoflatMap[Streaming]", SerializableTests.serializable(CoflatMap[Streaming])) - - checkAll("Streaming[Int]", MonadCombineTests[Streaming].monadCombine[Int, Int, Int]) - checkAll("MonadCombine[Streaming]", SerializableTests.serializable(MonadCombine[Streaming])) - - checkAll("Streaming[Int] with Option", TraverseTests[Streaming].traverse[Int, Int, Int, Int, Option, Option]) - checkAll("Traverse[Streaming]", SerializableTests.serializable(Traverse[Streaming])) - - checkAll("Streaming[Int]", OrderLaws[Streaming[Int]].order) - checkAll("Order[Streaming[Int]]", SerializableTests.serializable(Order[Streaming[Int]])) - - { - implicit val I = ListWrapper.partialOrder[Int] - checkAll("Streaming[ListWrapper[Int]]", OrderLaws[Streaming[ListWrapper[Int]]].partialOrder) - checkAll("PartialOrder[Streaming[ListWrapper[Int]]]", SerializableTests.serializable(PartialOrder[Streaming[ListWrapper[Int]]])) - } - - { - implicit val I = ListWrapper.eqv[Int] - checkAll("Streaming[ListWrapper[Int]]", OrderLaws[Streaming[ListWrapper[Int]]].eqv) - checkAll("Eq[Streaming[ListWrapper[Int]]]", SerializableTests.serializable(Eq[Streaming[ListWrapper[Int]]])) - } -} - -class AdHocStreamingTests extends CatsSuite { - - test("results aren't reevaluated after memoize") { - forAll { (orig: Streaming[Int]) => - val ns = orig.toList - val size = ns.size - - var i = 0 - val memoized = orig.map { n => i += 1; n }.memoize - - val xs = memoized.toList - i should === (size) - xs should === (ns) - - val ys = memoized.toList - i should === (size) - ys should === (ns) - } - } - - test("fromList/toList") { - forAll { (xs: List[Int]) => - Streaming.fromList(xs).toList should === (xs) - } - - forAll { (xs: Streaming[Int]) => - val list = xs.toList - Streaming.fromList(list).toList should === (list) - } - } - - test("map") { - forAll { (xs: Streaming[Int], f: Int => Double) => - xs.map(f).toList should === (xs.toList.map(f)) - } - } - - test("flatMap") { - forAll { (xs: Streaming[Int], f: Int => Streaming[Double]) => - xs.flatMap(f).toList should === (xs.toList.flatMap(f(_).toList)) - } - } - - test("filter") { - forAll { (xs: Streaming[Int], f: Int => Boolean) => - xs.filter(f).toList should === (xs.toList.filter(f)) - } - } - - test("foldLeft") { - forAll { (xs: Streaming[String], n: Int, f: (Int, String) => Int) => - xs.foldLeft(n)(f) should === (xs.toList.foldLeft(n)(f)) - } - } - - test("isEmpty") { - forAll { (xs: Streaming[String], n: Int, f: (Int, String) => Int) => - xs.isEmpty should === (xs.toList.isEmpty) - } - } - - test("++") { - forAll { (xs: Streaming[Int], ys: Streaming[Int]) => - (xs ++ ys).toList should === (xs.toList ::: ys.toList) - } - } - - test("zip") { - forAll { (xs: Streaming[Int], ys: Streaming[Int]) => - (xs zip ys).toList should === (xs.toList zip ys.toList) - } - } - - test("zipWithIndex") { - forAll { (xs: Streaming[Int]) => - xs.zipWithIndex.toList should === (xs.toList.zipWithIndex) - } - } - - test("unzip") { - forAll { (xys: Streaming[(Int, Int)]) => - val (xs, ys): (Streaming[Int], Streaming[Int]) = xys.unzip - (xs.toList, ys.toList) should === (xys.toList.unzip) - } - } - - test("exists") { - forAll { (xs: Streaming[Int], f: Int => Boolean) => - xs.exists(f) should === (xs.toList.exists(f)) - } - } - - test("forall") { - forAll { (xs: Streaming[Int], f: Int => Boolean) => - xs.forall(f) should === (xs.toList.forall(f)) - } - } - - test("take") { - forAll { (xs: Streaming[Int], n: Int) => - xs.take(n).toList should === (xs.toList.take(n)) - } - } - - test("drop") { - forAll { (xs: Streaming[Int], n: Int) => - xs.drop(n).toList should === (xs.toList.drop(n)) - } - } - - test("takeWhile") { - forAll { (xs: Streaming[Int], f: Int => Boolean) => - xs.takeWhile(f).toList should === (xs.toList.takeWhile(f)) - } - } - - test("dropWhile") { - forAll { (xs: Streaming[Int], f: Int => Boolean) => - xs.dropWhile(f).toList should === (xs.toList.dropWhile(f)) - } - } - - test("tails") { - forAll { (xs: Streaming[Int]) => - xs.tails.map(_.toList).toList should === (xs.toList.tails.toList) - } - } - - test("toArray") { - forAll { (xs: Streaming[Int]) => - xs.toArray should be (xs.toList.toArray) - } - } - - test("compact should result in same values") { - forAll { (xs: Streaming[Int]) => - xs.compact should === (xs) - } - } - - test("fromFoldable consistent with fromList") { - forAll { (xs: List[Int]) => - Streaming.fromFoldable(xs) should === (Streaming.fromList(xs)) - } - } - - test("fromIterable consistent with fromList") { - forAll { (xs: List[Int]) => - Streaming.fromIterable(xs) should === (Streaming.fromList(xs)) - } - } - - test("fromIteratorUnsafe consistent with fromList") { - forAll { (xs: List[Int]) => - Streaming.fromIteratorUnsafe(xs.iterator) should === (Streaming.fromList(xs)) - } - } - - test("continually consistent with List.fill") { - forAll { (l: Long, b: Byte) => - val n = b.toInt - Streaming.continually(l).take(n).toList should === (List.fill(n)(l)) - } - } - - test("continually consistent with thunk") { - forAll { (l: Long, b: Byte) => - val n = b.toInt - Streaming.continually(l).take(n) should === (Streaming.thunk(() => l).take(n)) - } - } - - test("equality consistent with list equality") { - forAll { (xs: Streaming[Int], ys: Streaming[Int]) => - Eq[Streaming[Int]].eqv(xs, ys) should === (xs.toList == ys.toList) - } - } - - test("unfold with Some consistent with infinite") { - forAll { (start: Int, b: Byte) => - val n = b.toInt - val unfolded = Streaming.unfold(Some(start))(n => Some(n + 1)).take(n) - unfolded should === (Streaming.infinite(start)(_ + 1).take(n)) - } - } - - test("unfold consistent with infinite then takeWhile") { - implicit val arbInt: Arbitrary[Int] = Arbitrary(Gen.choose(-10, 20)) - forAll { (start: Int, n: Int) => - val end = start + n - def check(i: Int): Option[Int] = if (i <= end) Some(i) else None - val unfolded = Streaming.unfold(check(start))(i => check(i + 1)) - val fromInfinite = Streaming.infinite(start)(_ + 1).takeWhile(_ <= end) - unfolded.toList should === (fromInfinite.toList) - } - } - - test("unfold on None returns empty stream") { - forAll { (f: Int => Option[Int]) => - Streaming.unfold(none[Int])(f) should === (Streaming.empty[Int]) - } - } - - test("peekEmpty consistent with isEmpty") { - forAll { (s: Streaming[Int]) => - s.peekEmpty.foreach(_ should === (s.isEmpty)) - } - } - - test("memoize doesn't change values") { - forAll { (s: Streaming[Int]) => - s.memoize should === (s) - } - } - - test("thunk is evaluated for each item") { - // don't want the stream to be too big - implicit val arbInt = Arbitrary(Gen.choose(-10, 20)) - forAll { (start: Int, end: Int) => - var i = start - 1 - val stream = Streaming.thunk{ () => i += 1; i}.takeWhile(_ <= end) - stream.toList should === ((start to end).toList) - } - } - - test("uncons consistent with headOption"){ - forAll { (s: Streaming[Int]) => - s.uncons.map(_._1) should === (s.toList.headOption) - } - } - - test("uncons tail consistent with drop(1)"){ - forAll { (s: Streaming[Int]) => - val tail: Option[Streaming[Int]] = s.uncons.map(_._2.value) - tail.foreach(_.toList should === (s.toList.drop(1))) - } - } - - test("isEmpty consistent with fold"){ - forAll { (s: Streaming[Int]) => - s.isEmpty should === (s.fold(Now(true), (_, _) => false)) - } - } - - test("foldStreaming consistent with fold"){ - forAll { (ints: Streaming[Int], longs: Streaming[Long], f: (Int, Eval[Streaming[Int]]) => Streaming[Long]) => - ints.foldStreaming(longs, f) should === (ints.fold(Now(longs), f)) - } - } - - test("interval") { - // we don't want this test to take a really long time - implicit val arbInt: Arbitrary[Int] = Arbitrary(Gen.choose(-10, 20)) - forAll { (start: Int, end: Int) => - Streaming.interval(start, end).toList should === ((start to end).toList) - } - } - - test("merge") { - forAll { (xs: List[Int], ys: List[Int]) => - (Streaming.fromList(xs.sorted) merge Streaming.fromList(ys.sorted)).toList should === ((xs ::: ys).sorted) - } - } - - test("product") { - forAll { (xs: Streaming[Int], ys: Streaming[Int]) => - val result = (xs product ys).iterator.toSet - val expected = (for { x <- xs.toList; y <- ys.toList } yield (x, y)).toSet - result should === (expected) - } - - val nats = Streaming.from(1) // 1, 2, 3, ... - - def isRelativelyPrime(t: (Int, Int)): Boolean = { - def euclid(x: Int, y: Int): Int = if (y == 0) x else euclid(y, x % y) - euclid(t._1, t._2) == 1 - } - - val positiveRationals = (nats product nats).filter(isRelativelyPrime) - val e = Set((1,1), (2,1), (1,2), (3,1), (1,3), (4,1), (3,2), (2,3), (1,4)) - positiveRationals.take(e.size).iterator.toSet should === (e) - } - - test("interleave") { - forAll { (xs: Vector[Int]) => - // not a complete test but it'll have to do for now - val s = Streaming.fromVector(xs) - val r = (s interleave s).iterator.toVector - for (i <- 0 until xs.length) { - r(i * 2) shouldBe xs(i) - r(i * 2 + 1) shouldBe xs(i) - } - } - } - - import scala.util.Try - - val bomb: Streaming[Int] = - Streaming.defer(sys.error("boom")) - - val dangerous: Streaming[Int] = - 1 %:: 2 %:: 3 %:: bomb - - val veryDangerous: Streaming[Int] = - 1 %:: bomb - - test("lazy uncons") { - veryDangerous.uncons.map(_._1) shouldBe Some(1) - } - - def isok[U](body: => U): Unit = - Try(body).isSuccess shouldBe true - - test("lazy map") { - isok(bomb.map(_ + 1)) - } - - test("lazy flatMap") { - isok(bomb.flatMap(n => Streaming(n, n))) - } - - test("lazy filter") { - isok(bomb.filter(_ > 10)) - } - - test("lazy foldRight") { - isok(bomb.foldRight(Now(0))((x, total) => total.map(_ + x))) - } - - test("lazy peekEmpty") { - bomb.peekEmpty shouldBe None - } - - test("lazy ++") { - isok(bomb ++ bomb) - } - - test("lazier ++") { - isok(bomb ++ Always(sys.error("ouch"): Streaming[Int])) - } - - test("lazy zip") { - isok(bomb zip dangerous) - isok(dangerous zip bomb) - } - - test("lazy zipWithIndex") { - isok(bomb.zipWithIndex) - } - - test("lazy izip") { - isok(bomb izip dangerous) - isok(dangerous izip bomb) - } - - test("lazy unzip") { - val bombBomb: Streaming[(Int, Int)] = bomb.map(n => (n, n)) - isok { val t: (Streaming[Int], Streaming[Int]) = bombBomb.unzip } - } - - test("lazy merge") { - isok(bomb merge bomb) - } - - test("lazy interleave") { - isok(bomb interleave bomb) - } - - test("lazy product") { - isok(bomb product bomb) - } - - test("lazy take") { - isok(bomb.take(10)) - isok(bomb.take(0)) - } - - test("take up to the last valid element"){ - isok(dangerous.take(3).toList) - } - - test("lazy drop") { - isok(bomb.drop(10)) - isok(bomb.drop(0)) - } - - test("lazy takeWhile") { - isok(bomb.takeWhile(_ < 10)) - } - - test("lazy dropWhile") { - isok(bomb.takeWhile(_ < 10)) - } - - test("lazy tails") { - isok(bomb.tails) - } -} From 9dc2c37b27b027024e571b412c2776db8a6a60ff Mon Sep 17 00:00:00 2001 From: Adelbert Chang Date: Wed, 2 Mar 2016 23:10:34 -0500 Subject: [PATCH 016/185] Add more MonadWriter combinators --- core/src/main/scala/cats/MonadWriter.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/MonadWriter.scala b/core/src/main/scala/cats/MonadWriter.scala index 7529117c84..3a19669636 100644 --- a/core/src/main/scala/cats/MonadWriter.scala +++ b/core/src/main/scala/cats/MonadWriter.scala @@ -11,8 +11,16 @@ trait MonadWriter[F[_], W] extends Monad[F] { /** Apply the effectful function to the accumulator */ def pass[A](fa: F[(A, W => W)]): F[A] - /** An effect that when run, logs w */ + /** Lift the log into the effect */ def tell(w: W): F[Unit] = writer(((), w)) + + /** Pair the value with an inspection of the accumulator */ + def listens[A, B](fa: F[A])(f: W => B): F[(A, B)] = + map(listen(fa)) { case (a, w) => (a, f(w)) } + + /** Modify the accumulator */ + def censor[A](fa: F[A])(f: W => W): F[A] = + flatMap(listen(fa)) { case (a, w) => writer((a, f(w))) } } object MonadWriter { From f8b5e295bf748fa0a5e51bfa04f9c41058a96870 Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Sat, 5 Mar 2016 06:28:47 +0900 Subject: [PATCH 017/185] add -P:scalajs:mapSourceURI option --- build.sbt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/build.sbt b/build.sbt index dc996b1051..c32b263d30 100644 --- a/build.sbt +++ b/build.sbt @@ -41,7 +41,19 @@ lazy val commonSettings = Seq( scalacOptions in (Compile, doc) := (scalacOptions in (Compile, doc)).value.filter(_ != "-Xfatal-warnings") ) ++ warnUnusedImport +lazy val tagName = Def.setting{ + s"v${if (releaseUseGlobalVersion.value) (version in ThisBuild).value else version.value}" +} + lazy val commonJsSettings = Seq( + scalacOptions += { + val tagOrHash = + if(isSnapshot.value) sys.process.Process("git rev-parse HEAD").lines_!.head + else tagName.value + val a = (baseDirectory in LocalRootProject).value.toURI.toString + val g = "https://raw.githubusercontent.com/typelevel/cats/" + tagOrHash + s"-P:scalajs:mapSourceURI:$a->$g/" + }, scalaJSStage in Global := FastOptStage, parallelExecution := false ) @@ -327,6 +339,7 @@ lazy val commonScalacOptions = Seq( lazy val sharedPublishSettings = Seq( releaseCrossBuild := true, + releaseTagName := tagName.value, releasePublishArtifactsAction := PgpKeys.publishSigned.value, publishMavenStyle := true, publishArtifact in Test := false, From 989d72857a9b3ec6c3fdad562a141be681c507e2 Mon Sep 17 00:00:00 2001 From: Dave Gurnell Date: Tue, 8 Mar 2016 22:28:07 +0000 Subject: [PATCH 018/185] Add .tell and .writer syntax for creating Writers. Fixes #920. --- core/src/main/scala/cats/syntax/all.scala | 1 + core/src/main/scala/cats/syntax/package.scala | 1 + core/src/main/scala/cats/syntax/writer.scala | 13 ++++++++++ .../test/scala/cats/tests/WriterTests.scala | 26 +++++++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 core/src/main/scala/cats/syntax/writer.scala create mode 100644 tests/src/test/scala/cats/tests/WriterTests.scala diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index 2a2f68faef..b99ed8785f 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -37,3 +37,4 @@ trait AllSyntax with XorSyntax with ValidatedSyntax with CoproductSyntax + with WriterSyntax diff --git a/core/src/main/scala/cats/syntax/package.scala b/core/src/main/scala/cats/syntax/package.scala index 154a9e94cf..26ec7fd4f5 100644 --- a/core/src/main/scala/cats/syntax/package.scala +++ b/core/src/main/scala/cats/syntax/package.scala @@ -35,4 +35,5 @@ package object syntax { object traverse extends TraverseSyntax object xor extends XorSyntax object validated extends ValidatedSyntax + object writer extends WriterSyntax } diff --git a/core/src/main/scala/cats/syntax/writer.scala b/core/src/main/scala/cats/syntax/writer.scala new file mode 100644 index 0000000000..41dab81a63 --- /dev/null +++ b/core/src/main/scala/cats/syntax/writer.scala @@ -0,0 +1,13 @@ +package cats +package syntax + +import cats.data.Writer + +trait WriterSyntax { + implicit def writerIdSyntax[A](a: A): WriterIdSyntax[A] = new WriterIdSyntax(a) +} + +final class WriterIdSyntax[A](val a: A) extends AnyVal { + def tell: Writer[A, Unit] = Writer(a, ()) + def writer[W](w: W): Writer[W, A] = Writer(w, a) +} diff --git a/tests/src/test/scala/cats/tests/WriterTests.scala b/tests/src/test/scala/cats/tests/WriterTests.scala new file mode 100644 index 0000000000..6b93c1ef72 --- /dev/null +++ b/tests/src/test/scala/cats/tests/WriterTests.scala @@ -0,0 +1,26 @@ +package cats +package tests + +import cats.data.Writer +import cats.laws.discipline.eq._ + +class WriterTests extends CatsSuite { + test("pure syntax creates a writer with an empty log"){ + forAll { (result: String) => + type Logged[A] = Writer[List[Int], A] + result.pure[Logged] should === (Writer(List.empty[Int], result)) + } + } + + test("tell syntax creates a writer with a unit result"){ + forAll { (log: List[Int]) => + log.tell should === (Writer(log, ())) + } + } + + test("writer syntax creates a writer with the specified result and log") { + forAll { (result: String, log: List[Int]) => + result.writer(log) should === (Writer(log, result)) + } + } +} From bd7fe87532446cc0ad913a1343a0587eb0739991 Mon Sep 17 00:00:00 2001 From: Tomas Mikula Date: Wed, 9 Mar 2016 20:51:47 -0500 Subject: [PATCH 019/185] Make Call.loop @tailrec optimized. --- core/src/main/scala/cats/Eval.scala | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/Eval.scala b/core/src/main/scala/cats/Eval.scala index dfe03f7d1e..867abe42b1 100644 --- a/core/src/main/scala/cats/Eval.scala +++ b/core/src/main/scala/cats/Eval.scala @@ -228,17 +228,23 @@ object Eval extends EvalInstances { object Call { /** Collapse the call stack for eager evaluations */ - private def loop[A](fa: Eval[A]): Eval[A] = fa match { + @tailrec private def loop[A](fa: Eval[A]): Eval[A] = fa match { case call: Eval.Call[A] => loop(call.thunk()) case compute: Eval.Compute[A] => new Eval.Compute[A] { type Start = compute.Start val start: () => Eval[Start] = () => compute.start() - val run: Start => Eval[A] = s => loop(compute.run(s)) + val run: Start => Eval[A] = s => loop1(compute.run(s)) } case other => other } + + /** + * Alias for loop that can be called in a non-tail position + * from an otherwise tailrec-optimized loop. + */ + private def loop1[A](fa: Eval[A]): Eval[A] = loop(fa) } /** From 926aed69d3087995c7d4864b7ac05a04a0b4c9e9 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 18 Jun 2015 22:39:28 -0400 Subject: [PATCH 020/185] Add foldM --- core/src/main/scala/cats/Foldable.scala | 11 +++++++++++ tests/src/test/scala/cats/tests/FoldableTests.scala | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index 676c80f5ac..f992b068b1 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -76,6 +76,17 @@ import simulacrum.typeclass def foldMap[A, B](fa: F[A])(f: A => B)(implicit B: Monoid[B]): B = foldLeft(fa, B.empty)((b, a) => B.combine(b, f(a))) + /** + * Left associative monadic folding on `F`. + */ + def foldM[G[_], A, B](fa: F[A], z: B)(f: (B, A) => G[B]) + (implicit G: Monad[G]): G[B] = + partialFold[A, B => G[B]](fa)({a: A => + Fold.Continue({ b => + (w: B) => G.flatMap(f(w, a))(b) + }) + }).complete(Lazy { b: B => G.pure(b) })(z) + /** * Traverse `F[A]` using `Applicative[G]`. * diff --git a/tests/src/test/scala/cats/tests/FoldableTests.scala b/tests/src/test/scala/cats/tests/FoldableTests.scala index 8d4efefd24..a4976f60a7 100644 --- a/tests/src/test/scala/cats/tests/FoldableTests.scala +++ b/tests/src/test/scala/cats/tests/FoldableTests.scala @@ -63,6 +63,12 @@ class FoldableTestsAdditional extends CatsSuite { // more basic checks val names = List("Aaron", "Betty", "Calvin", "Deirdra") F.foldMap(names)(_.length) should === (names.map(_.length).sum) + val sumM = F.foldM(names, "") { (acc, x) => (Some(acc + x): Option[String]) } + assert(sumM == Some("AaronBettyCalvinDeirdra")) + val notCalvin = F.foldM(names, "") { (acc, x) => + if (x == "Calvin") (None: Option[String]) + else (Some(acc + x): Option[String]) } + assert(notCalvin == None) // test trampolining val large = (1 to 10000).toList From 651046d4c661336f38ea204be7784445c78c83e1 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 2 Jan 2016 23:14:12 -0500 Subject: [PATCH 021/185] Rewrote to use foldRight --- core/src/main/scala/cats/Foldable.scala | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index f992b068b1..1b0cb490cb 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -79,13 +79,15 @@ import simulacrum.typeclass /** * Left associative monadic folding on `F`. */ - def foldM[G[_], A, B](fa: F[A], z: B)(f: (B, A) => G[B]) - (implicit G: Monad[G]): G[B] = - partialFold[A, B => G[B]](fa)({a: A => - Fold.Continue({ b => - (w: B) => G.flatMap(f(w, a))(b) - }) - }).complete(Lazy { b: B => G.pure(b) })(z) + def foldM[G[_], A, B](fa: F[A], z: Eval[B])(f: (B, A) => G[B]) + (implicit G: Monad[G]): Eval[G[B]] = + foldRight[A, B => G[B]](fa, Eval.later { G.pure _ }) { (a: A, acc: Eval[B => G[B]]) => + acc map { (k: B => G[B]) => + w: B => G.flatMap(f(w, a))(k) + } + } flatMap { acc => + z map { b: B => acc(b) } + } /** * Traverse `F[A]` using `Applicative[G]`. From 6bbb05c3b4684d01416dd4101a0565428fde0884 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 2 Jan 2016 23:21:29 -0500 Subject: [PATCH 022/185] Fix test --- tests/src/test/scala/cats/tests/FoldableTests.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/test/scala/cats/tests/FoldableTests.scala b/tests/src/test/scala/cats/tests/FoldableTests.scala index a4976f60a7..de91808d69 100644 --- a/tests/src/test/scala/cats/tests/FoldableTests.scala +++ b/tests/src/test/scala/cats/tests/FoldableTests.scala @@ -63,9 +63,9 @@ class FoldableTestsAdditional extends CatsSuite { // more basic checks val names = List("Aaron", "Betty", "Calvin", "Deirdra") F.foldMap(names)(_.length) should === (names.map(_.length).sum) - val sumM = F.foldM(names, "") { (acc, x) => (Some(acc + x): Option[String]) } + val sumM = F.foldM(names, Eval.later { "" }) { (acc, x) => (Some(acc + x): Option[String]) } assert(sumM == Some("AaronBettyCalvinDeirdra")) - val notCalvin = F.foldM(names, "") { (acc, x) => + val notCalvin = F.foldM(names, Eval.later { "" }) { (acc, x) => if (x == "Calvin") (None: Option[String]) else (Some(acc + x): Option[String]) } assert(notCalvin == None) From 5146e59cb7e75deb0e31432cc3e90ca0cd5943f1 Mon Sep 17 00:00:00 2001 From: Tomas Mikula Date: Thu, 10 Mar 2016 15:29:14 -0500 Subject: [PATCH 023/185] Fix stack-safety of foldM. --- core/src/main/scala/cats/Foldable.scala | 11 ++--------- .../src/test/scala/cats/tests/FoldableTests.scala | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index 1b0cb490cb..aa83ebf49e 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -79,15 +79,8 @@ import simulacrum.typeclass /** * Left associative monadic folding on `F`. */ - def foldM[G[_], A, B](fa: F[A], z: Eval[B])(f: (B, A) => G[B]) - (implicit G: Monad[G]): Eval[G[B]] = - foldRight[A, B => G[B]](fa, Eval.later { G.pure _ }) { (a: A, acc: Eval[B => G[B]]) => - acc map { (k: B => G[B]) => - w: B => G.flatMap(f(w, a))(k) - } - } flatMap { acc => - z map { b: B => acc(b) } - } + def foldM[G[_], A, B](fa: F[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] = + foldLeft(fa, G.pure(z))((gb, a) => G.flatMap(gb)(f(_, a))) /** * Traverse `F[A]` using `Applicative[G]`. diff --git a/tests/src/test/scala/cats/tests/FoldableTests.scala b/tests/src/test/scala/cats/tests/FoldableTests.scala index de91808d69..fda51a443a 100644 --- a/tests/src/test/scala/cats/tests/FoldableTests.scala +++ b/tests/src/test/scala/cats/tests/FoldableTests.scala @@ -63,9 +63,9 @@ class FoldableTestsAdditional extends CatsSuite { // more basic checks val names = List("Aaron", "Betty", "Calvin", "Deirdra") F.foldMap(names)(_.length) should === (names.map(_.length).sum) - val sumM = F.foldM(names, Eval.later { "" }) { (acc, x) => (Some(acc + x): Option[String]) } + val sumM = F.foldM(names, "") { (acc, x) => (Some(acc + x): Option[String]) } assert(sumM == Some("AaronBettyCalvinDeirdra")) - val notCalvin = F.foldM(names, Eval.later { "" }) { (acc, x) => + val notCalvin = F.foldM(names, "") { (acc, x) => if (x == "Calvin") (None: Option[String]) else (Some(acc + x): Option[String]) } assert(notCalvin == None) @@ -79,6 +79,16 @@ class FoldableTestsAdditional extends CatsSuite { larger.value should === (large.map(_ + 1)) } + test("Foldable[List].foldM stack safety") { + def nonzero(acc: Long, x: Long): Option[Long] = + if (x == 0) None else Some(acc + x) + + val n = 100000L + val expected = n*(n+1)/2 + val actual = Foldable[List].foldM((1L to n).toList, 0L)(nonzero) + assert(actual.get == expected) + } + test("Foldable[Stream]") { val F = Foldable[Stream] From 4dae55be18655de03b2ff98ff40fa0ab55a936c0 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 10 Mar 2016 20:25:15 -0800 Subject: [PATCH 024/185] Initial commit for ISSUE-607. Wondering if typeclass instances for Show could be automatically derived for Tuples and case classes (see: https://github.com/milessabin/kittens), but ignored that possibility for now and just wrote an instance for Tuple2 to get this to work. --- core/src/main/scala/cats/data/WriterT.scala | 6 +++++ core/src/main/scala/cats/std/tuple.scala | 9 ++++++++ .../test/scala/cats/tests/TupleTests.scala | 23 +++++++++++++++++++ .../test/scala/cats/tests/WriterTTests.scala | 5 ++++ 4 files changed, 43 insertions(+) diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 515d63f5a5..5cf55f8fd0 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -44,6 +44,8 @@ final case class WriterT[F[_], L, V](run: F[(L, V)]) { def reset(implicit monoidL: Monoid[L], functorF: Functor[F]): WriterT[F, L, V] = mapWritten(_ => monoidL.empty) + + def show(implicit F: Show[F[(L, V)]]): String = F.show(run) } object WriterT extends WriterTInstances with WriterTFunctions @@ -68,6 +70,10 @@ private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { def liftT[A](ma: M[A]): WriterT[M, W, A] = WriterT(M.map(ma)((W.empty, _))) } + + implicit def writerTShow[F[_], L, V](implicit F: Show[F[(L, V)]]): Show[WriterT[F, L, V]] = new Show[WriterT[F, L, V]] { + override def show(f: WriterT[F, L, V]): String = f.show + } } private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 { diff --git a/core/src/main/scala/cats/std/tuple.scala b/core/src/main/scala/cats/std/tuple.scala index cbf9f8d075..e0bc33dd20 100644 --- a/core/src/main/scala/cats/std/tuple.scala +++ b/core/src/main/scala/cats/std/tuple.scala @@ -15,4 +15,13 @@ sealed trait Tuple2Instances { def bifoldRight[A, B, C](fab: (A, B), c: Eval[C])(f: (A, Eval[C]) => Eval[C], g: (B, Eval[C]) => Eval[C]): Eval[C] = g(fab._2, f(fab._1, c)) } + + implicit def tuple2Show[A, B](implicit aShow: Show[A], bShow: Show[B]): Show[(A, B)] = new Show[(A, B)] { + override def show(f: (A, B)): String = { + // Note: the toString method for Tuple2 does not always include a space after the comma. For example, + // ("foo", "").toString returns "(foo,)" instead of "(foo, )" and ("","쾷").toString returns "(,쾷)". This + // implementation avoids this question of consistency by never including a space. + s"(${aShow.show(f._1)},${bShow.show(f._2)})" + } + } } diff --git a/tests/src/test/scala/cats/tests/TupleTests.scala b/tests/src/test/scala/cats/tests/TupleTests.scala index 419fabc698..4e346d82a4 100644 --- a/tests/src/test/scala/cats/tests/TupleTests.scala +++ b/tests/src/test/scala/cats/tests/TupleTests.scala @@ -7,4 +7,27 @@ import cats.laws.discipline.eq.tuple2Eq class TupleTests extends CatsSuite { checkAll("Tuple2", BitraverseTests[Tuple2].bitraverse[Option, Int, Int, Int, String, String, String]) checkAll("Bitraverse[Tuple2]", SerializableTests.serializable(Bitraverse[Tuple2])) + + test("show") { + (1, 2).show should === ("(1,2)") + + forAll { fs: (String, String) => + fs.show should === (s"(${fs._1},${fs._2})") + } + + // Provide some "non-standard" Show instances to make sure the tuple2 is actually use the Show instances for the + // relevant types instead of blindly calling toString + case class Foo(x: Int) + implicit val fooShow: Show[Foo] = new Show[Foo] { + override def show(f: Foo): String = s"foo.x = ${f.x}" + } + case class Bar(y: Int) + implicit val barShow: Show[Bar] = new Show[Bar] { + override def show(f: Bar): String = s"bar.y = ${f.y}" + } + + val foo = Foo(1) + val bar = Bar(2) + (foo, bar).show should === (s"(${fooShow.show(foo)},${barShow.show(bar)})") + } } diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index 7d8e874f9b..a106e05ae3 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -62,6 +62,11 @@ class WriterTTests extends CatsSuite { } } + test("show") { + val writerT: WriterT[Id, List[String], String] = WriterT.put("foo")(List("Some log message")) + writerT.show should === ("(List(Some log message),foo)") + } + { // F has a SemigroupK implicit val F: SemigroupK[ListWrapper] = ListWrapper.semigroupK From ba6b93dea6b0b9ad71a78062f4b4fafc88b08136 Mon Sep 17 00:00:00 2001 From: Matt Martin Date: Thu, 10 Mar 2016 20:55:06 -0800 Subject: [PATCH 025/185] Removing superfluous comment. --- core/src/main/scala/cats/std/tuple.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/main/scala/cats/std/tuple.scala b/core/src/main/scala/cats/std/tuple.scala index e0bc33dd20..a690245a81 100644 --- a/core/src/main/scala/cats/std/tuple.scala +++ b/core/src/main/scala/cats/std/tuple.scala @@ -18,9 +18,6 @@ sealed trait Tuple2Instances { implicit def tuple2Show[A, B](implicit aShow: Show[A], bShow: Show[B]): Show[(A, B)] = new Show[(A, B)] { override def show(f: (A, B)): String = { - // Note: the toString method for Tuple2 does not always include a space after the comma. For example, - // ("foo", "").toString returns "(foo,)" instead of "(foo, )" and ("","쾷").toString returns "(,쾷)". This - // implementation avoids this question of consistency by never including a space. s"(${aShow.show(f._1)},${bShow.show(f._2)})" } } From 42a0583ce30379aecea24374e8e48c34739f2e9a Mon Sep 17 00:00:00 2001 From: Matt Martin Date: Thu, 10 Mar 2016 20:58:41 -0800 Subject: [PATCH 026/185] Simplifying TupleTests --- tests/src/test/scala/cats/tests/TupleTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/test/scala/cats/tests/TupleTests.scala b/tests/src/test/scala/cats/tests/TupleTests.scala index 4e346d82a4..dae6c08429 100644 --- a/tests/src/test/scala/cats/tests/TupleTests.scala +++ b/tests/src/test/scala/cats/tests/TupleTests.scala @@ -12,7 +12,7 @@ class TupleTests extends CatsSuite { (1, 2).show should === ("(1,2)") forAll { fs: (String, String) => - fs.show should === (s"(${fs._1},${fs._2})") + fs.show should === (fs.toString) } // Provide some "non-standard" Show instances to make sure the tuple2 is actually use the Show instances for the From c4b4134246ffa500070c5098ad38f2a3f2fe4867 Mon Sep 17 00:00:00 2001 From: ImLiar Date: Fri, 11 Mar 2016 00:11:55 +0300 Subject: [PATCH 027/185] "ensure" method for XorT --- .gitignore | 1 + core/src/main/scala/cats/data/Xor.scala | 4 ++-- core/src/main/scala/cats/data/XorT.scala | 2 ++ .../src/test/scala/cats/tests/XorTTests.scala | 24 +++++++++++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 4c19d3d8e4..4581b4d5be 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ TAGS .sbtrc *.sublime-project *.sublime-workspace +tests.iml diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index b086575a13..ef778bc37a 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -59,8 +59,8 @@ sealed abstract class Xor[+A, +B] extends Product with Serializable { def exists(f: B => Boolean): Boolean = fold(_ => false, f) - def ensure[AA >: A](ifLeft: => AA)(f: B => Boolean): AA Xor B = - fold(_ => this, b => if (f(b)) this else Xor.Left(ifLeft)) + def ensure[AA >: A](onFailure: => AA)(f: B => Boolean): AA Xor B = + fold(_ => this, b => if (f(b)) this else Xor.Left(onFailure)) def toIor: A Ior B = fold(Ior.left, Ior.right) diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index d7ff05bf80..71d4a97c28 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -51,6 +51,8 @@ final case class XorT[F[_], A, B](value: F[A Xor B]) { def exists(f: B => Boolean)(implicit F: Functor[F]): F[Boolean] = F.map(value)(_.exists(f)) + def ensure[AA >: A](onFailure: => AA)(f: B => Boolean)(implicit F: Functor[F]): XorT[F, AA, B] = XorT(F.map(value)(_.ensure(onFailure)(f))) + def toEither(implicit F: Functor[F]): F[Either[A, B]] = F.map(value)(_.toEither) def toOption(implicit F: Functor[F]): OptionT[F, B] = OptionT(F.map(value)(_.toOption)) diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index f75b14a713..7ea4e9b343 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -251,4 +251,28 @@ class XorTTests extends CatsSuite { x.toEither.map(_.right.toOption) should === (x.toOption.value) } } + + test("ensure on left is identity") { + forAll { (x: XorT[Id, String, Int], s: String, p: Int => Boolean) => + if (x.isLeft) { + x.ensure(s)(p) should === (x) + } + } + } + + test("ensure on right is identity if predicate satisfied") { + forAll { (x: XorT[Id, String, Int], s: String, p: Int => Boolean) => + if (x.isRight && p(x getOrElse 0)) { + x.ensure(s)(p) should === (x) + } + } + } + + test("ensure should fail if predicate not satisfied") { + forAll { (x: XorT[Id, String, Int], s: String, p: Int => Boolean) => + if (x.isRight && !p(x getOrElse 0)) { + x.ensure(s)(p) should === (XorT.left[Id, String, Int](s)) + } + } + } } From 2a001aff3596300c496e0e52cc34caea0fd4785f Mon Sep 17 00:00:00 2001 From: Alexandru Nedelcu Date: Fri, 18 Mar 2016 12:41:23 +0200 Subject: [PATCH 028/185] Issue #934 - Make Eval covariant --- core/src/main/scala/cats/Eval.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/Eval.scala b/core/src/main/scala/cats/Eval.scala index 867abe42b1..b0970cc808 100644 --- a/core/src/main/scala/cats/Eval.scala +++ b/core/src/main/scala/cats/Eval.scala @@ -33,7 +33,7 @@ import cats.syntax.all._ * Eval instance -- this can defeat the trampolining and lead to stack * overflows. */ -sealed abstract class Eval[A] extends Serializable { self => +sealed abstract class Eval[+A] extends Serializable { self => /** * Evaluate the computation and return an A value. @@ -279,7 +279,7 @@ object Eval extends EvalInstances { cc.start().asInstanceOf[L], cc.run.asInstanceOf[C] :: c.run.asInstanceOf[C] :: fs) case xx => - loop(c.run(xx.value).asInstanceOf[L], fs) + loop(c.run(xx.value), fs) } case x => fs match { @@ -350,7 +350,7 @@ trait EvalMonoid[A] extends Monoid[Eval[A]] with EvalSemigroup[A] { trait EvalGroup[A] extends Group[Eval[A]] with EvalMonoid[A] { implicit def algebra: Group[A] def inverse(lx: Eval[A]): Eval[A] = - lx.map(_.inverse) + lx.map(_.inverse()) override def remove(lx: Eval[A], ly: Eval[A]): Eval[A] = for { x <- lx; y <- ly } yield x |-| y } From 05e2c66e3b3307b8bceb8ad3a4d491d29a2028d8 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 17 Mar 2016 16:17:08 -0700 Subject: [PATCH 029/185] Onward to scala 2.11.8. Unused import detection has improved, leading to a number of deletions. --- .travis.yml | 2 +- .../scala/cats/bench/TrampolineBench.scala | 2 -- build.sbt | 10 ++++---- core/src/main/scala/cats/MonadError.scala | 2 -- core/src/main/scala/cats/SemigroupK.scala | 4 ++-- core/src/main/scala/cats/data/Xor.scala | 2 -- core/src/main/scala/cats/std/future.scala | 1 - .../test/scala/cats/tests/FutureTests.scala | 14 +++++++---- .../test/scala/cats/tests/FutureTests.scala | 4 ++-- .../scala/cats/laws/SerializableLaws.scala | 2 +- .../main/scala/cats/laws/TraverseLaws.scala | 1 - .../discipline/ApplicativeErrorTests.scala | 1 - .../laws/discipline/MonadErrorTests.scala | 1 - .../laws/discipline/MonadStateTests.scala | 1 - project/plugins.sbt | 24 +++++++++---------- scripts/travis-publish.sh | 2 +- .../src/test/scala/cats/tests/CatsSuite.scala | 2 +- .../test/scala/cats/tests/ComposeTests.scala | 3 +-- .../test/scala/cats/tests/CoyonedaTests.scala | 3 +-- .../test/scala/cats/tests/FoldableTests.scala | 3 +-- .../cats/tests/FreeApplicativeTests.scala | 2 +- .../src/test/scala/cats/tests/FreeTests.scala | 2 +- .../src/test/scala/cats/tests/FuncTests.scala | 4 ++-- .../test/scala/cats/tests/FunctionTests.scala | 2 -- tests/src/test/scala/cats/tests/IdTests.scala | 1 - .../test/scala/cats/tests/InjectTests.scala | 1 - .../src/test/scala/cats/tests/IorTests.scala | 1 - .../test/scala/cats/tests/ListWrapper.scala | 2 -- .../test/scala/cats/tests/OneAndTests.scala | 4 +--- .../test/scala/cats/tests/OptionTTests.scala | 7 +++--- .../src/test/scala/cats/tests/ProdTests.scala | 1 - .../test/scala/cats/tests/StateTTests.scala | 4 ++-- .../test/scala/cats/tests/SyntaxTests.scala | 6 ----- .../scala/cats/tests/ValidatedTests.scala | 1 - .../test/scala/cats/tests/WriterTTests.scala | 1 - .../src/test/scala/cats/tests/XorTests.scala | 2 +- 36 files changed, 49 insertions(+), 76 deletions(-) diff --git a/.travis.yml b/.travis.yml index 08e2ade2c3..30e14c9509 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ git: scala: - 2.10.6 -- 2.11.7 +- 2.11.8 script: - scripts/travis-publish.sh diff --git a/bench/src/main/scala/cats/bench/TrampolineBench.scala b/bench/src/main/scala/cats/bench/TrampolineBench.scala index 2efbe0f1eb..074dbe7e9a 100644 --- a/bench/src/main/scala/cats/bench/TrampolineBench.scala +++ b/bench/src/main/scala/cats/bench/TrampolineBench.scala @@ -6,8 +6,6 @@ import cats._ import cats.implicits._ import cats.free.Trampoline -import scala.util.control.TailCalls - @State(Scope.Benchmark) class TrampolineBench { diff --git a/build.sbt b/build.sbt index c32b263d30..51e15edba7 100644 --- a/build.sbt +++ b/build.sbt @@ -14,8 +14,8 @@ lazy val scoverageSettings = Seq( lazy val buildSettings = Seq( organization := "org.typelevel", - scalaVersion := "2.11.7", - crossScalaVersions := Seq("2.10.6", "2.11.7") + scalaVersion := "2.11.8", + crossScalaVersions := Seq("2.10.6", "2.11.8") ) lazy val catsDoctestSettings = Seq( @@ -34,7 +34,7 @@ lazy val commonSettings = Seq( "org.spire-math" %%% "algebra" % "0.3.1", "org.spire-math" %%% "algebra-std" % "0.3.1", "org.typelevel" %%% "machinist" % "0.4.1", - compilerPlugin("org.scalamacros" %% "paradise" % "2.1.0-M5" cross CrossVersion.full), + compilerPlugin("org.scalamacros" %% "paradise" % "2.1.0" cross CrossVersion.full), compilerPlugin("org.spire-math" %% "kind-projector" % "0.6.3") ), parallelExecution in Test := false, @@ -280,7 +280,7 @@ addCommandAlias("validateJS", ";macrosJS/compile;coreJS/compile;lawsJS/compile;t addCommandAlias("validate", ";validateJS;validateJVM") //////////////////////////////////////////////////////////////////////////////////////////////////// -// Base Build Settings - Should not need to edit below this line. +// Base Build Settings - Should not need to edit below this line. // These settings could also come from another file or a plugin. // The only issue if coming from a plugin is that the Macro lib versions // are hard coded, so an overided facility would be required. @@ -352,7 +352,7 @@ lazy val sharedPublishSettings = Seq( Some("Releases" at nexus + "service/local/staging/deploy/maven2") } ) - + lazy val sharedReleaseProcess = Seq( releaseProcess := Seq[ReleaseStep]( checkSnapshotDependencies, diff --git a/core/src/main/scala/cats/MonadError.scala b/core/src/main/scala/cats/MonadError.scala index 0284edcab5..763b0b68ff 100644 --- a/core/src/main/scala/cats/MonadError.scala +++ b/core/src/main/scala/cats/MonadError.scala @@ -1,7 +1,5 @@ package cats -import cats.data.{Xor, XorT} - /** * A monad that also allows you to raise and or handle an error value. * diff --git a/core/src/main/scala/cats/SemigroupK.scala b/core/src/main/scala/cats/SemigroupK.scala index 4372ea0e26..113228cb3b 100644 --- a/core/src/main/scala/cats/SemigroupK.scala +++ b/core/src/main/scala/cats/SemigroupK.scala @@ -1,6 +1,6 @@ package cats -import simulacrum.{op, typeclass} +import simulacrum.typeclass /** * SemigroupK is a universal semigroup which operates on kinds. @@ -25,7 +25,7 @@ import simulacrum.{op, typeclass} /** * Combine two F[A] values. */ - @op("<+>", alias=true) + @simulacrum.op("<+>", alias=true) def combineK[A](x: F[A], y: F[A]): F[A] /** diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index ef778bc37a..7b6cc3bf27 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -1,8 +1,6 @@ package cats package data -import cats.functor.Bifunctor - import scala.reflect.ClassTag import scala.util.{Failure, Success, Try} diff --git a/core/src/main/scala/cats/std/future.scala b/core/src/main/scala/cats/std/future.scala index fb544d0941..3c74ddc723 100644 --- a/core/src/main/scala/cats/std/future.scala +++ b/core/src/main/scala/cats/std/future.scala @@ -6,7 +6,6 @@ import cats.data.Xor import scala.util.control.NonFatal import scala.concurrent.{ExecutionContext, Future} -import scala.concurrent.duration.FiniteDuration trait FutureInstances extends FutureInstances1 { diff --git a/js/src/test/scala/cats/tests/FutureTests.scala b/js/src/test/scala/cats/tests/FutureTests.scala index aff4973654..0af2e907e0 100644 --- a/js/src/test/scala/cats/tests/FutureTests.scala +++ b/js/src/test/scala/cats/tests/FutureTests.scala @@ -6,17 +6,23 @@ import cats.data.Xor import cats.laws.discipline._ import cats.laws.discipline.eq.tuple3Eq import cats.js.std.Await -import cats.js.std.future.{futureEq, futureComonad} +import cats.js.std.future.futureComonad import cats.tests.CatsSuite -import scala.concurrent.{ExecutionContext, Future} +import scala.concurrent.Future import scala.concurrent.duration._ -import scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow - import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary.arbitrary +// https://issues.scala-lang.org/browse/SI-7934 +@deprecated("", "") +class DeprecatedForwarder { + implicit def runNow = scala.scalajs.concurrent.JSExecutionContext.Implicits.runNow +} +object DeprecatedForwarder extends DeprecatedForwarder +import DeprecatedForwarder.runNow + class FutureTests extends CatsSuite { val timeout = 3.seconds diff --git a/jvm/src/test/scala/cats/tests/FutureTests.scala b/jvm/src/test/scala/cats/tests/FutureTests.scala index a19ee09819..21a73f9519 100644 --- a/jvm/src/test/scala/cats/tests/FutureTests.scala +++ b/jvm/src/test/scala/cats/tests/FutureTests.scala @@ -5,10 +5,10 @@ package tests import cats.data.Xor import cats.laws.discipline._ import cats.laws.discipline.eq.tuple3Eq -import cats.jvm.std.future.{futureEq, futureComonad} +import cats.jvm.std.future.futureComonad import cats.tests.CatsSuite -import scala.concurrent.{Await, ExecutionContext, Future} +import scala.concurrent.{Await, Future} import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits.global diff --git a/laws/src/main/scala/cats/laws/SerializableLaws.scala b/laws/src/main/scala/cats/laws/SerializableLaws.scala index 38d3284fc8..38de7aa198 100644 --- a/laws/src/main/scala/cats/laws/SerializableLaws.scala +++ b/laws/src/main/scala/cats/laws/SerializableLaws.scala @@ -2,7 +2,7 @@ package cats package laws import org.scalacheck.Prop -import org.scalacheck.Prop.{ Exception, False, Proof, Result } +import org.scalacheck.Prop.{ Exception, Proof, Result } import catalysts.Platform diff --git a/laws/src/main/scala/cats/laws/TraverseLaws.scala b/laws/src/main/scala/cats/laws/TraverseLaws.scala index ffc27ef393..9acbbfe11c 100644 --- a/laws/src/main/scala/cats/laws/TraverseLaws.scala +++ b/laws/src/main/scala/cats/laws/TraverseLaws.scala @@ -2,7 +2,6 @@ package cats package laws import cats.Id -import cats.arrow.Compose import cats.data.Const import cats.syntax.traverse._ import cats.syntax.foldable._ diff --git a/laws/src/main/scala/cats/laws/discipline/ApplicativeErrorTests.scala b/laws/src/main/scala/cats/laws/discipline/ApplicativeErrorTests.scala index 1360866cd9..036e2a8469 100644 --- a/laws/src/main/scala/cats/laws/discipline/ApplicativeErrorTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/ApplicativeErrorTests.scala @@ -5,7 +5,6 @@ package discipline import cats.data.{ Xor, XorT } import cats.laws.discipline.CartesianTests.Isomorphisms import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq.unitEq import org.scalacheck.{Arbitrary, Prop} import org.scalacheck.Prop.forAll diff --git a/laws/src/main/scala/cats/laws/discipline/MonadErrorTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadErrorTests.scala index 60993ccfcf..732159043b 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonadErrorTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonadErrorTests.scala @@ -4,7 +4,6 @@ package discipline import cats.data.{ Xor, XorT } import cats.laws.discipline.CartesianTests.Isomorphisms -import cats.laws.discipline.eq.unitEq import org.scalacheck.{Arbitrary, Prop} import org.scalacheck.Prop.forAll diff --git a/laws/src/main/scala/cats/laws/discipline/MonadStateTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadStateTests.scala index db8454098b..c27d93d7ac 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonadStateTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonadStateTests.scala @@ -3,7 +3,6 @@ package laws package discipline import cats.laws.discipline.CartesianTests.Isomorphisms -import eq.unitEq import org.scalacheck.{Arbitrary, Prop} import org.scalacheck.Prop.forAll diff --git a/project/plugins.sbt b/project/plugins.sbt index 4e23acefbb..4e6d3041c1 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,13 +1,13 @@ -addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.3.2") -addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.0") -addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") -addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.3") -addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.8.1") -addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.0") -addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.3") +addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.3.2") +addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.0") +addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") +addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.3") +addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.8.1") +addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.0") +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.3") addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0") -addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.2.0") -addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.4") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.5") -addSbtPlugin("com.github.tkawachi" % "sbt-doctest" % "0.3.5") -addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.2.0") +addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.4") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.7") +addSbtPlugin("com.github.tkawachi" % "sbt-doctest" % "0.3.5") +addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1") diff --git a/scripts/travis-publish.sh b/scripts/travis-publish.sh index e1aab9727c..360f11eda4 100755 --- a/scripts/travis-publish.sh +++ b/scripts/travis-publish.sh @@ -20,7 +20,7 @@ export publish_cmd="publishLocal" if [[ $TRAVIS_PULL_REQUEST == "false" && $TRAVIS_BRANCH == "master" && $(cat version.sbt) =~ "-SNAPSHOT" ]]; then export publish_cmd="publish gitSnapshots publish" # temporarily disable to stabilize travis - #if [[ $TRAVIS_SCALA_VERSION = "2.11.7" ]]; then + #if [[ $TRAVIS_SCALA_VERSION = "2.11.8" ]]; then # export publish_cmd="$publish_cmd ghpagesPushSite" #fi fi diff --git a/tests/src/test/scala/cats/tests/CatsSuite.scala b/tests/src/test/scala/cats/tests/CatsSuite.scala index 8594d8668f..6672095eb9 100644 --- a/tests/src/test/scala/cats/tests/CatsSuite.scala +++ b/tests/src/test/scala/cats/tests/CatsSuite.scala @@ -7,7 +7,7 @@ import cats.std.AllInstances import cats.syntax.{AllSyntax, EqOps} import org.scalactic.anyvals.{PosZDouble, PosInt} -import org.scalatest.{FunSuite, PropSpec, Matchers} +import org.scalatest.{FunSuite, Matchers} import org.scalatest.prop.{Configuration, GeneratorDrivenPropertyChecks} import org.typelevel.discipline.scalatest.Discipline diff --git a/tests/src/test/scala/cats/tests/ComposeTests.scala b/tests/src/test/scala/cats/tests/ComposeTests.scala index 4ba655529c..88fcbe3718 100644 --- a/tests/src/test/scala/cats/tests/ComposeTests.scala +++ b/tests/src/test/scala/cats/tests/ComposeTests.scala @@ -1,9 +1,8 @@ package cats package tests -import cats.data.{ NonEmptyList, NonEmptyVector, OneAnd } +import cats.data.{ NonEmptyList, NonEmptyVector } import cats.laws.discipline.{ AlternativeTests, ApplicativeTests, FoldableTests, CartesianTests, MonoidKTests, SemigroupKTests, arbitrary, eq }, arbitrary._, eq._ -import org.scalacheck.Arbitrary class ComposeTests extends CatsSuite { // we have a lot of generated lists of lists in these tests. We have to tell diff --git a/tests/src/test/scala/cats/tests/CoyonedaTests.scala b/tests/src/test/scala/cats/tests/CoyonedaTests.scala index 26b517e22a..1c9cbe3e90 100644 --- a/tests/src/test/scala/cats/tests/CoyonedaTests.scala +++ b/tests/src/test/scala/cats/tests/CoyonedaTests.scala @@ -5,8 +5,7 @@ import cats.arrow.NaturalTransformation import cats.free.Coyoneda import cats.laws.discipline.{FunctorTests, SerializableTests} -import org.scalacheck.{Arbitrary, Gen} -import org.scalacheck.Arbitrary.arbitrary +import org.scalacheck.Arbitrary class CoyonedaTests extends CatsSuite { implicit def coyonedaArbitrary[F[_] : Functor, A : Arbitrary](implicit F: Arbitrary[F[A]]): Arbitrary[Coyoneda[F, A]] = diff --git a/tests/src/test/scala/cats/tests/FoldableTests.scala b/tests/src/test/scala/cats/tests/FoldableTests.scala index fda51a443a..512edaf9e3 100644 --- a/tests/src/test/scala/cats/tests/FoldableTests.scala +++ b/tests/src/test/scala/cats/tests/FoldableTests.scala @@ -2,8 +2,7 @@ package cats package tests import org.scalatest.prop.PropertyChecks -import org.scalacheck.{Arbitrary, Gen} -import org.scalacheck.Arbitrary.arbitrary +import org.scalacheck.Arbitrary import cats.std.all._ diff --git a/tests/src/test/scala/cats/tests/FreeApplicativeTests.scala b/tests/src/test/scala/cats/tests/FreeApplicativeTests.scala index 2a27e9a451..c8f6dd8861 100644 --- a/tests/src/test/scala/cats/tests/FreeApplicativeTests.scala +++ b/tests/src/test/scala/cats/tests/FreeApplicativeTests.scala @@ -5,7 +5,7 @@ import cats.arrow.NaturalTransformation import cats.free.FreeApplicative import cats.laws.discipline.{CartesianTests, ApplicativeTests, SerializableTests} import cats.laws.discipline.eq.{tuple3Eq, tuple2Eq} -import cats.data.{Const, State} +import cats.data.State import org.scalacheck.{Arbitrary, Gen} diff --git a/tests/src/test/scala/cats/tests/FreeTests.scala b/tests/src/test/scala/cats/tests/FreeTests.scala index ecb4876f8f..56fe308ac2 100644 --- a/tests/src/test/scala/cats/tests/FreeTests.scala +++ b/tests/src/test/scala/cats/tests/FreeTests.scala @@ -7,7 +7,7 @@ import cats.laws.discipline.{CartesianTests, MonadTests, SerializableTests} import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary.function0Arbitrary import org.scalacheck.{Arbitrary, Gen} -import Arbitrary.{arbitrary, arbFunction1} +import Arbitrary.arbFunction1 class FreeTests extends CatsSuite { import FreeTests._ diff --git a/tests/src/test/scala/cats/tests/FuncTests.scala b/tests/src/test/scala/cats/tests/FuncTests.scala index 906e127da3..0a2083bf7c 100644 --- a/tests/src/test/scala/cats/tests/FuncTests.scala +++ b/tests/src/test/scala/cats/tests/FuncTests.scala @@ -1,8 +1,8 @@ package cats package tests -import cats.data.{ Func, AppFunc, Const } -import Func.{ appFunc, appFuncU } +import cats.data.{ Func, AppFunc } +import Func.appFunc import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ import org.scalacheck.Arbitrary diff --git a/tests/src/test/scala/cats/tests/FunctionTests.scala b/tests/src/test/scala/cats/tests/FunctionTests.scala index b5aa74c6a2..7daf31c445 100644 --- a/tests/src/test/scala/cats/tests/FunctionTests.scala +++ b/tests/src/test/scala/cats/tests/FunctionTests.scala @@ -1,8 +1,6 @@ package cats package tests -import org.scalacheck.Arbitrary - import cats.arrow.{Arrow, Choice} import cats.functor.Contravariant import cats.laws.discipline._ diff --git a/tests/src/test/scala/cats/tests/IdTests.scala b/tests/src/test/scala/cats/tests/IdTests.scala index 1c9eca65cf..d811e9c3ed 100644 --- a/tests/src/test/scala/cats/tests/IdTests.scala +++ b/tests/src/test/scala/cats/tests/IdTests.scala @@ -1,7 +1,6 @@ package cats package tests -import org.scalacheck.Prop.forAll import cats.laws.discipline._ import cats.laws.discipline.eq.tuple3Eq diff --git a/tests/src/test/scala/cats/tests/InjectTests.scala b/tests/src/test/scala/cats/tests/InjectTests.scala index 54611ee836..6887e84aa6 100644 --- a/tests/src/test/scala/cats/tests/InjectTests.scala +++ b/tests/src/test/scala/cats/tests/InjectTests.scala @@ -3,7 +3,6 @@ package tests import cats.data.{Xor, Coproduct} import cats.free.{Free, Inject,:<:} -import cats.laws.discipline.arbitrary import org.scalacheck._ class InjectTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/IorTests.scala b/tests/src/test/scala/cats/tests/IorTests.scala index 1c555064c8..27c7779516 100644 --- a/tests/src/test/scala/cats/tests/IorTests.scala +++ b/tests/src/test/scala/cats/tests/IorTests.scala @@ -5,7 +5,6 @@ import cats.data.{Xor, Ior} import cats.laws.discipline.{BifunctorTests, TraverseTests, MonadTests, SerializableTests, CartesianTests} import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq._ -import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary._ class IorTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/ListWrapper.scala b/tests/src/test/scala/cats/tests/ListWrapper.scala index c1081d5f0d..06d58e5d4b 100644 --- a/tests/src/test/scala/cats/tests/ListWrapper.scala +++ b/tests/src/test/scala/cats/tests/ListWrapper.scala @@ -1,9 +1,7 @@ package cats package tests -import cats.data.OneAnd import cats.std.list._ -import cats.laws.discipline.arbitrary.oneAndArbitrary import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary.arbitrary diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index fe9bf60bb6..29768816ed 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -5,11 +5,9 @@ import algebra.laws.{GroupLaws, OrderLaws} import cats.data.{NonEmptyList, OneAnd} import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, CartesianTests, TraverseTests, ReducibleTests} -import cats.laws.discipline.arbitrary.{evalArbitrary, oneAndArbitrary} +import cats.laws.discipline.arbitrary.oneAndArbitrary import cats.laws.discipline.eq._ -import scala.util.Random - class OneAndTests extends CatsSuite { checkAll("OneAnd[List, Int]", OrderLaws[OneAnd[List, Int]].eqv) diff --git a/tests/src/test/scala/cats/tests/OptionTTests.scala b/tests/src/test/scala/cats/tests/OptionTTests.scala index 044dec5037..26db3b2149 100644 --- a/tests/src/test/scala/cats/tests/OptionTTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTTests.scala @@ -1,11 +1,10 @@ package cats.tests -import cats.{Applicative, Id, Monad, Cartesian, Show} -import cats.data.{OptionT, Validated, Xor} -import cats.laws.discipline.{ApplicativeTests, FunctorTests, MonadCombineTests, SerializableTests, CartesianTests, MonadTests} +import cats.{Id, Monad, Cartesian, Show} +import cats.data.{OptionT, Xor} +import cats.laws.discipline.{FunctorTests, SerializableTests, CartesianTests, MonadTests} import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq._ -import org.scalacheck.{Arbitrary, Gen} class OptionTTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/ProdTests.scala b/tests/src/test/scala/cats/tests/ProdTests.scala index aa56364210..c0c10077f5 100644 --- a/tests/src/test/scala/cats/tests/ProdTests.scala +++ b/tests/src/test/scala/cats/tests/ProdTests.scala @@ -5,7 +5,6 @@ import cats.data.Prod import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq._ -import org.scalacheck.Arbitrary class ProdTests extends CatsSuite { implicit val iso = CartesianTests.Isomorphisms.invariant[Prod[Option, List, ?]] diff --git a/tests/src/test/scala/cats/tests/StateTTests.scala b/tests/src/test/scala/cats/tests/StateTTests.scala index 6673c628d8..00d01c9550 100644 --- a/tests/src/test/scala/cats/tests/StateTTests.scala +++ b/tests/src/test/scala/cats/tests/StateTTests.scala @@ -1,11 +1,11 @@ package cats package tests -import cats.laws.discipline.{CartesianTests, MonadStateTests, MonoidKTests, SerializableTests} +import cats.laws.discipline.{CartesianTests, MonadStateTests, SerializableTests} import cats.data.{State, StateT} import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary._ -import org.scalacheck.{Arbitrary, Gen} +import org.scalacheck.Arbitrary class StateTTests extends CatsSuite { import StateTTests._ diff --git a/tests/src/test/scala/cats/tests/SyntaxTests.scala b/tests/src/test/scala/cats/tests/SyntaxTests.scala index e4c1849411..f5533c86f9 100644 --- a/tests/src/test/scala/cats/tests/SyntaxTests.scala +++ b/tests/src/test/scala/cats/tests/SyntaxTests.scala @@ -3,13 +3,7 @@ package tests import cats.std.AllInstances import cats.syntax.AllSyntax -import algebra.laws.GroupLaws import cats.functor.{Invariant, Contravariant} -import cats.laws.discipline.SerializableTests - -import org.scalacheck.{Arbitrary} -import org.scalatest.prop.PropertyChecks -import scala.reflect.runtime.universe.TypeTag /** * Test that our syntax implicits are working. diff --git a/tests/src/test/scala/cats/tests/ValidatedTests.scala b/tests/src/test/scala/cats/tests/ValidatedTests.scala index 4a7ccec0ca..e755b1719d 100644 --- a/tests/src/test/scala/cats/tests/ValidatedTests.scala +++ b/tests/src/test/scala/cats/tests/ValidatedTests.scala @@ -4,7 +4,6 @@ package tests import cats.data.{NonEmptyList, Validated, ValidatedNel, Xor, XorT} import cats.data.Validated.{Valid, Invalid} import cats.laws.discipline.{BifunctorTests, TraverseTests, ApplicativeErrorTests, SerializableTests, CartesianTests} -import org.scalacheck.{Gen, Arbitrary} import org.scalacheck.Arbitrary._ import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq.tuple3Eq diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index a106e05ae3..bfeea81314 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -8,7 +8,6 @@ import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary._ import algebra.laws.OrderLaws -import org.scalacheck.Prop.forAll class WriterTTests extends CatsSuite { type Logged[A] = Writer[ListWrapper[Int], A] diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index 9d3325d9db..adf7cf91e7 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -7,7 +7,7 @@ import cats.laws.discipline.arbitrary._ import cats.laws.discipline.{BitraverseTests, TraverseTests, MonadErrorTests, SerializableTests, CartesianTests} import cats.laws.discipline.eq.tuple3Eq import algebra.laws.{GroupLaws, OrderLaws} -import org.scalacheck.{Arbitrary, Gen} +import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary._ import scala.util.Try From adc8adfd99ef16b3cb9d696211325672fbd3e0ef Mon Sep 17 00:00:00 2001 From: Adelbert Chang Date: Fri, 18 Mar 2016 18:09:01 -0700 Subject: [PATCH 030/185] Make MonadWriter tuple ordering consistent with Writer data type --- core/src/main/scala/cats/MonadWriter.scala | 14 +++++++------- core/src/main/scala/cats/data/WriterT.scala | 12 ++++++------ .../src/main/scala/cats/laws/MonadWriterLaws.scala | 10 +++++----- .../cats/laws/discipline/MonadWriterTests.scala | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/core/src/main/scala/cats/MonadWriter.scala b/core/src/main/scala/cats/MonadWriter.scala index 3a19669636..5481e4cb96 100644 --- a/core/src/main/scala/cats/MonadWriter.scala +++ b/core/src/main/scala/cats/MonadWriter.scala @@ -3,24 +3,24 @@ package cats /** A monad that support monoidal accumulation (e.g. logging List[String]) */ trait MonadWriter[F[_], W] extends Monad[F] { /** Lift a writer action into the effect */ - def writer[A](aw: (A, W)): F[A] + def writer[A](aw: (W, A)): F[A] /** Run the effect and pair the accumulator with the result */ - def listen[A](fa: F[A]): F[(A, W)] + def listen[A](fa: F[A]): F[(W, A)] /** Apply the effectful function to the accumulator */ - def pass[A](fa: F[(A, W => W)]): F[A] + def pass[A](fa: F[(W => W, A)]): F[A] /** Lift the log into the effect */ - def tell(w: W): F[Unit] = writer(((), w)) + def tell(w: W): F[Unit] = writer((w, ())) /** Pair the value with an inspection of the accumulator */ - def listens[A, B](fa: F[A])(f: W => B): F[(A, B)] = - map(listen(fa)) { case (a, w) => (a, f(w)) } + def listens[A, B](fa: F[A])(f: W => B): F[(B, A)] = + map(listen(fa)) { case (w, a) => (f(w), a) } /** Modify the accumulator */ def censor[A](fa: F[A])(f: W => W): F[A] = - flatMap(listen(fa)) { case (a, w) => writer((a, f(w))) } + flatMap(listen(fa)) { case (w, a) => writer((f(w), a)) } } object MonadWriter { diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 55770196f1..8f91f27159 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -191,14 +191,14 @@ private[data] sealed trait WriterTMonad[F[_], L] extends WriterTApplicative[F, L } private[data] sealed trait WriterTMonadWriter[F[_], L] extends MonadWriter[WriterT[F, L, ?], L] with WriterTMonad[F, L] { - def writer[A](aw: (A, L)): WriterT[F, L, A] = - WriterT.put(aw._1)(aw._2) + def writer[A](aw: (L, A)): WriterT[F, L, A] = + WriterT.put(aw._2)(aw._1) - def listen[A](fa: WriterT[F, L, A]): WriterT[F, L, (A, L)] = - WriterT(F0.flatMap(fa.value)(a => F0.map(fa.written)(l => (l, (a, l))))) + def listen[A](fa: WriterT[F, L, A]): WriterT[F, L, (L, A)] = + WriterT(F0.flatMap(fa.value)(a => F0.map(fa.written)(l => (l, (l, a))))) - def pass[A](fa: WriterT[F, L, (A, L => L)]): WriterT[F, L, A] = - WriterT(F0.flatMap(fa.value) { case (a, f) => F0.map(fa.written)(l => (f(l), a)) }) + def pass[A](fa: WriterT[F, L, (L => L, A)]): WriterT[F, L, A] = + WriterT(F0.flatMap(fa.value) { case (f, a) => F0.map(fa.written)(l => (f(l), a)) }) } private[data] sealed trait WriterTSemigroupK[F[_], L] extends SemigroupK[WriterT[F, L, ?]] { diff --git a/laws/src/main/scala/cats/laws/MonadWriterLaws.scala b/laws/src/main/scala/cats/laws/MonadWriterLaws.scala index c4022a0c88..07c55eb656 100644 --- a/laws/src/main/scala/cats/laws/MonadWriterLaws.scala +++ b/laws/src/main/scala/cats/laws/MonadWriterLaws.scala @@ -5,16 +5,16 @@ trait MonadWriterLaws[F[_], W] extends MonadLaws[F] { implicit override def F: MonadWriter[F, W] def monadWriterWriterPure[A](a: A)(implicit W: Monoid[W]): IsEq[F[A]] = - F.writer((a, W.empty)) <-> F.pure(a) + F.writer((W.empty, a)) <-> F.pure(a) def monadWriterTellFusion(x: W, y: W)(implicit W: Monoid[W]): IsEq[F[Unit]] = F.flatMap(F.tell(x))(_ => F.tell(y)) <-> F.tell(W.combine(x, y)) - def monadWriterListenPure[A](a: A)(implicit W: Monoid[W]): IsEq[F[(A, W)]] = - F.listen(F.pure(a)) <-> F.pure((a, W.empty)) + def monadWriterListenPure[A](a: A)(implicit W: Monoid[W]): IsEq[F[(W, A)]] = + F.listen(F.pure(a)) <-> F.pure((W.empty, a)) - def monadWriterListenWriter[A](aw: (A, W)): IsEq[F[(A, W)]] = - F.listen(F.writer(aw)) <-> F.map(F.tell(aw._2))(_ => aw) + def monadWriterListenWriter[A](aw: (W, A)): IsEq[F[(W, A)]] = + F.listen(F.writer(aw)) <-> F.map(F.tell(aw._1))(_ => aw) } object MonadWriterLaws { diff --git a/laws/src/main/scala/cats/laws/discipline/MonadWriterTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadWriterTests.scala index 19317e8bd3..e3f92eb563 100644 --- a/laws/src/main/scala/cats/laws/discipline/MonadWriterTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/MonadWriterTests.scala @@ -16,7 +16,7 @@ trait MonadWriterTests[F[_], W] extends MonadTests[F] { ArbFAtoB: Arbitrary[F[A => B]], ArbFBtoC: Arbitrary[F[B => C]], EqFA: Eq[F[A]], - EqFAW: Eq[F[(A, W)]], + EqFAW: Eq[F[(W, A)]], EqFB: Eq[F[B]], EqFC: Eq[F[C]], EqFU: Eq[F[Unit]], From 7f29e87a44a1c60552a4d7d4fba937b1bed9ed65 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sat, 5 Mar 2016 00:15:28 -0500 Subject: [PATCH 031/185] First stab at a more flexible TransLift --- core/src/main/scala/cats/TransLift.scala | 19 +++++++++++++++-- core/src/main/scala/cats/data/Kleisli.scala | 10 +++++---- core/src/main/scala/cats/data/OptionT.scala | 9 +++++--- core/src/main/scala/cats/data/StateT.scala | 8 ++++--- core/src/main/scala/cats/data/WriterT.scala | 10 +++++---- core/src/main/scala/cats/data/XorT.scala | 10 +++++---- .../main/scala/cats/syntax/transLift.scala | 21 ++++++++++++++++++- 7 files changed, 66 insertions(+), 21 deletions(-) diff --git a/core/src/main/scala/cats/TransLift.scala b/core/src/main/scala/cats/TransLift.scala index 82c490b558..a41636a9bb 100644 --- a/core/src/main/scala/cats/TransLift.scala +++ b/core/src/main/scala/cats/TransLift.scala @@ -5,9 +5,24 @@ package cats * A typeclass which abstracts over the ability to lift an M[A] into a * MonadTransformer */ -trait TransLift[MT[_[_], _], M[_]] { +trait TransLift[MT[_[_], _]] { + + /** + * The typeclass which constrains liftT as a function of the type + * constructor it is given. A safe "identity" value for this type + * if your transformer does not constrain its lifted effects would + * be `type TC[M[_]] = Unit =:= Unit`. A more common constraint + * might be `type TC[M[_]] = Monad[M]`. + */ + type TC[M[_]] + /** * Lift a value of type M[A] into a monad transformer MT[M, A] */ - def liftT[A](ma: M[A]): MT[M,A] + def liftT[M[_]: TC, A](ma: M[A]): MT[M, A] +} + +object TransLift { + type Aux[MT[_[_], _], TC0[_[_]]] = TransLift[MT] { type TC[M[_]] = TC0[M] } + type AuxId[MT[_[_], _]] = Aux[MT, λ[X[_] => Unit =:= Unit]] // TODO we need a Trivial typeclass } diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index bbe8a9e246..af61308cfe 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -115,9 +115,11 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 { fa.local(f) } - implicit def kleisliTransLift[M[_], A]: TransLift[({type λ[α[_], β] = Kleisli[α, A, β]})#λ, M] = - new TransLift[({type λ[α[_], β] = Kleisli[α, A, β]})#λ, M] { - def liftT[B](ma: M[B]): Kleisli[M, A, B] = Kleisli[M, A, B](a => ma) + implicit def kleisliTransLift[A]: TransLift.AuxId[Kleisli[?[_], A, ?]] = + new TransLift[Kleisli[?[_], A, ?]] { + type TC[M[_]] = Unit =:= Unit + + def liftT[M[_], B](ma: M[B])(implicit ev: Unit =:= Unit): Kleisli[M, A, B] = Kleisli[M, A, B](a => ma) } } @@ -244,7 +246,7 @@ private trait KleisliStrong[F[_]] extends Strong[Kleisli[F, ?, ?]] { private trait KleisliSemigroup[F[_], A, B] extends Semigroup[Kleisli[F, A, B]] { implicit def FB: Semigroup[F[B]] - override def combine(a: Kleisli[F, A, B], b: Kleisli[F, A, B]): Kleisli[F, A, B] = + override def combine(a: Kleisli[F, A, B], b: Kleisli[F, A, B]): Kleisli[F, A, B] = Kleisli[F, A, B](x => FB.combine(a.run(x), b.run(x))) } diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index 93a904bc5d..9ba91b9d1a 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -129,9 +129,12 @@ private[data] sealed trait OptionTInstances1 { fa.map(f) } - implicit def optionTTransLift[M[_]: Functor]: TransLift[OptionT, M] = - new TransLift[OptionT, M] { - def liftT[A](ma: M[A]): OptionT[M, A] = OptionT.liftF(ma) + // do NOT change this to val! I know it looks like it should work, and really I agree, but it doesn't (for... reasons) + implicit def optionTTransLift: TransLift.Aux[OptionT, Functor] = + new TransLift[OptionT] { + type TC[M[_]] = Functor[M] + + def liftT[M[_]: Functor, A](ma: M[A]): OptionT[M, A] = OptionT.liftF(ma) } } diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 9e20c1cadb..439b86a6ff 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -143,9 +143,11 @@ private[data] sealed abstract class StateTInstances { fa.map(f) } - implicit def stateTLift[M[_], S](implicit M: Applicative[M]): TransLift[({type λ[α[_], β] = StateT[α, S, β]})#λ, M] = - new TransLift[({type λ[α[_], β] = StateT[α, S, β]})#λ, M] { - def liftT[A](ma: M[A]): StateT[M, S, A] = StateT(s => M.map(ma)(s -> _)) + implicit def stateTLift[S]: TransLift.Aux[StateT[?[_], S, ?], Applicative] = + new TransLift[StateT[?[_], S, ?]] { + type TC[M[_]] = Applicative[M] + + def liftT[M[_]: Applicative, A](ma: M[A]): StateT[M, S, A] = StateT(s => Applicative[M].map(ma)(s -> _)) } } diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 5cf55f8fd0..dacba37d28 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -65,10 +65,12 @@ private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { fab.bimap(f, g) } - implicit def writerTTransLift[M[_], W](implicit M: Functor[M], W: Monoid[W]): TransLift[({type λ[α[_], β] = WriterT[α,W,β]})#λ, M] = - new TransLift[({type λ[α[_], β] = WriterT[α,W,β]})#λ, M] { - def liftT[A](ma: M[A]): WriterT[M, W, A] = - WriterT(M.map(ma)((W.empty, _))) + implicit def writerTTransLift[W](implicit W: Monoid[W]): TransLift.Aux[WriterT[?[_], W, ?], Functor] = + new TransLift[WriterT[?[_], W, ?]] { + type TC[M[_]] = Functor[M] + + def liftT[M[_]: Functor, A](ma: M[A]): WriterT[M, W, A] = + WriterT(Functor[M].map(ma)((W.empty, _))) } implicit def writerTShow[F[_], L, V](implicit F: Show[F[(L, V)]]): Show[WriterT[F, L, V]] = new Show[WriterT[F, L, V]] { diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index 71d4a97c28..4e0331f24a 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -196,10 +196,12 @@ private[data] abstract class XorTInstances extends XorTInstances1 { val F0: Traverse[F] = F } - implicit def xortTransLift[M[_],E](implicit M: Functor[M]): TransLift[({type λ[α[_], β] = XorT[α,E,β]})#λ, M] = - new TransLift[({type λ[α[_], β] = XorT[α,E,β]})#λ, M] { - def liftT[A](ma: M[A]): XorT[M,E,A] = - XorT(M.map(ma)(Xor.right)) + implicit def xortTransLift[E]: TransLift.Aux[XorT[?[_], E, ?], Functor] = + new TransLift[XorT[?[_], E, ?]] { + type TC[M[_]] = Functor[M] + + def liftT[M[_]: Functor, A](ma: M[A]): XorT[M,E,A] = + XorT(Functor[M].map(ma)(Xor.right)) } } diff --git a/core/src/main/scala/cats/syntax/transLift.scala b/core/src/main/scala/cats/syntax/transLift.scala index c5fc39ef8d..4b8082ed9e 100644 --- a/core/src/main/scala/cats/syntax/transLift.scala +++ b/core/src/main/scala/cats/syntax/transLift.scala @@ -6,5 +6,24 @@ trait TransLiftSyntax { } final class TransLiftOps[M[_], A](val ma: M[A]) extends AnyVal { - def liftT[MT[_[_],_]](implicit TL: TransLift[MT, M]): MT[M,A] = TL.liftT(ma) + def liftT[MT[_[_],_]](implicit extract: TLExtract[MT, M]): MT[M, A] = extract.TL.liftT(ma)(extract.TC) } + +trait TLExtract[MT[_[_], _], M[_]] { + val TL: TransLift[MT] + val TC: TL.TC[M] +} + +object TLExtract { + + implicit def extract1[MT[_[_], _], M[_], TC[_[_]]](implicit TL0: TransLift.Aux[MT, TC], TC0: TC[M]): TLExtract[MT, M] = new TLExtract[MT, M] { + val TL = TL0 + val TC = TC0 + } + + implicit def extract1Id[MT[_[_], _], M[_]](implicit TL0: TransLift.Aux[MT, λ[X[_] => Unit =:= Unit]], TC0: Unit =:= Unit): TLExtract[MT, M] = extract1[MT, M, λ[X[_] => Unit =:= Unit]] + + // sigh... + implicit def extract2[MT[_[_], _, _], Z, M[_], TC[_[_]]](implicit TL0: TransLift.Aux[MT[?[_], Z, ?], TC], TC0: TC[M]): TLExtract[MT[?[_], Z, ?], M] = extract1[MT[?[_], Z, ?], M, TC] + implicit def extract2Id[MT[_[_], _, _], Z, M[_]](implicit TL0: TransLift.Aux[MT[?[_], Z, ?], λ[X[_] => Unit =:= Unit]], TC0: Unit =:= Unit): TLExtract[MT[?[_], Z, ?], M] = extract1[MT[?[_], Z, ?], M, λ[X[_] => Unit =:= Unit]] +} \ No newline at end of file From 9b6807e766b1aae32bb2f0d665b4b5db9afc78a2 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 20 Mar 2016 13:52:10 -0600 Subject: [PATCH 032/185] Replaced Unit =:= Unit with a proper Trivial --- core/src/main/scala/cats/TransLift.scala | 2 +- core/src/main/scala/cats/Trivial.scala | 23 +++++++++++++++++++ core/src/main/scala/cats/data/Kleisli.scala | 4 ++-- .../main/scala/cats/syntax/transLift.scala | 4 ++-- 4 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 core/src/main/scala/cats/Trivial.scala diff --git a/core/src/main/scala/cats/TransLift.scala b/core/src/main/scala/cats/TransLift.scala index a41636a9bb..a11f440f34 100644 --- a/core/src/main/scala/cats/TransLift.scala +++ b/core/src/main/scala/cats/TransLift.scala @@ -24,5 +24,5 @@ trait TransLift[MT[_[_], _]] { object TransLift { type Aux[MT[_[_], _], TC0[_[_]]] = TransLift[MT] { type TC[M[_]] = TC0[M] } - type AuxId[MT[_[_], _]] = Aux[MT, λ[X[_] => Unit =:= Unit]] // TODO we need a Trivial typeclass + type AuxId[MT[_[_], _]] = Aux[MT, Trivial.PH1] } diff --git a/core/src/main/scala/cats/Trivial.scala b/core/src/main/scala/cats/Trivial.scala new file mode 100644 index 0000000000..13c2d4fb28 --- /dev/null +++ b/core/src/main/scala/cats/Trivial.scala @@ -0,0 +1,23 @@ +package cats + +/** + * The "Unit typeclass". The only instance of `Trivial` is given by + * `Trivial.manifest`, and this instance is guaranteed to be in the + * implicit scope. Several convenience type aliases are provided in + * companion object, covering a few common use cases and avoiding the + * need for unnecessary lambdas (e.g. if you want a trivial typeclass + * instance for a type constructor, you should use `Trivial.PH1`). + */ +sealed trait Trivial + +object Trivial { + type P1[A] = Trivial + type PH1[F[_]] = Trivial + type P1H1[F[_], A] = Trivial + type P2[A, B] = Trivial + type P2H1[F[_], A, B] = Trivial + type P3[A, B, C] = Trivial + type P3H1[F[_], A, B, C] = Trivial + + implicit val manifest: Trivial = new Trivial {} +} \ No newline at end of file diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index af61308cfe..f0e827d1e9 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -117,9 +117,9 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 { implicit def kleisliTransLift[A]: TransLift.AuxId[Kleisli[?[_], A, ?]] = new TransLift[Kleisli[?[_], A, ?]] { - type TC[M[_]] = Unit =:= Unit + type TC[M[_]] = Trivial - def liftT[M[_], B](ma: M[B])(implicit ev: Unit =:= Unit): Kleisli[M, A, B] = Kleisli[M, A, B](a => ma) + def liftT[M[_], B](ma: M[B])(implicit ev: Trivial): Kleisli[M, A, B] = Kleisli[M, A, B](a => ma) } } diff --git a/core/src/main/scala/cats/syntax/transLift.scala b/core/src/main/scala/cats/syntax/transLift.scala index 4b8082ed9e..d7b8df6650 100644 --- a/core/src/main/scala/cats/syntax/transLift.scala +++ b/core/src/main/scala/cats/syntax/transLift.scala @@ -21,9 +21,9 @@ object TLExtract { val TC = TC0 } - implicit def extract1Id[MT[_[_], _], M[_]](implicit TL0: TransLift.Aux[MT, λ[X[_] => Unit =:= Unit]], TC0: Unit =:= Unit): TLExtract[MT, M] = extract1[MT, M, λ[X[_] => Unit =:= Unit]] + implicit def extract1Id[MT[_[_], _], M[_]](implicit TL0: TransLift.Aux[MT, Trivial.PH1], TC0: Trivial): TLExtract[MT, M] = extract1[MT, M, Trivial.PH1] // sigh... implicit def extract2[MT[_[_], _, _], Z, M[_], TC[_[_]]](implicit TL0: TransLift.Aux[MT[?[_], Z, ?], TC], TC0: TC[M]): TLExtract[MT[?[_], Z, ?], M] = extract1[MT[?[_], Z, ?], M, TC] - implicit def extract2Id[MT[_[_], _, _], Z, M[_]](implicit TL0: TransLift.Aux[MT[?[_], Z, ?], λ[X[_] => Unit =:= Unit]], TC0: Unit =:= Unit): TLExtract[MT[?[_], Z, ?], M] = extract1[MT[?[_], Z, ?], M, λ[X[_] => Unit =:= Unit]] + implicit def extract2Id[MT[_[_], _, _], Z, M[_]](implicit TL0: TransLift.Aux[MT[?[_], Z, ?], Trivial.PH1], TC0: Trivial): TLExtract[MT[?[_], Z, ?], M] = extract1[MT[?[_], Z, ?], M, Trivial.PH1] } \ No newline at end of file From 837f8863444b60a58275b2c3591548043f7b4708 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 20 Mar 2016 14:24:51 -0600 Subject: [PATCH 033/185] Rewrote TLExtract to bypass SI-2712 --- .../main/scala/cats/syntax/transLift.scala | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/core/src/main/scala/cats/syntax/transLift.scala b/core/src/main/scala/cats/syntax/transLift.scala index d7b8df6650..fcaa4c3559 100644 --- a/core/src/main/scala/cats/syntax/transLift.scala +++ b/core/src/main/scala/cats/syntax/transLift.scala @@ -5,25 +5,31 @@ trait TransLiftSyntax { implicit def transLiftSyntax[M[_], A](ma: M[A]): TransLiftOps[M, A] = new TransLiftOps(ma) } -final class TransLiftOps[M[_], A](val ma: M[A]) extends AnyVal { - def liftT[MT[_[_],_]](implicit extract: TLExtract[MT, M]): MT[M, A] = extract.TL.liftT(ma)(extract.TC) +final class TransLiftOps[M0[_], A](val ma: M0[A]) extends AnyVal { + import TLExtract._ + + def liftT[MT0[_[_],_]](implicit extract: TLExtract[SingletonMT { type MT[F[_], B] = MT0[F, B] }, SingletonM { type M[B] = M0[B] }]): MT0[M0, A] = extract.TL.liftT(ma)(extract.TC) } -trait TLExtract[MT[_[_], _], M[_]] { - val TL: TransLift[MT] - val TC: TL.TC[M] +trait TLExtract[MTS <: TLExtract.SingletonMT, MS <: TLExtract.SingletonM] { + val TL: TransLift[MTS#MT] + val TC: TL.TC[MS#M] } object TLExtract { - implicit def extract1[MT[_[_], _], M[_], TC[_[_]]](implicit TL0: TransLift.Aux[MT, TC], TC0: TC[M]): TLExtract[MT, M] = new TLExtract[MT, M] { + trait SingletonMT { + type MT[F[_], A] + } + + trait SingletonM { + type M[A] + } + + implicit def extract[MTS <: SingletonMT, MS <: SingletonM, TC[_[_]]](implicit TL0: TransLift.Aux[MTS#MT, TC], TC0: TC[MS#M]): TLExtract[MTS, MS] = new TLExtract[MTS, MS] { val TL = TL0 val TC = TC0 } - implicit def extract1Id[MT[_[_], _], M[_]](implicit TL0: TransLift.Aux[MT, Trivial.PH1], TC0: Trivial): TLExtract[MT, M] = extract1[MT, M, Trivial.PH1] - - // sigh... - implicit def extract2[MT[_[_], _, _], Z, M[_], TC[_[_]]](implicit TL0: TransLift.Aux[MT[?[_], Z, ?], TC], TC0: TC[M]): TLExtract[MT[?[_], Z, ?], M] = extract1[MT[?[_], Z, ?], M, TC] - implicit def extract2Id[MT[_[_], _, _], Z, M[_]](implicit TL0: TransLift.Aux[MT[?[_], Z, ?], Trivial.PH1], TC0: Trivial): TLExtract[MT[?[_], Z, ?], M] = extract1[MT[?[_], Z, ?], M, Trivial.PH1] + implicit def extractId[MTS <: SingletonMT, MS <: SingletonM](implicit TL0: TransLift.Aux[MTS#MT, Trivial.PH1]): TLExtract[MTS, MS] = extract[MTS, MS, Trivial.PH1] } \ No newline at end of file From 0ad9dbc733f700743fff6ba241a706aca896bdca Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sun, 20 Mar 2016 14:49:01 -0600 Subject: [PATCH 034/185] Adjusted transLiftSyntax to avoid SI-2712 --- core/src/main/scala/cats/syntax/transLift.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/syntax/transLift.scala b/core/src/main/scala/cats/syntax/transLift.scala index fcaa4c3559..d5570581d9 100644 --- a/core/src/main/scala/cats/syntax/transLift.scala +++ b/core/src/main/scala/cats/syntax/transLift.scala @@ -2,7 +2,7 @@ package cats package syntax trait TransLiftSyntax { - implicit def transLiftSyntax[M[_], A](ma: M[A]): TransLiftOps[M, A] = new TransLiftOps(ma) + implicit def transLiftSyntax[E](ma: E)(implicit U: Unapply[Trivial.PH1, E]): TransLiftOps[U.M, U.A] = new TransLiftOps(U.subst(ma)) } final class TransLiftOps[M0[_], A](val ma: M0[A]) extends AnyVal { From 51416c72cdc74cd3360e8981c5c4f3f6d6a1b3f2 Mon Sep 17 00:00:00 2001 From: Adelbert Chang Date: Sun, 20 Mar 2016 14:00:11 -0700 Subject: [PATCH 035/185] Journey to >= 90% test coverage Type class composition and MonadState tests --- core/src/main/scala/cats/Bitraverse.scala | 2 +- .../main/scala/cats/laws/discipline/Eq.scala | 9 ++++++++ .../scala/cats/tests/BifoldableTests.scala | 15 +++++++++++++ .../scala/cats/tests/BitraverseTests.scala | 16 ++++++++++++++ .../test/scala/cats/tests/CategoryTests.scala | 19 ++++++++++++++++ .../test/scala/cats/tests/ComposeTest.scala | 19 ++++++++++++++++ .../test/scala/cats/tests/FunctorTests.scala | 22 +++++++++++++++++++ .../scala/cats/tests/MonadStateTests.scala | 22 +++++++++++++++++++ 8 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 tests/src/test/scala/cats/tests/BifoldableTests.scala create mode 100644 tests/src/test/scala/cats/tests/BitraverseTests.scala create mode 100644 tests/src/test/scala/cats/tests/CategoryTests.scala create mode 100644 tests/src/test/scala/cats/tests/ComposeTest.scala create mode 100644 tests/src/test/scala/cats/tests/FunctorTests.scala create mode 100644 tests/src/test/scala/cats/tests/MonadStateTests.scala diff --git a/core/src/main/scala/cats/Bitraverse.scala b/core/src/main/scala/cats/Bitraverse.scala index a9a5f9c285..90804d364b 100644 --- a/core/src/main/scala/cats/Bitraverse.scala +++ b/core/src/main/scala/cats/Bitraverse.scala @@ -14,7 +14,7 @@ trait Bitraverse[F[_, _]] extends Bifoldable[F] with Bifunctor[F] { self => bitraverse(fab)(identity, identity) /** If F and G are both [[cats.Bitraverse]] then so is their composition F[G[_, _], G[_, _]] */ - def compose[G[_, _]](implicit ev: Bitraverse[G]): Bifoldable[Lambda[(A, B) => F[G[A, B], G[A, B]]]] = + def compose[G[_, _]](implicit ev: Bitraverse[G]): Bitraverse[Lambda[(A, B) => F[G[A, B], G[A, B]]]] = new CompositeBitraverse[F, G] { val F = self val G = ev diff --git a/laws/src/main/scala/cats/laws/discipline/Eq.scala b/laws/src/main/scala/cats/laws/discipline/Eq.scala index d79a1a6a84..3e5769486e 100644 --- a/laws/src/main/scala/cats/laws/discipline/Eq.scala +++ b/laws/src/main/scala/cats/laws/discipline/Eq.scala @@ -21,6 +21,15 @@ object eq { } } + /** Create an approximation of Eq[Show[A]] by using function1Eq[A, String] */ + implicit def showEq[A: Arbitrary]: Eq[Show[A]] = + Eq.by[Show[A], A => String] { showInstance => + (a: A) => showInstance.show(a) + } + + // Temporary, see https://github.com/non/algebra/pull/143 + implicit val stringEq: Eq[String] = Eq.fromUniversalEquals + // Temporary, see https://github.com/non/algebra/pull/82 implicit def tuple2Eq[A, B](implicit A: Eq[A], B: Eq[B]): Eq[(A, B)] = new Eq[(A, B)] { diff --git a/tests/src/test/scala/cats/tests/BifoldableTests.scala b/tests/src/test/scala/cats/tests/BifoldableTests.scala new file mode 100644 index 0000000000..2c121909bd --- /dev/null +++ b/tests/src/test/scala/cats/tests/BifoldableTests.scala @@ -0,0 +1,15 @@ +package cats +package tests + +import cats.data.Xor +import cats.laws.discipline.{BifoldableTests, SerializableTests} +import cats.laws.discipline.arbitrary._ + +class BifoldableTest extends CatsSuite { + type EitherXor[A, B] = Either[Xor[A, B], Xor[A, B]] + val eitherComposeXor: Bifoldable[EitherXor] = + Bifoldable[Either].compose[Xor] + + checkAll("Either compose Xor", BifoldableTests(eitherComposeXor).bifoldable[Int, Int, Int]) + checkAll("Bifoldable[Either compose Xor]", SerializableTests.serializable(eitherComposeXor)) +} diff --git a/tests/src/test/scala/cats/tests/BitraverseTests.scala b/tests/src/test/scala/cats/tests/BitraverseTests.scala new file mode 100644 index 0000000000..24b5a01cd3 --- /dev/null +++ b/tests/src/test/scala/cats/tests/BitraverseTests.scala @@ -0,0 +1,16 @@ +package cats +package tests + +import cats.data.Xor +import cats.laws.discipline.{BitraverseTests, SerializableTests} +import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq.tuple2Eq + +class BitraverseTest extends CatsSuite { + type XorTuple2[A, B] = Xor[(A, B), (A, B)] + val xorComposeTuple2: Bitraverse[XorTuple2] = + Bitraverse[Xor].compose[Tuple2] + + checkAll("Xor compose Tuple2", BitraverseTests(xorComposeTuple2).bitraverse[List, Int, Int, Int, String, String, String]) + checkAll("Bitraverse[Xor compose Tuple2]", SerializableTests.serializable(xorComposeTuple2)) +} diff --git a/tests/src/test/scala/cats/tests/CategoryTests.scala b/tests/src/test/scala/cats/tests/CategoryTests.scala new file mode 100644 index 0000000000..b93cc732d6 --- /dev/null +++ b/tests/src/test/scala/cats/tests/CategoryTests.scala @@ -0,0 +1,19 @@ +package cats +package tests + +import algebra.laws.GroupLaws + +import cats.arrow.Category +import cats.laws.discipline.{MonoidKTests, SerializableTests} +import cats.laws.discipline.eq.function1Eq + +class CategoryTest extends CatsSuite { + val functionCategory = Category[Function1] + type Endo[A] = Function1[A, A] + + checkAll("Category[Function1].algebraK", MonoidKTests[Endo](functionCategory.algebraK).monoidK[Int]) + checkAll("Category[Function1].algebraK", SerializableTests.serializable(functionCategory.algebraK)) + + val functionAlgebra = functionCategory.algebra[Int] + checkAll("Category[Function1].algebra[Int]", GroupLaws[Endo[Int]].monoid(functionAlgebra)) +} diff --git a/tests/src/test/scala/cats/tests/ComposeTest.scala b/tests/src/test/scala/cats/tests/ComposeTest.scala new file mode 100644 index 0000000000..74019444bb --- /dev/null +++ b/tests/src/test/scala/cats/tests/ComposeTest.scala @@ -0,0 +1,19 @@ +package cats +package tests + +import algebra.laws.GroupLaws + +import cats.arrow.Compose +import cats.laws.discipline.{SemigroupKTests, SerializableTests} +import cats.laws.discipline.eq.function1Eq + +class ComposeTest extends CatsSuite { + val functionCompose = Compose[Function1] + type Endo[A] = Function1[A, A] + + checkAll("Compose[Function1].algebraK", SemigroupKTests[Endo](functionCompose.algebraK).semigroupK[Int]) + checkAll("Compose[Function1].algebraK", SerializableTests.serializable(functionCompose.algebraK)) + + val functionAlgebra = functionCompose.algebra[Int] + checkAll("Compose[Function1].algebra[Int]", GroupLaws[Endo[Int]].semigroup(functionAlgebra)) +} diff --git a/tests/src/test/scala/cats/tests/FunctorTests.scala b/tests/src/test/scala/cats/tests/FunctorTests.scala new file mode 100644 index 0000000000..6c9a2b2c77 --- /dev/null +++ b/tests/src/test/scala/cats/tests/FunctorTests.scala @@ -0,0 +1,22 @@ +package cats +package tests + +import cats.functor.Contravariant +import cats.laws.discipline.{ContravariantTests, FunctorTests, SerializableTests} +import cats.laws.discipline.arbitrary.showArbitrary +import cats.laws.discipline.eq.showEq + +class FunctorTest extends CatsSuite { + type OptionList[A] = Option[List[A]] + + val optionListFunctor: Functor[OptionList] = + Functor[Option].compose[List] + checkAll("Option compose List", FunctorTests(optionListFunctor).functor[Int, Int, Int]) + checkAll("Functor[Option compose List]", SerializableTests.serializable(optionListFunctor)) + + type OptionShow[A] = Option[Show[A]] + val optionShowContravariant: Contravariant[OptionShow] = + Functor[Option].composeWithContravariant[Show] + checkAll("Option composeWithContravariant Show", ContravariantTests(optionShowContravariant).contravariant[Int, Int, Int]) + checkAll("Contravariant[Option composeWithContravariant Show]", SerializableTests.serializable(optionShowContravariant)) +} diff --git a/tests/src/test/scala/cats/tests/MonadStateTests.scala b/tests/src/test/scala/cats/tests/MonadStateTests.scala new file mode 100644 index 0000000000..3c9d825b28 --- /dev/null +++ b/tests/src/test/scala/cats/tests/MonadStateTests.scala @@ -0,0 +1,22 @@ +package cats +package tests + +import cats.data.State + +class MonadStateTest extends CatsSuite { + val testInstance = MonadState[State[Int, ?], Int] + + test("MonadState#modify identity does not modify the state") { + forAll { (i: Int) => + val x = testInstance.modify(identity).runS(i).value + x should === (i) + } + } + + test("MonadState#inspect identity does not modify the state") { + forAll { (i: Int) => + val x = testInstance.inspect(identity).runA(i).value + x should === (i) + } + } +} From 31933032b0185998edfdc3058943e24edfda9297 Mon Sep 17 00:00:00 2001 From: Max Worgan Date: Thu, 24 Mar 2016 14:28:24 +0000 Subject: [PATCH 036/185] Added OptionT.none as per issue #935 --- core/src/main/scala/cats/data/OptionT.scala | 3 +++ tests/src/test/scala/cats/tests/OptionTTests.scala | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index 93a904bc5d..adc055502b 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -94,6 +94,9 @@ object OptionT extends OptionTInstances { def pure[F[_], A](a: A)(implicit F: Applicative[F]): OptionT[F, A] = OptionT(F.pure(Some(a))) + def none[F[_], A](implicit F: Applicative[F]) : OptionT[F, A] = + OptionT(F.pure(None)) + /** * Transforms an `Option` into an `OptionT`, lifted into the specified `Applicative`. * diff --git a/tests/src/test/scala/cats/tests/OptionTTests.scala b/tests/src/test/scala/cats/tests/OptionTTests.scala index 044dec5037..8d9b67f18e 100644 --- a/tests/src/test/scala/cats/tests/OptionTTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTTests.scala @@ -134,6 +134,10 @@ class OptionTTests extends CatsSuite { OptionT[Xor[String, ?], Int](xor).show should === ("Xor.Right(Some(1))") } + test("none") { + OptionT.none[List,Int] should === (OptionT[List,Int](List(None))) + } + test("implicit Show[OptionT] instance and explicit show method are consistent") { forAll { optionT: OptionT[List, Int] => optionT.show should === (implicitly[Show[OptionT[List, Int]]].show(optionT)) From 3df9e4eab6aaf5565c6caed9bd3432154d291c69 Mon Sep 17 00:00:00 2001 From: Max Worgan Date: Thu, 24 Mar 2016 14:52:14 +0000 Subject: [PATCH 037/185] Added documentation and some alias --- core/src/main/scala/cats/data/OptionT.scala | 4 ++++ docs/src/main/tut/optiont.md | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index adc055502b..cff671687c 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -94,6 +94,10 @@ object OptionT extends OptionTInstances { def pure[F[_], A](a: A)(implicit F: Applicative[F]): OptionT[F, A] = OptionT(F.pure(Some(a))) + /** An alias for pure */ + def some[F[_], A](a: A)(implicit F: Applicative[F]): OptionT[F, A] = + pure(a) + def none[F[_], A](implicit F: Applicative[F]) : OptionT[F, A] = OptionT(F.pure(None)) diff --git a/docs/src/main/tut/optiont.md b/docs/src/main/tut/optiont.md index 7d70b282e7..a771840ea6 100644 --- a/docs/src/main/tut/optiont.md +++ b/docs/src/main/tut/optiont.md @@ -72,6 +72,23 @@ val result: Future[Option[String]] = ot.value // Future(Some("Hello Jane Doe")) ``` +## From `A` to `OptionT[F,A]` + +If you have only an `A` and you wish to *lift* it into an `OptionT[F,A]` assuming you have an applicative instance for F you can use `some` which is an alias for `pure`. There also exists a `none` method which can be used to create an OptionT[F,A], where the Option wrapped A type is actually a `None`: + +```tut:silent + +import cats.std.future._ + +val greet: OptionT[Future,String] = OptionT.pure("Hola!") + +val greetAlt: OptionT[Future,String] = OptionT.some("Hi!") + +val failedGreet: OptionT[Future,String] = OptionT.none + +``` + + ## Beyond map Sometimes the operation you want to perform on an `Future[Option[String]]` might not be as simple as just wrapping the `Option` method in a `Future.map` call. For example, what if we want to greet the customer with their custom greeting if it exists but otherwise fall back to a default `Future[String]` greeting? Without `OptionT`, this implementation might look like: From 0e7ae5242ed0a34867537030f01ccb2cea5938a2 Mon Sep 17 00:00:00 2001 From: Dave Gurnell Date: Thu, 24 Mar 2016 18:53:25 +0000 Subject: [PATCH 038/185] Syntax for ApplicativeError --- core/src/main/scala/cats/syntax/all.scala | 1 + .../scala/cats/syntax/applicativeError.scala | 37 ++++++++++++++++ core/src/main/scala/cats/syntax/package.scala | 1 + .../cats/tests/ApplicativeErrorTests.scala | 42 +++++++++++++++++++ .../test/scala/cats/tests/SyntaxTests.scala | 25 +++++++++++ 5 files changed, 106 insertions(+) create mode 100644 core/src/main/scala/cats/syntax/applicativeError.scala create mode 100644 tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index b99ed8785f..bbb196d91b 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -3,6 +3,7 @@ package syntax trait AllSyntax extends ApplicativeSyntax + with ApplicativeErrorSyntax with ApplySyntax with BifunctorSyntax with BifoldableSyntax diff --git a/core/src/main/scala/cats/syntax/applicativeError.scala b/core/src/main/scala/cats/syntax/applicativeError.scala new file mode 100644 index 0000000000..22e0927c5b --- /dev/null +++ b/core/src/main/scala/cats/syntax/applicativeError.scala @@ -0,0 +1,37 @@ +package cats +package syntax + +import cats.data.{Xor, XorT} + +trait ApplicativeErrorSyntax { + implicit def applicativeErrorIdSyntax[E](e: E): ApplicativeErrorIdOps[E] = + new ApplicativeErrorIdOps(e) + + implicit def applicativeErrorSyntax[F[_, _], E, A](fa: F[E, A])(implicit F: ApplicativeError[F[E, ?], E]): ApplicativeErrorOps[F[E, ?], E, A] = + new ApplicativeErrorOps[F[E, ?], E, A](fa) +} + +final class ApplicativeErrorIdOps[E](e: E) { + def raiseError[F[_], A](implicit F: ApplicativeError[F, E]): F[A] = + F.raiseError(e) +} + +final class ApplicativeErrorOps[F[_], E, A](fa: F[A])(implicit F: ApplicativeError[F, E]) { + def handleError(f: E => A): F[A] = + F.handleError(fa)(f) + + def handleErrorWith(f: E => F[A]): F[A] = + F.handleErrorWith(fa)(f) + + def attempt: F[E Xor A] = + F.attempt(fa) + + def attemptT: XorT[F, E, A] = + F.attemptT(fa) + + def recover(pf: PartialFunction[E, A]): F[A] = + F.recover(fa)(pf) + + def recoverWith(pf: PartialFunction[E, F[A]]): F[A] = + F.recoverWith(fa)(pf) +} diff --git a/core/src/main/scala/cats/syntax/package.scala b/core/src/main/scala/cats/syntax/package.scala index 26ec7fd4f5..668c2f7311 100644 --- a/core/src/main/scala/cats/syntax/package.scala +++ b/core/src/main/scala/cats/syntax/package.scala @@ -3,6 +3,7 @@ package cats package object syntax { object all extends AllSyntax object applicative extends ApplicativeSyntax + object applicativeError extends ApplicativeErrorSyntax object apply extends ApplySyntax object bifunctor extends BifunctorSyntax object bifoldable extends BifoldableSyntax diff --git a/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala b/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala new file mode 100644 index 0000000000..b49dfaf3d3 --- /dev/null +++ b/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala @@ -0,0 +1,42 @@ +package cats +package tests + +import cats.data.{Xor, XorT} + +class ApplicativeErrorCheck extends CatsSuite { + + type ErrorOr[A] = String Xor A + + val failed: String Xor Int = + "Badness".raiseError[ErrorOr, Int] + + test("raiseError syntax creates an Xor with the correct type parameters") { + failed should === ("Badness".left[Int]) + } + + test("handleError syntax transforms an error to a success") { + failed.handleError(error => error.length) should === (7.right) + } + + test("handleErrorWith transforms an error to a success") { + failed.handleErrorWith(error => error.length.right) should === (7.right) + } + + test("attempt syntax creates a wrapped Xor") { + failed.attempt should === ("Badness".left.right) + } + + test("attemptT syntax creates an XorT") { + type ErrorOrT[A] = XorT[ErrorOr, String, A] + failed.attemptT should === (XorT[ErrorOr, String, Int](failed.right)) + } + + test("recover syntax transforms an error to a success") { + failed.recover { case error => error.length } should === (7.right) + } + + test("recoverWith transforms an error to a success") { + failed.recoverWith { case error => error.length.right } should === (7.right) + } + +} \ No newline at end of file diff --git a/tests/src/test/scala/cats/tests/SyntaxTests.scala b/tests/src/test/scala/cats/tests/SyntaxTests.scala index f5533c86f9..3663d17917 100644 --- a/tests/src/test/scala/cats/tests/SyntaxTests.scala +++ b/tests/src/test/scala/cats/tests/SyntaxTests.scala @@ -214,4 +214,29 @@ class SyntaxTests extends AllInstances with AllSyntax { val la = mock[Eval[A]] val lfa = la.pureEval[F] } + + def testApplicativeError[F[_, _], E, A](implicit F: ApplicativeError[F[E, ?], E]): Unit = { + type G[X] = F[E, X] + + val e = mock[E] + val ga = e.raiseError[G, A] + + val gea = mock[G[A]] + + val ea = mock[E => A] + val gea1 = ga.handleError(ea) + + val egea = mock[E => G[A]] + val gea2 = ga.handleErrorWith(egea) + + val gxea = ga.attempt + + val gxtea = ga.attemptT + + val pfea = mock[PartialFunction[E, A]] + val gea3 = ga.recover(pfea) + + val pfegea = mock[PartialFunction[E, G[A]]] + val gea4 = ga.recoverWith(pfegea) + } } From f19477fadc442ce56e99be26f41c0ffbacdafedf Mon Sep 17 00:00:00 2001 From: Mike Curry Date: Sat, 26 Mar 2016 21:37:41 +0000 Subject: [PATCH 039/185] Add .ensime_cache to gitignore Adds .ensime_cache to gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4581b4d5be..b31f7b0d44 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ project/boot target .ensime .ensime_lucene +.ensime_cache TAGS \#*# *~ From 471b6485591228d9811a3d049017d96ee5fa4cdb Mon Sep 17 00:00:00 2001 From: Dave Gurnell Date: Sun, 27 Mar 2016 13:33:23 +0100 Subject: [PATCH 040/185] Remove unnecessary type annotation --- tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala b/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala index b49dfaf3d3..e23e35a20d 100644 --- a/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala +++ b/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala @@ -27,7 +27,6 @@ class ApplicativeErrorCheck extends CatsSuite { } test("attemptT syntax creates an XorT") { - type ErrorOrT[A] = XorT[ErrorOr, String, A] failed.attemptT should === (XorT[ErrorOr, String, Int](failed.right)) } From dcc292fa677e692983d330dc2aca0f710cc9217c Mon Sep 17 00:00:00 2001 From: Binh Nguyen Date: Mon, 28 Mar 2016 00:33:07 +0700 Subject: [PATCH 041/185] Switch to use nodeJsEnv as default jsEnv to build scala.js By default, scala.js compiler uses `Rhino` as the js environment. But it usually requires a lot of memory and often lead to OOM. This PR replace the default to a node.js one that should be much faster and slimer. --- .travis.yml | 7 +++++++ build.sbt | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 30e14c9509..dd2a74d4c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,10 @@ scala: script: - scripts/travis-publish.sh +# http://austinpray.com/ops/2015/09/20/change-travis-node-version.html +install: + - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION + notifications: webhooks: urls: @@ -21,12 +25,15 @@ env: global: - secure: Kf44XQFpq2QGe3rn98Dsf5Uz3WXzPDralS54co7sqT5oQGs1mYLYZRYz+I75ZSo5ffZ86H7M+AI9YFofqGwAjBixBbqf1tGkUh3oZp2fN3QfqzazGV3HzC+o41zALG5FL+UBaURev9ChQ5fYeTtFB7YAzejHz4y5E97awk934Rg= - secure: QbNAu0jCaKrwjJi7KZtYEBA/pYbTJ91Y1x/eLAJpsamswVOvwnThA/TLYuux+oiZQCiDUpBzP3oxksIrEEUAhl0lMtqRFY3MrcUr+si9NIjX8hmoFwkvZ5o1b7pmLF6Vz3rQeP/EWMLcljLzEwsrRXeK0Ei2E4vFpsg8yz1YXJg= + - TRAVIS_NODE_VERSION="4" cache: directories: - $HOME/.sbt/0.13/dependency - $HOME/.sbt/boot/scala* - $HOME/.sbt/launchers - $HOME/.ivy2/cache + - $HOME/.nvm + before_cache: - du -h -d 1 $HOME/.ivy2/cache - du -h -d 2 $HOME/.sbt/ diff --git a/build.sbt b/build.sbt index 51e15edba7..7ca986daf7 100644 --- a/build.sbt +++ b/build.sbt @@ -55,7 +55,11 @@ lazy val commonJsSettings = Seq( s"-P:scalajs:mapSourceURI:$a->$g/" }, scalaJSStage in Global := FastOptStage, - parallelExecution := false + parallelExecution := false, + // Using Rhino as jsEnv to build scala.js code can lead to OOM, switch to PhantomJS by default + scalaJSUseRhino := false, + requiresDOM := false, + jsEnv := NodeJSEnv().value ) lazy val commonJvmSettings = Seq( From 8608018e1ca26469c77b32ecd6c66d65de5cb956 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Sun, 27 Mar 2016 09:09:43 -0400 Subject: [PATCH 042/185] Reduce maxSize and minSuccessful in Scalacheck params This is an experiment to see if builds time out less if we don't run as many samples with Scalacheck. --- laws/src/main/scala/cats/laws/discipline/Eq.scala | 2 +- tests/src/test/scala/cats/tests/CatsSuite.scala | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/laws/src/main/scala/cats/laws/discipline/Eq.scala b/laws/src/main/scala/cats/laws/discipline/Eq.scala index d79a1a6a84..b422081670 100644 --- a/laws/src/main/scala/cats/laws/discipline/Eq.scala +++ b/laws/src/main/scala/cats/laws/discipline/Eq.scala @@ -13,7 +13,7 @@ object eq { */ implicit def function1Eq[A, B](implicit A: Arbitrary[A], B: Eq[B]): Eq[A => B] = new Eq[A => B] { def eqv(f: A => B, g: A => B): Boolean = { - val samples = List.fill(100)(A.arbitrary.sample).collect{ + val samples = List.fill(10)(A.arbitrary.sample).collect{ case Some(a) => a case None => sys.error("Could not generate arbitrary values to compare two functions") } diff --git a/tests/src/test/scala/cats/tests/CatsSuite.scala b/tests/src/test/scala/cats/tests/CatsSuite.scala index 6672095eb9..3fde4a3d25 100644 --- a/tests/src/test/scala/cats/tests/CatsSuite.scala +++ b/tests/src/test/scala/cats/tests/CatsSuite.scala @@ -6,7 +6,7 @@ import catalysts.Platform import cats.std.AllInstances import cats.syntax.{AllSyntax, EqOps} -import org.scalactic.anyvals.{PosZDouble, PosInt} +import org.scalactic.anyvals.{PosZDouble, PosInt, PosZInt} import org.scalatest.{FunSuite, Matchers} import org.scalatest.prop.{Configuration, GeneratorDrivenPropertyChecks} import org.typelevel.discipline.scalatest.Discipline @@ -20,8 +20,11 @@ trait TestSettings extends Configuration with Matchers { lazy val checkConfiguration: PropertyCheckConfiguration = PropertyCheckConfiguration( - minSuccessful = if (Platform.isJvm) PosInt(100) else PosInt(10), - maxDiscardedFactor = if (Platform.isJvm) PosZDouble(5.0) else PosZDouble(50.0)) + minSuccessful = PosInt(5), + maxDiscardedFactor = if (Platform.isJvm) PosZDouble(5.0) else PosZDouble(50.0), + minSize = PosZInt(0), + sizeRange = PosZInt(5), + workers = PosInt(1)) lazy val slowCheckConfiguration: PropertyCheckConfiguration = if (Platform.isJvm) checkConfiguration From ed5e72274bab184845a7b0e0196be92edd2cbdc6 Mon Sep 17 00:00:00 2001 From: Binh Nguyen Date: Mon, 28 Mar 2016 07:24:20 +0700 Subject: [PATCH 043/185] Enable `batchMode` on scala.js Optimizer The batchMode should help reduce the memory requirement to compile scala.js code as per https://github.com/scala-js/scala-js/issues/1788#issuecomment-178346142 Implementation details: botBuild is a sbt setting that will be turned on on travisCi so that it will not interfere with local dev experience. --- .travis.yml | 1 + build.sbt | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dd2a74d4c0..b56b245cde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ env: - secure: Kf44XQFpq2QGe3rn98Dsf5Uz3WXzPDralS54co7sqT5oQGs1mYLYZRYz+I75ZSo5ffZ86H7M+AI9YFofqGwAjBixBbqf1tGkUh3oZp2fN3QfqzazGV3HzC+o41zALG5FL+UBaURev9ChQ5fYeTtFB7YAzejHz4y5E97awk934Rg= - secure: QbNAu0jCaKrwjJi7KZtYEBA/pYbTJ91Y1x/eLAJpsamswVOvwnThA/TLYuux+oiZQCiDUpBzP3oxksIrEEUAhl0lMtqRFY3MrcUr+si9NIjX8hmoFwkvZ5o1b7pmLF6Vz3rQeP/EWMLcljLzEwsrRXeK0Ei2E4vFpsg8yz1YXJg= - TRAVIS_NODE_VERSION="4" + - CATS_BOT_BUILD=true cache: directories: - $HOME/.sbt/0.13/dependency diff --git a/build.sbt b/build.sbt index 7ca986daf7..1b1196f096 100644 --- a/build.sbt +++ b/build.sbt @@ -5,6 +5,8 @@ import sbtunidoc.Plugin.UnidocKeys._ import ReleaseTransformations._ import ScoverageSbtPlugin._ +lazy val botBuild = settingKey[Boolean]("Build by TravisCI instead of local dev environment") + lazy val scoverageSettings = Seq( ScoverageKeys.coverageMinimum := 60, ScoverageKeys.coverageFailOnMinimum := false, @@ -59,7 +61,11 @@ lazy val commonJsSettings = Seq( // Using Rhino as jsEnv to build scala.js code can lead to OOM, switch to PhantomJS by default scalaJSUseRhino := false, requiresDOM := false, - jsEnv := NodeJSEnv().value + jsEnv := NodeJSEnv().value, + // Only used for scala.js for now + botBuild := sys.props.getOrElse("CATS_BOT_BUILD", default="false") == "true", + // batch mode decreases the amount of memory needed to compile scala.js code + scalaJSOptimizerOptions := scalaJSOptimizerOptions.value.withBatchMode(botBuild.value) ) lazy val commonJvmSettings = Seq( From 372633c5104e078dd7c4643e923c4d94558a72fb Mon Sep 17 00:00:00 2001 From: Adelbert Chang Date: Sun, 27 Mar 2016 18:22:31 -0700 Subject: [PATCH 044/185] Rename Id instances to idInstances to make selective import easier, fixes #945 --- core/src/main/scala/cats/package.scala | 2 +- tests/src/test/scala/cats/tests/InjectTests.scala | 6 +++--- .../scala/cats/tests/NaturalTransformationTests.scala | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/main/scala/cats/package.scala b/core/src/main/scala/cats/package.scala index fa7b86f1e4..393ecc7004 100644 --- a/core/src/main/scala/cats/package.scala +++ b/core/src/main/scala/cats/package.scala @@ -26,7 +26,7 @@ package object cats { * encodes pure unary function application. */ type Id[A] = A - implicit val Id: Bimonad[Id] with Traverse[Id] = + implicit val idInstances: Bimonad[Id] with Traverse[Id] = new Bimonad[Id] with Traverse[Id] { def pure[A](a: A): A = a def extract[A](a: A): A = a diff --git a/tests/src/test/scala/cats/tests/InjectTests.scala b/tests/src/test/scala/cats/tests/InjectTests.scala index 6887e84aa6..d174180d9c 100644 --- a/tests/src/test/scala/cats/tests/InjectTests.scala +++ b/tests/src/test/scala/cats/tests/InjectTests.scala @@ -41,13 +41,13 @@ class InjectTests extends CatsSuite { object Test1Interpreter extends (Test1Algebra ~> Id) { override def apply[A](fa: Test1Algebra[A]): Id[A] = fa match { - case Test1(k, h) => Id.pure[A](h(k)) + case Test1(k, h) => h(k) } } object Test2Interpreter extends (Test2Algebra ~> Id) { override def apply[A](fa: Test2Algebra[A]): Id[A] = fa match { - case Test2(k, h) => Id.pure[A](h(k)) + case Test2(k, h) => h(k) } } @@ -65,7 +65,7 @@ class InjectTests extends CatsSuite { b <- Free.inject[Test2Algebra, F](Test2(y, identity)) } yield a + b } - (res[T] foldMap coProductInterpreter) == Id.pure(x + y) should ===(true) + (res[T] foldMap coProductInterpreter) == (x + y) should ===(true) } } diff --git a/tests/src/test/scala/cats/tests/NaturalTransformationTests.scala b/tests/src/test/scala/cats/tests/NaturalTransformationTests.scala index 4a70916c91..b46fa0026b 100644 --- a/tests/src/test/scala/cats/tests/NaturalTransformationTests.scala +++ b/tests/src/test/scala/cats/tests/NaturalTransformationTests.scala @@ -29,11 +29,11 @@ class NaturalTransformationTests extends CatsSuite { case class Test2[A](v : A) extends Test2Algebra[A] object Test1NT extends (Test1Algebra ~> Id) { - override def apply[A](fa: Test1Algebra[A]): Id[A] = Id.pure(fa.v) + override def apply[A](fa: Test1Algebra[A]): Id[A] = fa.v } object Test2NT extends (Test2Algebra ~> Id) { - override def apply[A](fa: Test2Algebra[A]): Id[A] = Id.pure(fa.v) + override def apply[A](fa: Test2Algebra[A]): Id[A] = fa.v } type T[A] = Coproduct[Test1Algebra, Test2Algebra, A] @@ -61,8 +61,8 @@ class NaturalTransformationTests extends CatsSuite { test("or") { val combinedInterpreter = Test1NT or Test2NT forAll { (a : Int, b : Int) => - combinedInterpreter(Coproduct.left(Test1(a))) should === (Id.pure(a)) - combinedInterpreter(Coproduct.right(Test2(b))) should === (Id.pure(b)) + combinedInterpreter(Coproduct.left(Test1(a))) should === (a) + combinedInterpreter(Coproduct.right(Test2(b))) should === (b) } } } From 1f05efea73fe62e732f0af8fcc605e837715940d Mon Sep 17 00:00:00 2001 From: Binh Nguyen Date: Mon, 28 Mar 2016 12:07:27 +0700 Subject: [PATCH 045/185] Upgrade scala.js from 0.6.7 -> 0.6.8 --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 4e6d3041c1..6c3ba461f7 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -8,6 +8,6 @@ addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.3") addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.2.0") addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.4") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.7") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.8") addSbtPlugin("com.github.tkawachi" % "sbt-doctest" % "0.3.5") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1") From 33bdd041b323ff01b7c27407aecec193cd72234f Mon Sep 17 00:00:00 2001 From: Adelbert Chang Date: Mon, 28 Mar 2016 00:59:25 -0700 Subject: [PATCH 046/185] Remove List from some property checks, maybe Scala.JS doesn't like them Also removed unneeded Eq[String] --- laws/src/main/scala/cats/laws/discipline/Eq.scala | 4 +--- .../src/test/scala/cats/tests/BitraverseTests.scala | 2 +- tests/src/test/scala/cats/tests/FunctorTests.scala | 13 +++++++------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/laws/src/main/scala/cats/laws/discipline/Eq.scala b/laws/src/main/scala/cats/laws/discipline/Eq.scala index 3e5769486e..ed5879bb22 100644 --- a/laws/src/main/scala/cats/laws/discipline/Eq.scala +++ b/laws/src/main/scala/cats/laws/discipline/Eq.scala @@ -3,6 +3,7 @@ package laws package discipline import algebra.Eq +import cats.std.string._ import org.scalacheck.Arbitrary object eq { @@ -27,9 +28,6 @@ object eq { (a: A) => showInstance.show(a) } - // Temporary, see https://github.com/non/algebra/pull/143 - implicit val stringEq: Eq[String] = Eq.fromUniversalEquals - // Temporary, see https://github.com/non/algebra/pull/82 implicit def tuple2Eq[A, B](implicit A: Eq[A], B: Eq[B]): Eq[(A, B)] = new Eq[(A, B)] { diff --git a/tests/src/test/scala/cats/tests/BitraverseTests.scala b/tests/src/test/scala/cats/tests/BitraverseTests.scala index 24b5a01cd3..e981925d80 100644 --- a/tests/src/test/scala/cats/tests/BitraverseTests.scala +++ b/tests/src/test/scala/cats/tests/BitraverseTests.scala @@ -11,6 +11,6 @@ class BitraverseTest extends CatsSuite { val xorComposeTuple2: Bitraverse[XorTuple2] = Bitraverse[Xor].compose[Tuple2] - checkAll("Xor compose Tuple2", BitraverseTests(xorComposeTuple2).bitraverse[List, Int, Int, Int, String, String, String]) + checkAll("Xor compose Tuple2", BitraverseTests(xorComposeTuple2).bitraverse[Option, Int, Int, Int, String, String, String]) checkAll("Bitraverse[Xor compose Tuple2]", SerializableTests.serializable(xorComposeTuple2)) } diff --git a/tests/src/test/scala/cats/tests/FunctorTests.scala b/tests/src/test/scala/cats/tests/FunctorTests.scala index 6c9a2b2c77..40ee82e96c 100644 --- a/tests/src/test/scala/cats/tests/FunctorTests.scala +++ b/tests/src/test/scala/cats/tests/FunctorTests.scala @@ -1,18 +1,19 @@ package cats package tests +import cats.data.Xor import cats.functor.Contravariant import cats.laws.discipline.{ContravariantTests, FunctorTests, SerializableTests} -import cats.laws.discipline.arbitrary.showArbitrary +import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq.showEq class FunctorTest extends CatsSuite { - type OptionList[A] = Option[List[A]] + type OptionXor[A] = Option[Xor[String, A]] - val optionListFunctor: Functor[OptionList] = - Functor[Option].compose[List] - checkAll("Option compose List", FunctorTests(optionListFunctor).functor[Int, Int, Int]) - checkAll("Functor[Option compose List]", SerializableTests.serializable(optionListFunctor)) + val optionXorFunctor: Functor[OptionXor] = + Functor[Option].compose[Xor[String, ?]] + checkAll("Option compose Xor", FunctorTests(optionXorFunctor).functor[Int, Int, Int]) + checkAll("Functor[Option compose Xor]", SerializableTests.serializable(optionXorFunctor)) type OptionShow[A] = Option[Show[A]] val optionShowContravariant: Contravariant[OptionShow] = From 5b162903ffb008b847ca8cee880fa5b2851c5678 Mon Sep 17 00:00:00 2001 From: Mike Curry Date: Mon, 28 Mar 2016 12:12:40 +0100 Subject: [PATCH 047/185] Switch off scaladoc generation for Scala 2.10 due to macro problems --- build.sbt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 1b1196f096..a20a1b7448 100644 --- a/build.sbt +++ b/build.sbt @@ -83,9 +83,21 @@ lazy val disciplineDependencies = Seq( libraryDependencies += "org.typelevel" %%% "discipline" % "0.4" ) +/** + * Remove 2.10 projects from doc generation, as the macros used in the projects + * cause problems generating the documentation on scala 2.10. As the APIs for 2.10 + * and 2.11 are the same this has no effect on the resultant documentation, though + * it does mean that the scaladocs cannot be generated when the build is in 2.10 mode. + */ +def noDocProjects(sv: String): Seq[ProjectReference] = CrossVersion.partialVersion(sv) match { + case Some((2, 10)) => Seq[ProjectReference](coreJVM) + case _ => Nil + } + lazy val docSettings = Seq( autoAPIMappings := true, - unidocProjectFilter in (ScalaUnidoc, unidoc) := inProjects(coreJVM), + unidocProjectFilter in (ScalaUnidoc, unidoc) := + inProjects(coreJVM) -- inProjects(noDocProjects(scalaVersion.value): _*), site.addMappingsToSiteDir(mappings in (ScalaUnidoc, packageDoc), "api"), site.addMappingsToSiteDir(tut, "_tut"), ghpagesNoJekyll := false, From ce1ce966e91abed7d15270bd32c44ec29e05da5b Mon Sep 17 00:00:00 2001 From: Mike Curry Date: Mon, 28 Mar 2016 17:20:34 +0100 Subject: [PATCH 048/185] Disable scaladoc during packaging for scala 2.10 Disables scaladoc generation during publish phase for scala 2.10 --- build.sbt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.sbt b/build.sbt index a20a1b7448..e0f4d915ab 100644 --- a/build.sbt +++ b/build.sbt @@ -231,6 +231,12 @@ lazy val publishSettings = Seq( scmInfo := Some(ScmInfo(url("https://github.com/typelevel/cats"), "scm:git:git@github.com:typelevel/cats.git")), autoAPIMappings := true, apiURL := Some(url("http://typelevel.org/cats/api/")), + publishArtifact in (Compile, packageDoc) := { + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 10)) => false // don't package scaladoc when publishing for 2.10 + case _ => true + } + }, pomExtra := ( From f05d77228bd19b6980df40026d75b72809c7c1ac Mon Sep 17 00:00:00 2001 From: Adelbert Chang Date: Mon, 28 Mar 2016 11:29:16 -0700 Subject: [PATCH 049/185] More Reducible tests --- core/src/main/scala/cats/Reducible.scala | 2 +- .../main/scala/cats/laws/ReducibleLaws.scala | 6 +++ .../cats/laws/discipline/ReducibleTests.scala | 9 +++- .../test/scala/cats/tests/OneAndTests.scala | 49 ++++++++++++++++++- .../scala/cats/tests/ReducibleTests.scala | 18 +++++++ 5 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 tests/src/test/scala/cats/tests/ReducibleTests.scala diff --git a/core/src/main/scala/cats/Reducible.scala b/core/src/main/scala/cats/Reducible.scala index 20a38bf1ae..2f92905e38 100644 --- a/core/src/main/scala/cats/Reducible.scala +++ b/core/src/main/scala/cats/Reducible.scala @@ -97,7 +97,7 @@ import simulacrum.typeclass * that we only need `Apply[G]` here, since we don't need to call * `Applicative#pure` for a starting value. */ - def sequence1_[G[_], A, B](fga: F[G[A]])(implicit G: Apply[G]): G[Unit] = + def sequence1_[G[_], A](fga: F[G[A]])(implicit G: Apply[G]): G[Unit] = G.map(reduceLeft(fga)((x, y) => G.map2(x, y)((_, b) => b)))(_ => ()) /** diff --git a/laws/src/main/scala/cats/laws/ReducibleLaws.scala b/laws/src/main/scala/cats/laws/ReducibleLaws.scala index 5075a97ab6..937a23c35d 100644 --- a/laws/src/main/scala/cats/laws/ReducibleLaws.scala +++ b/laws/src/main/scala/cats/laws/ReducibleLaws.scala @@ -21,6 +21,12 @@ trait ReducibleLaws[F[_]] extends FoldableLaws[F] { B: Semigroup[B] ): IsEq[B] = fa.reduceMap(f) <-> fa.reduceRightTo(f)((a, eb) => eb.map(f(a) |+| _)).value + + def traverseConsistent[G[_]: Applicative, A, B](fa: F[A], f: A => G[B]): IsEq[G[Unit]] = + fa.traverse1_(f) <-> fa.traverse_(f) + + def sequenceConsistent[G[_]: Applicative, A](fa: F[G[A]]): IsEq[G[Unit]] = + fa.sequence1_ <-> fa.sequence_ } object ReducibleLaws { diff --git a/laws/src/main/scala/cats/laws/discipline/ReducibleTests.scala b/laws/src/main/scala/cats/laws/discipline/ReducibleTests.scala index cb1f343aa6..4f7db2d858 100644 --- a/laws/src/main/scala/cats/laws/discipline/ReducibleTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/ReducibleTests.scala @@ -8,16 +8,21 @@ import org.scalacheck.Prop.forAll trait ReducibleTests[F[_]] extends FoldableTests[F] { def laws: ReducibleLaws[F] - def reducible[A: Arbitrary, B: Arbitrary](implicit + def reducible[G[_]: Applicative, A: Arbitrary, B: Arbitrary](implicit ArbFA: Arbitrary[F[A]], + ArbFGA: Arbitrary[F[G[A]]], + ArbGB: Arbitrary[G[B]], B: Monoid[B], + EqG: Eq[G[Unit]], EqB: Eq[B] ): RuleSet = new DefaultRuleSet( name = "reducible", parent = Some(foldable[A, B]), "reduceLeftTo consistent with reduceMap" -> forAll(laws.reduceLeftToConsistentWithReduceMap[A, B] _), - "reduceRightTo consistent with reduceMap" -> forAll(laws.reduceRightToConsistentWithReduceMap[A, B] _) + "reduceRightTo consistent with reduceMap" -> forAll(laws.reduceRightToConsistentWithReduceMap[A, B] _), + "traverse1_ consistent with traverse_" -> forAll(laws.traverseConsistent[G, A, B] _), + "sequence1_ consistent with sequence_" -> forAll(laws.sequenceConsistent[G, A] _) ) } diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index 29768816ed..db94b3d14a 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -5,16 +5,20 @@ import algebra.laws.{GroupLaws, OrderLaws} import cats.data.{NonEmptyList, OneAnd} import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, CartesianTests, TraverseTests, ReducibleTests} -import cats.laws.discipline.arbitrary.oneAndArbitrary +import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq._ class OneAndTests extends CatsSuite { + // Lots of collections here.. telling ScalaCheck to calm down a bit + implicit override val generatorDrivenConfig: PropertyCheckConfiguration = + PropertyCheckConfig(maxSize = 5, minSuccessful = 20) + checkAll("OneAnd[List, Int]", OrderLaws[OneAnd[List, Int]].eqv) checkAll("OneAnd[List, Int] with Option", TraverseTests[OneAnd[List, ?]].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[OneAnd[List, A]]", SerializableTests.serializable(Traverse[OneAnd[List, ?]])) - checkAll("OneAnd[List, Int]", ReducibleTests[OneAnd[List, ?]].reducible[Int, Int]) + checkAll("OneAnd[List, Int]", ReducibleTests[OneAnd[List, ?]].reducible[Option, Int, Int]) checkAll("Reducible[OneAnd[List, ?]]", SerializableTests.serializable(Reducible[OneAnd[List, ?]])) implicit val iso = CartesianTests.Isomorphisms.invariant[OneAnd[ListWrapper, ?]](OneAnd.oneAndFunctor(ListWrapper.functor)) @@ -117,4 +121,45 @@ class OneAndTests extends CatsSuite { nel.map(p).unwrap should === (list.map(p)) } } + + test("reduceLeft consistent with foldLeft") { + forAll { (nel: NonEmptyList[Int], f: (Int, Int) => Int) => + nel.reduceLeft(f) should === (nel.tail.foldLeft(nel.head)(f)) + } + } + + test("reduceRight consistent with foldRight") { + forAll { (nel: NonEmptyList[Int], f: (Int, Eval[Int]) => Eval[Int]) => + nel.reduceRight(f).value should === (nel.tail.foldRight(nel.head)((a, b) => f(a, Now(b)).value)) + } + } + + test("reduce consistent with fold") { + forAll { (nel: NonEmptyList[Int]) => + nel.reduce should === (nel.fold) + } + } + + test("reduce consistent with reduceK") { + forAll { (nel: NonEmptyList[Option[Int]]) => + nel.reduce(SemigroupK[Option].algebra[Int]) should === (nel.reduceK) + } + } + + test("reduceLeftToOption consistent with foldLeft + Option") { + forAll { (nel: NonEmptyList[Int], f: Int => String, g: (String, Int) => String) => + val expected = nel.tail.foldLeft(Option(f(nel.head))) { (opt, i) => + opt.map(s => g(s, i)) + } + } + } + + test("reduceRightToOption consistent with foldRight + Option") { + forAll { (nel: NonEmptyList[Int], f: Int => String, g: (Int, Eval[String]) => Eval[String]) => + val expected = nel.tail.foldRight(Option(f(nel.head))) { (i, opt) => + opt.map(s => g(i, Now(s)).value) + } + nel.reduceRightToOption(f)(g).value should === (expected) + } + } } diff --git a/tests/src/test/scala/cats/tests/ReducibleTests.scala b/tests/src/test/scala/cats/tests/ReducibleTests.scala new file mode 100644 index 0000000000..d834a99e02 --- /dev/null +++ b/tests/src/test/scala/cats/tests/ReducibleTests.scala @@ -0,0 +1,18 @@ +package cats +package tests + +import cats.data.{NonEmptyList, NonEmptyVector} +import cats.laws.discipline.{ReducibleTests, SerializableTests} +import cats.laws.discipline.arbitrary.oneAndArbitrary + +class ReducibleTest extends CatsSuite { + // Lots of collections here.. telling ScalaCheck to calm down a bit + implicit override val generatorDrivenConfig: PropertyCheckConfiguration = + PropertyCheckConfig(maxSize = 5, minSuccessful = 20) + + type NEVL[A] = NonEmptyList[NonEmptyVector[A]] + val nevlReducible: Reducible[NEVL] = + Reducible[NonEmptyList].compose[NonEmptyVector] + checkAll("NonEmptyList compose NonEmptyVector", ReducibleTests(nevlReducible).reducible[Option, Int, String]) + checkAll("Reducible[NonEmptyList compose NonEmptyVector]", SerializableTests.serializable(nevlReducible)) +} From e80ccd20bfb372ddce4c4039b060bc73f2725880 Mon Sep 17 00:00:00 2001 From: Adelbert Chang Date: Mon, 28 Mar 2016 12:45:45 -0700 Subject: [PATCH 050/185] Actually test reduceLeftToOption --- tests/src/test/scala/cats/tests/OneAndTests.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index db94b3d14a..ad711ea410 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -151,6 +151,7 @@ class OneAndTests extends CatsSuite { val expected = nel.tail.foldLeft(Option(f(nel.head))) { (opt, i) => opt.map(s => g(s, i)) } + nel.reduceLeftToOption(f)(g) should === (expected) } } From b4079734955ac8fc1f6d30e2113fe70ca00c871c Mon Sep 17 00:00:00 2001 From: David Gregory Date: Mon, 28 Mar 2016 22:14:54 +0100 Subject: [PATCH 051/185] Improving test coverage --- .../test/scala/cats/tests/FutureTests.scala | 7 ++++++ .../test/scala/cats/tests/FunctorTests.scala | 22 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/jvm/src/test/scala/cats/tests/FutureTests.scala b/jvm/src/test/scala/cats/tests/FutureTests.scala index 21a73f9519..58a46e13dd 100644 --- a/jvm/src/test/scala/cats/tests/FutureTests.scala +++ b/jvm/src/test/scala/cats/tests/FutureTests.scala @@ -4,6 +4,7 @@ package tests import cats.data.Xor import cats.laws.discipline._ +import cats.laws.discipline.arbitrary.evalArbitrary import cats.laws.discipline.eq.tuple3Eq import cats.jvm.std.future.futureComonad import cats.tests.CatsSuite @@ -40,4 +41,10 @@ class FutureTests extends CatsSuite { checkAll("Future[Int]", MonadErrorTests[Future, Throwable].monadError[Int, Int, Int]) checkAll("Future[Int]", ComonadTests[Future].comonad[Int, Int, Int]) + + test("pureEval lifts a potentially lazy value into Future") { + forAll { e: Eval[Int] => + e.pureEval[Future].extract should === (e.value) + } + } } diff --git a/tests/src/test/scala/cats/tests/FunctorTests.scala b/tests/src/test/scala/cats/tests/FunctorTests.scala index 40ee82e96c..d934e0a39c 100644 --- a/tests/src/test/scala/cats/tests/FunctorTests.scala +++ b/tests/src/test/scala/cats/tests/FunctorTests.scala @@ -5,6 +5,7 @@ import cats.data.Xor import cats.functor.Contravariant import cats.laws.discipline.{ContravariantTests, FunctorTests, SerializableTests} import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq._ import cats.laws.discipline.eq.showEq class FunctorTest extends CatsSuite { @@ -15,9 +16,30 @@ class FunctorTest extends CatsSuite { checkAll("Option compose Xor", FunctorTests(optionXorFunctor).functor[Int, Int, Int]) checkAll("Functor[Option compose Xor]", SerializableTests.serializable(optionXorFunctor)) + val optionComposeWithFunctorXor: Functor[OptionXor] = + Functor[Option].composeWithFunctor[Xor[String, ?]] + checkAll("Option composeWithFunctor Xor", FunctorTests(optionComposeWithFunctorXor).functor[Int, Int, Int]) + checkAll("Functor[Option composeWithFunctor Xor]", SerializableTests.serializable(optionComposeWithFunctorXor)) + type OptionShow[A] = Option[Show[A]] val optionShowContravariant: Contravariant[OptionShow] = Functor[Option].composeWithContravariant[Show] checkAll("Option composeWithContravariant Show", ContravariantTests(optionShowContravariant).contravariant[Int, Int, Int]) checkAll("Contravariant[Option composeWithContravariant Show]", SerializableTests.serializable(optionShowContravariant)) + + test("void replaces values with unit preserving structure") { + forAll { (l: List[Int], o: Option[Int], m: Map[String, Int]) => + l.void should === (List.fill(l.length)(())) + o.void should === (if (o.nonEmpty) Some(()) else None) + m.void should === (m.keys.map(k => (k, ())).toMap) + } + } + + test("as replaces values with a constant value preserving structure") { + forAll { (l: List[Int], o: Option[Int], m: Map[String, Int], i: Int) => + l.as(i) should === (List.fill(l.length)(i)) + o.as(i) should === (if (o.nonEmpty) Some(i) else None) + m.as(i) should === (m.keys.map(k => (k, i)).toMap) + } + } } From b3e8ac7067518cc31f9417d225c252647386a449 Mon Sep 17 00:00:00 2001 From: Max Worgan Date: Tue, 29 Mar 2016 09:49:04 +0100 Subject: [PATCH 052/185] Added link to Applicative in tut --- docs/src/main/tut/optiont.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/tut/optiont.md b/docs/src/main/tut/optiont.md index a771840ea6..9125b0fcbf 100644 --- a/docs/src/main/tut/optiont.md +++ b/docs/src/main/tut/optiont.md @@ -74,7 +74,7 @@ val result: Future[Option[String]] = ot.value // Future(Some("Hello Jane Doe")) ## From `A` to `OptionT[F,A]` -If you have only an `A` and you wish to *lift* it into an `OptionT[F,A]` assuming you have an applicative instance for F you can use `some` which is an alias for `pure`. There also exists a `none` method which can be used to create an OptionT[F,A], where the Option wrapped A type is actually a `None`: +If you have only an `A` and you wish to *lift* it into an `OptionT[F,A]` assuming you have an [`Applicative`]({{ site.baseurl }}/tut/applicative.html) instance for `F` you can use `some` which is an alias for `pure`. There also exists a `none` method which can be used to create an `OptionT[F,A]`, where the `Option` wrapped `A` type is actually a `None`: ```tut:silent From 4ba397c1262dcb24e9ae448015a71dbc87b8de7e Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Sat, 2 Apr 2016 10:02:29 +0100 Subject: [PATCH 053/185] Clarify stabilty guarantees; drop 'proof of concept' and 'experimental'. --- README.md | 3 +-- docs/src/site/index.md | 19 +++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8903763812..c85f960907 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,7 @@ ### Overview -Cats is a proof-of-concept library intended to provide abstractions -for functional programming in Scala. +Cats is a library intended to provide abstractions for functional programming in Scala. The name is a playful shortening of the word *category*. diff --git a/docs/src/site/index.md b/docs/src/site/index.md index 262ce21457..9941044fdd 100644 --- a/docs/src/site/index.md +++ b/docs/src/site/index.md @@ -3,16 +3,15 @@ layout: default title: "Home" section: "home" --- -Cats is an experimental library intended to provide abstractions for -functional programming in the -[Scala programming language](https://scala-lang.org). The name is a -playful shortening of the word *category*. - -

Cats is currently an experimental - project under active development. Feedback and - contributions are welcomed as we look to improve the project. This - project is evolving quickly and we currently make no guarantees about what - might drastically change in the near future.

+Cats is a library intended to provide abstractions for functional +programming in the [Scala programming language](https://scala-lang.org). +The name is a playful shortening of the word *category*. + +

Cats is a new project under active + development. Feedback and contributions are welcomed as we look + to improve the it. This project is evolving quickly and we are making no + guarantees about stability until a 1.0 release is made (current est. + around Q3 2016).

From 2a4c0183940d285fe8614932e0490c0d1a045382 Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Sat, 2 Apr 2016 10:54:25 +0100 Subject: [PATCH 054/185] Fixed typo. --- docs/src/site/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/site/index.md b/docs/src/site/index.md index 9941044fdd..ca7f7b725c 100644 --- a/docs/src/site/index.md +++ b/docs/src/site/index.md @@ -9,7 +9,7 @@ The name is a playful shortening of the word *category*.

Cats is a new project under active development. Feedback and contributions are welcomed as we look - to improve the it. This project is evolving quickly and we are making no + to improve it. This project is evolving quickly and we are making no guarantees about stability until a 1.0 release is made (current est. around Q3 2016).

From dfe9a567cff2a6a6737e4178a3e5000e8023932e Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Sat, 2 Apr 2016 13:57:02 +0100 Subject: [PATCH 055/185] Actions trump intentions. --- README.md | 2 +- docs/src/site/index.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c85f960907..b10091d2f3 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ ### Overview -Cats is a library intended to provide abstractions for functional programming in Scala. +Cats is a library which provides abstractions for functional programming in Scala. The name is a playful shortening of the word *category*. diff --git a/docs/src/site/index.md b/docs/src/site/index.md index ca7f7b725c..b43c1f3671 100644 --- a/docs/src/site/index.md +++ b/docs/src/site/index.md @@ -3,7 +3,7 @@ layout: default title: "Home" section: "home" --- -Cats is a library intended to provide abstractions for functional +Cats is a library which provides abstractions for functional programming in the [Scala programming language](https://scala-lang.org). The name is a playful shortening of the word *category*. From 4226c6599d41a0f8ef762bbe8e0fdfb69f5d4016 Mon Sep 17 00:00:00 2001 From: Daniel Spiewak Date: Sat, 2 Apr 2016 15:47:05 -0700 Subject: [PATCH 056/185] Fixed old comment referencing pre-Trivial Trivial --- core/src/main/scala/cats/TransLift.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/TransLift.scala b/core/src/main/scala/cats/TransLift.scala index a11f440f34..7901c55925 100644 --- a/core/src/main/scala/cats/TransLift.scala +++ b/core/src/main/scala/cats/TransLift.scala @@ -11,8 +11,8 @@ trait TransLift[MT[_[_], _]] { * The typeclass which constrains liftT as a function of the type * constructor it is given. A safe "identity" value for this type * if your transformer does not constrain its lifted effects would - * be `type TC[M[_]] = Unit =:= Unit`. A more common constraint - * might be `type TC[M[_]] = Monad[M]`. + * be `type TC[M[_]] = Trivial`. A more common constraint might be + * `type TC[M[_]] = Monad[M]`. */ type TC[M[_]] From 051fb845d6219784de2fe231d8bfecfc1f880fb9 Mon Sep 17 00:00:00 2001 From: Adelbert Chang Date: Sun, 3 Apr 2016 17:40:23 -0700 Subject: [PATCH 057/185] product and map can be implemented in terms of ap, fixes #904 --- core/src/main/scala/cats/Applicative.scala | 3 +++ core/src/main/scala/cats/Apply.scala | 6 ++++-- core/src/main/scala/cats/data/Const.scala | 6 +++--- core/src/main/scala/cats/data/Func.scala | 8 ++++---- core/src/main/scala/cats/data/Kleisli.scala | 6 +++--- core/src/main/scala/cats/data/Prod.scala | 4 ++-- core/src/main/scala/cats/data/Validated.scala | 2 +- core/src/main/scala/cats/data/WriterT.scala | 2 +- core/src/main/scala/cats/free/FreeApplicative.scala | 4 ++-- docs/src/main/tut/apply.md | 6 ------ docs/src/main/tut/const.md | 10 ---------- docs/src/main/tut/validated.md | 3 --- laws/src/main/scala/cats/laws/TraverseLaws.scala | 4 ++-- 13 files changed, 25 insertions(+), 39 deletions(-) diff --git a/core/src/main/scala/cats/Applicative.scala b/core/src/main/scala/cats/Applicative.scala index 3c3eb683f0..41532316cf 100644 --- a/core/src/main/scala/cats/Applicative.scala +++ b/core/src/main/scala/cats/Applicative.scala @@ -29,6 +29,9 @@ import cats.std.list._ */ def pureEval[A](x: Eval[A]): F[A] = pure(x.value) + override def map[A, B](fa: F[A])(f: A => B): F[B] = + ap(pure(f))(fa) + /** * Given `fa` and `n`, apply `fa` `n` times to construct an `F[List[A]]` value. */ diff --git a/core/src/main/scala/cats/Apply.scala b/core/src/main/scala/cats/Apply.scala index 87e652278e..f70b153ce5 100644 --- a/core/src/main/scala/cats/Apply.scala +++ b/core/src/main/scala/cats/Apply.scala @@ -16,6 +16,9 @@ trait Apply[F[_]] extends Functor[F] with Cartesian[F] with ApplyArityFunctions[ */ def ap[A, B](ff: F[A => B])(fa: F[A]): F[B] + override def product[A, B](fa: F[A], fb: F[B]): F[(A, B)] = + ap(map(fa)(a => (b: B) => (a, b)))(fb) + /** * ap2 is a binary version of ap, defined in terms of ap. */ @@ -56,7 +59,6 @@ trait CompositeApply[F[_], 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) - def product[A, B](fa: F[G[A]], fb: F[G[B]]): F[G[(A, B)]] = + override def product[A, B](fa: F[G[A]], fb: F[G[B]]): F[G[(A, B)]] = F.map2(fa, fb)(G.product) - } diff --git a/core/src/main/scala/cats/data/Const.scala b/core/src/main/scala/cats/data/Const.scala index 692f6436f6..32fc1e3b91 100644 --- a/core/src/main/scala/cats/data/Const.scala +++ b/core/src/main/scala/cats/data/Const.scala @@ -98,10 +98,10 @@ private[data] sealed abstract class ConstInstances0 extends ConstInstances1 { def ap[A, B](f: Const[C, A => B])(fa: Const[C, A]): Const[C, B] = f.retag[B] combine fa.retag[B] - def map[A, B](fa: Const[C, A])(f: A => B): Const[C, B] = + override def map[A, B](fa: Const[C, A])(f: A => B): Const[C, B] = fa.retag[B] - def product[A, B](fa: Const[C, A], fb: Const[C, B]): Const[C, (A, B)] = + override def product[A, B](fa: Const[C, A], fb: Const[C, B]): Const[C, (A, B)] = fa.retag[(A, B)] combine fb.retag[(A, B)] } } @@ -116,7 +116,7 @@ private[data] sealed abstract class ConstInstances1 { def ap[A, B](f: Const[C, A => B])(fa: Const[C, A]): Const[C, B] = fa.retag[B] combine f.retag[B] - def product[A, B](fa: Const[C, A], fb: Const[C, B]): Const[C, (A, B)] = + override def product[A, B](fa: Const[C, A], fb: Const[C, B]): Const[C, (A, B)] = fa.retag[(A, B)] combine fb.retag[(A, B)] def map[A, B](fa: Const[C, A])(f: A => B): Const[C, B] = diff --git a/core/src/main/scala/cats/data/Func.scala b/core/src/main/scala/cats/data/Func.scala index f5f3a74214..54487031ff 100644 --- a/core/src/main/scala/cats/data/Func.scala +++ b/core/src/main/scala/cats/data/Func.scala @@ -54,7 +54,7 @@ private[data] abstract class FuncInstances1 { sealed trait FuncFunctor[F[_], C] extends Functor[Lambda[X => Func[F, C, X]]] { def F: Functor[F] - def map[A, B](fa: Func[F, C, A])(f: A => B): Func[F, C, B] = + override def map[A, B](fa: Func[F, C, A])(f: A => B): Func[F, C, B] = fa.map(f)(F) } @@ -62,7 +62,7 @@ sealed trait FuncApply[F[_], C] extends Apply[Lambda[X => Func[F, C, X]]] with F def F: Apply[F] def ap[A, B](f: Func[F, C, A => B])(fa: Func[F, C, A]): Func[F, C, B] = Func.func(c => F.ap(f.run(c))(fa.run(c))) - def product[A, B](fa: Func[F, C, A], fb: Func[F, C, B]): Func[F, C, (A, B)] = + override def product[A, B](fa: Func[F, C, A], fb: Func[F, C, B]): Func[F, C, (A, B)] = Func.func(c => F.product(fa.run(c), fb.run(c))) } @@ -121,11 +121,11 @@ private[data] abstract class AppFuncInstances { private[data] sealed trait AppFuncApplicative[F[_], C] extends Applicative[Lambda[X => AppFunc[F, C, X]]] { def F: Applicative[F] - def map[A, B](fa: AppFunc[F, C, A])(f: A => B): AppFunc[F, C, B] = + override def map[A, B](fa: AppFunc[F, C, A])(f: A => B): AppFunc[F, C, B] = fa.map(f) def ap[A, B](f: AppFunc[F, C, A => B])(fa: AppFunc[F, C, A]): AppFunc[F, C, B] = Func.appFunc[F, C, B](c => F.ap(f.run(c))(fa.run(c)))(F) - def product[A, B](fa: AppFunc[F, C, A], fb: AppFunc[F, C, B]): AppFunc[F, C, (A, B)] = + override def product[A, B](fa: AppFunc[F, C, A], fb: AppFunc[F, C, B]): AppFunc[F, C, (A, B)] = Func.appFunc[F, C, (A, B)](c => F.product(fa.run(c), fb.run(c)))(F) def pure[A](a: A): AppFunc[F, C, A] = Func.appFunc[F, C, A](c => F.pure(a))(F) diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index f0e827d1e9..b548a60cfd 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -153,10 +153,10 @@ private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 def ap[B, C](f: Kleisli[F, A, B => C])(fa: Kleisli[F, A, B]): Kleisli[F, A, C] = fa(f) - def map[B, C](fb: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = + override def map[B, C](fb: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = fb.map(f) - def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] = + override def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] = Kleisli(a => Applicative[F].product(fb.run(a), fc.run(a))) } } @@ -166,7 +166,7 @@ private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3 def ap[B, C](f: Kleisli[F, A, B => C])(fa: Kleisli[F, A, B]): Kleisli[F, A, C] = fa(f) - def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] = + override def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] = Kleisli(a => Apply[F].product(fb.run(a), fc.run(a))) def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = diff --git a/core/src/main/scala/cats/data/Prod.scala b/core/src/main/scala/cats/data/Prod.scala index f368e1abef..c50fc321db 100644 --- a/core/src/main/scala/cats/data/Prod.scala +++ b/core/src/main/scala/cats/data/Prod.scala @@ -71,7 +71,7 @@ private[data] sealed abstract class ProdInstances4 { sealed trait ProdFunctor[F[_], G[_]] extends Functor[Lambda[X => Prod[F, G, X]]] { def F: Functor[F] def G: Functor[G] - def map[A, B](fa: Prod[F, G, A])(f: A => B): Prod[F, G, B] = Prod(F.map(fa.first)(f), G.map(fa.second)(f)) + override def map[A, B](fa: Prod[F, G, A])(f: A => B): Prod[F, G, B] = Prod(F.map(fa.first)(f), G.map(fa.second)(f)) } sealed trait ProdApply[F[_], G[_]] extends Apply[Lambda[X => Prod[F, G, X]]] with ProdFunctor[F, G] { @@ -79,7 +79,7 @@ sealed trait ProdApply[F[_], G[_]] extends Apply[Lambda[X => Prod[F, G, X]]] wit def G: Apply[G] def ap[A, B](f: Prod[F, G, A => B])(fa: Prod[F, G, A]): Prod[F, G, B] = Prod(F.ap(f.first)(fa.first), G.ap(f.second)(fa.second)) - def product[A, B](fa: Prod[F, G, A], fb: Prod[F, G, B]): Prod[F, G, (A, B)] = + override def product[A, B](fa: Prod[F, G, A], fb: Prod[F, G, B]): Prod[F, G, (A, B)] = Prod(F.product(fa.first, fb.first), G.product(fa.second, fb.second)) } diff --git a/core/src/main/scala/cats/data/Validated.scala b/core/src/main/scala/cats/data/Validated.scala index 3242858dfc..22f97f78a6 100644 --- a/core/src/main/scala/cats/data/Validated.scala +++ b/core/src/main/scala/cats/data/Validated.scala @@ -254,7 +254,7 @@ private[data] sealed abstract class ValidatedInstances extends ValidatedInstance def ap[A,B](f: Validated[E,A=>B])(fa: Validated[E,A]): Validated[E, B] = fa.ap(f)(E) - def product[A, B](fa: Validated[E, A], fb: Validated[E, B]): Validated[E, (A, B)] = + override def product[A, B](fa: Validated[E, A], fb: Validated[E, B]): Validated[E, (A, B)] = fa.product(fb)(E) def handleErrorWith[A](fa: Validated[E, A])(f: E => Validated[E, A]): Validated[E, A] = diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index dacba37d28..97b203ae26 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -171,7 +171,7 @@ private[data] sealed trait WriterTApply[F[_], L] extends WriterTFunctor[F, L] wi def ap[A, B](f: WriterT[F, L, A => B])(fa: WriterT[F, L, A]): WriterT[F, L, B] = fa ap f - def product[A, B](fa: WriterT[F, L, A], fb: WriterT[F, L, B]): WriterT[F, L, (A, B)] = + override def product[A, B](fa: WriterT[F, L, A], fb: WriterT[F, L, B]): WriterT[F, L, (A, B)] = WriterT(F0.map(F0.product(fa.run, fb.run)) { case ((l1, a), (l2, b)) => (L0.combine(l1, l2), (a, b)) }) } diff --git a/core/src/main/scala/cats/free/FreeApplicative.scala b/core/src/main/scala/cats/free/FreeApplicative.scala index d21d64ea11..ad9c57aac4 100644 --- a/core/src/main/scala/cats/free/FreeApplicative.scala +++ b/core/src/main/scala/cats/free/FreeApplicative.scala @@ -79,8 +79,8 @@ object FreeApplicative { implicit final def freeApplicative[S[_]]: Applicative[FA[S, ?]] = { new Applicative[FA[S, ?]] { - def product[A, B](fa: FA[S, A], fb: FA[S, B]): FA[S, (A, B)] = ap(fa.map((a: A) => (b: B) => (a, b)))(fb) - def map[A, B](fa: FA[S, A])(f: A => B): FA[S, B] = fa.map(f) + override def product[A, B](fa: FA[S, A], fb: FA[S, B]): FA[S, (A, B)] = ap(fa.map((a: A) => (b: B) => (a, b)))(fb) + override def map[A, B](fa: FA[S, A])(f: A => B): FA[S, B] = fa.map(f) override def ap[A, B](f: FA[S, A => B])(fa: FA[S, A]): FA[S, B] = fa.ap(f) def pure[A](a: A): FA[S, A] = Pure(a) } diff --git a/docs/src/main/tut/apply.md b/docs/src/main/tut/apply.md index 016af63227..173974f482 100644 --- a/docs/src/main/tut/apply.md +++ b/docs/src/main/tut/apply.md @@ -26,9 +26,6 @@ implicit val optionApply: Apply[Option] = new Apply[Option] { fa.flatMap (a => f.map (ff => ff(a))) def map[A,B](fa: Option[A])(f: A => B): Option[B] = fa map f - - def product[A, B](fa: Option[A], fb: Option[B]): Option[(A, B)] = - fa.flatMap(a => fb.map(b => (a, b))) } implicit val listApply: Apply[List] = new Apply[List] { @@ -36,9 +33,6 @@ implicit val listApply: Apply[List] = new Apply[List] { fa.flatMap (a => f.map (ff => ff(a))) def map[A,B](fa: List[A])(f: A => B): List[B] = fa map f - - def product[A, B](fa: List[A], fb: List[B]): List[(A, B)] = - fa.zip(fb) } ``` diff --git a/docs/src/main/tut/const.md b/docs/src/main/tut/const.md index 6673799abb..5a958c3f45 100644 --- a/docs/src/main/tut/const.md +++ b/docs/src/main/tut/const.md @@ -219,10 +219,6 @@ implicit def constApplicative[Z]: Applicative[Const[Z, ?]] = def pure[A](a: A): Const[Z, A] = ??? def ap[A, B](f: Const[Z, A => B])(fa: Const[Z, A]): Const[Z, B] = ??? - - def map[A, B](fa: Const[Z, A])(f: A => B): Const[Z, B] = ??? - - def product[A, B](fa: Const[Z, A],fb: Const[Z, B]): Const[Z, (A, B)] = ??? } ``` @@ -246,12 +242,6 @@ implicit def constApplicative[Z : Monoid]: Applicative[Const[Z, ?]] = def ap[A, B](f: Const[Z, A => B])(fa: Const[Z, A]): Const[Z, B] = Const(Monoid[Z].combine(fa.getConst, f.getConst)) - - def map[A, B](fa: Const[Z, A])(f: A => B): Const[Z, B] = - Const(fa.getConst) - - def product[A, B](fa: Const[Z, A],fb: Const[Z, B]): Const[Z, (A, B)] = - Const(Monoid[Z].combine(fa.getConst, fb.getConst)) } ``` diff --git a/docs/src/main/tut/validated.md b/docs/src/main/tut/validated.md index bb52438e38..724aeaaa2e 100644 --- a/docs/src/main/tut/validated.md +++ b/docs/src/main/tut/validated.md @@ -199,9 +199,6 @@ implicit def validatedApplicative[E : Semigroup]: Applicative[Validated[E, ?]] = } def pure[A](x: A): Validated[E, A] = Validated.valid(x) - def map[A, B](fa: Validated[E, A])(f: A => B): Validated[E, B] = fa.map(f) - def product[A, B](fa: Validated[E, A], fb: Validated[E, B]): Validated[E, (A, B)] = - ap(fa.map(a => (b: B) => (a, b)))(fb) } ``` diff --git a/laws/src/main/scala/cats/laws/TraverseLaws.scala b/laws/src/main/scala/cats/laws/TraverseLaws.scala index 9acbbfe11c..5ae13c2c21 100644 --- a/laws/src/main/scala/cats/laws/TraverseLaws.scala +++ b/laws/src/main/scala/cats/laws/TraverseLaws.scala @@ -44,11 +44,11 @@ trait TraverseLaws[F[_]] extends FunctorLaws[F] with FoldableLaws[F] { val (fm, fn) = f (M.ap(fm)(fam), N.ap(fn)(fan)) } - def map[X, Y](fx: MN[X])(f: X => Y): MN[Y] = { + override def map[X, Y](fx: MN[X])(f: X => Y): MN[Y] = { val (mx, nx) = fx (M.map(mx)(f), N.map(nx)(f)) } - def product[X, Y](fx: MN[X], fy: MN[Y]): MN[(X, Y)] = { + override def product[X, Y](fx: MN[X], fy: MN[Y]): MN[(X, Y)] = { val (mx, nx) = fx val (my, ny) = fy (M.product(mx, my), N.product(nx, ny)) From f7e508d7e92585008e08568853f4b063296a73bb Mon Sep 17 00:00:00 2001 From: Zizheng Tai Date: Mon, 4 Apr 2016 15:21:21 -0700 Subject: [PATCH 058/185] Renamed old `Kleisli#apply` to `Kleisli#ap`, and added `Kleisli#apply` which is an alias for `Kleisli#run`. --- core/src/main/scala/cats/data/Kleisli.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index f0e827d1e9..fcb6cfbc4b 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -9,7 +9,7 @@ import cats.functor.{Contravariant, Strong} */ final case class Kleisli[F[_], A, B](run: A => F[B]) { self => - def apply[C](f: Kleisli[F, A, B => C])(implicit F: Apply[F]): Kleisli[F, A, C] = + def ap[C](f: Kleisli[F, A, B => C])(implicit F: Apply[F]): Kleisli[F, A, C] = Kleisli(a => F.ap(f.run(a))(run(a))) def dimap[C, D](f: C => A)(g: B => D)(implicit F: Functor[F]): Kleisli[F, C, D] = @@ -59,6 +59,8 @@ final case class Kleisli[F[_], A, B](run: A => F[B]) { self => def second[C](implicit F: Functor[F]): Kleisli[F, (C, A), (C, B)] = Kleisli{ case (c, a) => F.map(run(a))(c -> _)} + + def apply(a: A): F[B] = run(a) } object Kleisli extends KleisliInstances with KleisliFunctions @@ -151,7 +153,7 @@ private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 Kleisli.pure[F, A, B](x) def ap[B, C](f: Kleisli[F, A, B => C])(fa: Kleisli[F, A, B]): Kleisli[F, A, C] = - fa(f) + fa.ap(f) def map[B, C](fb: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = fb.map(f) @@ -164,7 +166,7 @@ private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3 { implicit def kleisliApply[F[_]: Apply, A]: Apply[Kleisli[F, A, ?]] = new Apply[Kleisli[F, A, ?]] { def ap[B, C](f: Kleisli[F, A, B => C])(fa: Kleisli[F, A, B]): Kleisli[F, A, C] = - fa(f) + fa.ap(f) def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] = Kleisli(a => Apply[F].product(fb.run(a), fc.run(a))) From 8a18a7345dc2cf2776485da32441b10128488abf Mon Sep 17 00:00:00 2001 From: Olli Helenius Date: Tue, 5 Apr 2016 21:02:21 +0300 Subject: [PATCH 059/185] Add toValidatedNel to Xor As a convenient shorthand for xor.toValidated.toValidatedNel. --- core/src/main/scala/cats/data/Xor.scala | 4 ++++ tests/src/test/scala/cats/tests/XorTests.scala | 1 + 2 files changed, 5 insertions(+) diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index 7b6cc3bf27..470fa448d4 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -70,6 +70,10 @@ sealed abstract class Xor[+A, +B] extends Product with Serializable { def toValidated: Validated[A,B] = fold(Validated.Invalid.apply, Validated.Valid.apply) + /** Returns a [[ValidatedNel]] representation of this disjunction with the `Left` value + * as a single element on the `Invalid` side of the [[NonEmptyList]]. */ + def toValidatedNel[AA >: A]: ValidatedNel[AA,B] = fold(Validated.invalidNel, Validated.valid) + def withValidated[AA,BB](f: Validated[A,B] => Validated[AA,BB]): AA Xor BB = f(toValidated).toXor diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index adf7cf91e7..58d0a90de2 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -204,6 +204,7 @@ class XorTests extends CatsSuite { x.isLeft should === (x.toOption.isEmpty) x.isLeft should === (x.toList.isEmpty) x.isLeft should === (x.toValidated.isInvalid) + x.isLeft should === (x.toValidatedNel.isInvalid) } } From 420b72c71681fbc4734f2e1bba6422dd28d9da09 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Wed, 6 Apr 2016 10:12:32 -0400 Subject: [PATCH 060/185] Fix swapped f and g in invariant docs --- docs/src/main/tut/invariant.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/tut/invariant.md b/docs/src/main/tut/invariant.md index 1f01e58ce2..eb1d6299f7 100644 --- a/docs/src/main/tut/invariant.md +++ b/docs/src/main/tut/invariant.md @@ -15,7 +15,7 @@ def imap[A, B](fa: F[A])(f: A => B)(g: B => A): F[B] ``` Every covariant (as well as [contravariant](contravariant.html)) functor gives rise to an invariant -functor, by ignoring the `f` (or in case of contravariance, `g`) function. +functor, by ignoring the `g` (or in case of contravariance, `f`) function. Examples for instances of `Invariant` are `Semigroup` and `Monoid`, in the following we will explain why this is the case using `Semigroup`, the From 03a8664218d1497e9070582ff27d2aab4907696d Mon Sep 17 00:00:00 2001 From: Travis Brown Date: Sat, 9 Apr 2016 17:24:20 -0500 Subject: [PATCH 061/185] Add flatMapF for StateT --- core/src/main/scala/cats/data/StateT.scala | 9 +++++++++ tests/src/test/scala/cats/tests/StateTTests.scala | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index 439b86a6ff..f3a2f2b3f4 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -17,6 +17,15 @@ final class StateT[F[_], S, A](val runF: F[S => F[(S, A)]]) extends Serializable } }) + def flatMapF[B](faf: A => F[B])(implicit F: Monad[F]): StateT[F, S, B] = + StateT(s => + F.flatMap(runF) { fsf => + F.flatMap(fsf(s)) { case (s, a) => + F.map(faf(a))((s, _)) + } + } + ) + def map[B](f: A => B)(implicit F: Monad[F]): StateT[F, S, B] = transform { case (s, a) => (s, f(a)) } diff --git a/tests/src/test/scala/cats/tests/StateTTests.scala b/tests/src/test/scala/cats/tests/StateTTests.scala index 00d01c9550..9e0b8a447e 100644 --- a/tests/src/test/scala/cats/tests/StateTTests.scala +++ b/tests/src/test/scala/cats/tests/StateTTests.scala @@ -40,6 +40,12 @@ class StateTTests extends CatsSuite { } } + test("flatMap and flatMapF consistent") { + forAll { (stateT: StateT[Option, Long, Int], f: Int => Option[Int]) => + stateT.flatMap(a => StateT(s => f(a).map(b => (s, b)))) should === (stateT.flatMapF(f)) + } + } + test("runEmpty, runEmptyS, and runEmptyA consistent"){ forAll { (f: StateT[List, Long, Int]) => (f.runEmptyS zip f.runEmptyA) should === (f.runEmpty) From 60ab295afe933aad976e54459fc40354aaf9144e Mon Sep 17 00:00:00 2001 From: peterneyens Date: Mon, 11 Apr 2016 21:33:06 +0200 Subject: [PATCH 062/185] Add syntax for separate from MonadCombine. I based myself on the structure in syntax.bitraverse for the two implicit conversions (the existing for unite and the new for separate). MonadCombine separate was added in #864 by @adelbertc. --- .../main/scala/cats/syntax/monadCombine.scala | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/core/src/main/scala/cats/syntax/monadCombine.scala b/core/src/main/scala/cats/syntax/monadCombine.scala index 6d44699e33..d3ccfb03a1 100644 --- a/core/src/main/scala/cats/syntax/monadCombine.scala +++ b/core/src/main/scala/cats/syntax/monadCombine.scala @@ -1,13 +1,18 @@ package cats package syntax -trait MonadCombineSyntax { +trait MonadCombineSyntax extends MonadCombineSyntax1 { // TODO: use simulacrum instances eventually - implicit def nestedMonadCombineSyntax[F[_]: MonadCombine, G[_], A](fga: F[G[A]]): NestedMonadCombineOps[F, G, A] = - new NestedMonadCombineOps[F, G, A](fga) + implicit def monadCombineSyntax[F[_]: MonadCombine, G[_], A](fga: F[G[A]]): MonadCombineOps[F, G, A] = + new MonadCombineOps[F, G, A](fga) } -final class NestedMonadCombineOps[F[_], G[_], A](fga: F[G[A]])(implicit F: MonadCombine[F]) { +private[syntax] trait MonadCombineSyntax1 { + implicit def nestedMonadCombineSyntax[F[_]: MonadCombine, G[_, _], A, B](fgab: F[G[A, B]]): NestedMonadCombineOps[F, G, A, B] = + new NestedMonadCombineOps[F, G, A, B](fgab) +} + +final class MonadCombineOps[F[_], G[_], A](fga: F[G[A]])(implicit F: MonadCombine[F]) { /** * @see [[MonadCombine.unite]] @@ -15,7 +20,7 @@ final class NestedMonadCombineOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Monad * Example: * {{{ * scala> import cats.std.list._ - * scala> import cats.std.vector._ + * scala> import cats.std.vector._ * scala> import cats.syntax.monadCombine._ * scala> val x: List[Vector[Int]] = List(Vector(1, 2), Vector(3, 4)) * scala> x.unite @@ -24,3 +29,21 @@ final class NestedMonadCombineOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Monad */ def unite(implicit G: Foldable[G]): F[A] = F.unite(fga) } + +final class NestedMonadCombineOps[F[_], G[_, _], A, B](fgab: F[G[A, B]])(implicit F: MonadCombine[F]) { + + /** + * @see [[MonadCombine.separate]] + * + * Example: + * {{{ + * scala> import cats.data.Xor + * scala> import cats.std.list._ + * scala> import cats.syntax.monadCombine._ + * scala> val l: List[Xor[String, Int]] = List(Xor.right(1), Xor.left("error")) + * scala> l.separate + * res0: (List[String], List[Int]) = (List(error),List(1)) + * }}} + */ + def separate(implicit G: Bifoldable[G]): (F[A], F[B]) = F.separate(fgab) +} \ No newline at end of file From ca09eaf803690810cca291cb951194deaab329f8 Mon Sep 17 00:00:00 2001 From: Aldo Stracquadanio Date: Wed, 13 Apr 2016 21:52:08 +0100 Subject: [PATCH 063/185] Fix outdated import for `cats.syntax.apply._` --- docs/src/main/tut/apply.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/tut/apply.md b/docs/src/main/tut/apply.md index 173974f482..9a69e14a66 100644 --- a/docs/src/main/tut/apply.md +++ b/docs/src/main/tut/apply.md @@ -115,7 +115,7 @@ Apply[Option].tuple3(Some(1), Some(2), Some(3)) The `|@|` operator offers an alternative syntax for the higher-arity `Apply` functions (`apN`, `mapN` and `tupleN`). -In order to use it, first import `cats.syntax.all._` or `cats.syntax.apply._`. +In order to use it, first import `cats.syntax.all._` or `cats.syntax.cartesian._`. Here we see that the following two functions, `f1` and `f2`, are equivalent: ```tut From c21a0651253cc2ad733f985ffc613253c3f0241a Mon Sep 17 00:00:00 2001 From: peterneyens Date: Fri, 15 Apr 2016 13:55:51 +0200 Subject: [PATCH 064/185] Add syntax tests for MonadCombine and rename/move the syntax conversion method and class for `separate`. --- core/src/main/scala/cats/syntax/monadCombine.scala | 10 ++++------ tests/src/test/scala/cats/tests/SyntaxTests.scala | 8 ++++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/core/src/main/scala/cats/syntax/monadCombine.scala b/core/src/main/scala/cats/syntax/monadCombine.scala index d3ccfb03a1..94784f0386 100644 --- a/core/src/main/scala/cats/syntax/monadCombine.scala +++ b/core/src/main/scala/cats/syntax/monadCombine.scala @@ -1,15 +1,13 @@ package cats package syntax -trait MonadCombineSyntax extends MonadCombineSyntax1 { +trait MonadCombineSyntax { // TODO: use simulacrum instances eventually implicit def monadCombineSyntax[F[_]: MonadCombine, G[_], A](fga: F[G[A]]): MonadCombineOps[F, G, A] = new MonadCombineOps[F, G, A](fga) -} -private[syntax] trait MonadCombineSyntax1 { - implicit def nestedMonadCombineSyntax[F[_]: MonadCombine, G[_, _], A, B](fgab: F[G[A, B]]): NestedMonadCombineOps[F, G, A, B] = - new NestedMonadCombineOps[F, G, A, B](fgab) + implicit def separateSyntax[F[_]: MonadCombine, G[_, _], A, B](fgab: F[G[A, B]]): SeparateOps[F, G, A, B] = + new SeparateOps[F, G, A, B](fgab) } final class MonadCombineOps[F[_], G[_], A](fga: F[G[A]])(implicit F: MonadCombine[F]) { @@ -30,7 +28,7 @@ final class MonadCombineOps[F[_], G[_], A](fga: F[G[A]])(implicit F: MonadCombin def unite(implicit G: Foldable[G]): F[A] = F.unite(fga) } -final class NestedMonadCombineOps[F[_], G[_, _], A, B](fgab: F[G[A, B]])(implicit F: MonadCombine[F]) { +final class SeparateOps[F[_], G[_, _], A, B](fgab: F[G[A, B]])(implicit F: MonadCombine[F]) { /** * @see [[MonadCombine.separate]] diff --git a/tests/src/test/scala/cats/tests/SyntaxTests.scala b/tests/src/test/scala/cats/tests/SyntaxTests.scala index 3663d17917..9208c26b06 100644 --- a/tests/src/test/scala/cats/tests/SyntaxTests.scala +++ b/tests/src/test/scala/cats/tests/SyntaxTests.scala @@ -207,6 +207,14 @@ class SyntaxTests extends AllInstances with AllSyntax { val gfab = fgagb.bisequence } + def testMonadCombine[F[_]: MonadCombine, G[_]: Foldable, H[_, _]: Bifoldable, A, B]: Unit = { + val fga = mock[F[G[A]]] + val fa = fga.unite + + val fhab = mock[F[H[A, B]]] + val fafb = fhab.separate + } + def testApplicative[F[_]: Applicative, A]: Unit = { val a = mock[A] val fa = a.pure[F] From 5f4bdc66bed21139403da6b35dbf993b2da3fabe Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 15 Apr 2016 17:02:39 -0400 Subject: [PATCH 065/185] added changes for 0.5.0 to CHANGES.md --- CHANGES.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 0207fb4925..e206a0b093 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,78 @@ +## Version 0.5.0 + +> 2016 April 15 + +Version 0.5.0 is the fifth release. + +This release includes some API changes: + +* [#910](https://github.com/typelevel/cats/pull/910): Remove `Streaming` and `StreamingT` +* [#967](https://github.com/typelevel/cats/pull/967): `product` and `map` can be implemented in terms of `ap` +* [#970](https://github.com/typelevel/cats/pull/970): Renamed `Kleisli#apply`to `ap` + + +And additions: + +* [#853](https://github.com/typelevel/cats/pull/853): Adds a new `LiftTrans` typeclass +* [#864](https://github.com/typelevel/cats/pull/864): Add `Bifoldable` +* [#875](https://github.com/typelevel/cats/pull/875): Add `.get` method to `StateT` +* [#884](https://github.com/typelevel/cats/pull/884): Add `Applicative` syntax +* [#886](https://github.com/typelevel/cats/pull/886): Add `map` method to `OneAnd` +* [#927](https://github.com/typelevel/cats/pull/927): `XorT.ensure` method +* [#925](https://github.com/typelevel/cats/pull/925): Stack-safe `foldM` +* [#922](https://github.com/typelevel/cats/pull/922): Add `tell` and `writer` syntax for creating `Writers`. +* [#903](https://github.com/typelevel/cats/pull/903): Add `Bitraverse` +* [#928](https://github.com/typelevel/cats/pull/928): Add missing `Show` instances +* [#940](https://github.com/typelevel/cats/pull/940): More flexible `TransLift` +* [#946](https://github.com/typelevel/cats/pull/946): Added `OptionT.none` +* [#947](https://github.com/typelevel/cats/pull/947): Syntax for `ApplicativeError` +* [#971](https://github.com/typelevel/cats/pull/971): Add `toValidatedNel` to `Xor` +* [#973](https://github.com/typelevel/cats/pull/973): Add `flatMapF` for `StateT` + + +And bug fixes: + +* [#873](https://github.com/typelevel/cats/pull/873): Fix `OptionIdOps.some` to always return `Some` +* [#958](https://github.com/typelevel/cats/pull/958): Switch off scaladoc generation for Scala 2.10 due to macro problems +* [#955](https://github.com/typelevel/cats/pull/955): Rename `Id` instances to `idInstances` to make selective import easier + + +And removals: + +* [#910](https://github.com/typelevel/cats/pull/910): Remove `Streaming` and `StreamingT` + + +And some other improvements to the documentation, tutorials, laws and tests, including: + +* [#880](https://github.com/typelevel/cats/pull/880): Optimize `Eq[Vector[A]]` instance +* [#878](https://github.com/typelevel/cats/pull/878): Fix bug in freemonad doc +* [#870](https://github.com/typelevel/cats/pull/870): Fixed doc string for `StateT`'s `runEmptyA()` +* [#866](https://github.com/typelevel/cats/pull/866): Add some tests for `Coproduct` and `WriterT` +* [#883](https://github.com/typelevel/cats/pull/883): Delegate to `Traverse.sequence` in `Applicative.sequence` +* [#893](https://github.com/typelevel/cats/pull/893): Add `Reducible` laws +* [#923](https://github.com/typelevel/cats/pull/923): Make `Call.loop` `@tailrec` optimized +* [#916](https://github.com/typelevel/cats/pull/916): add `-P:scalajs:mapSourceURI` option +* [#909](https://github.com/typelevel/cats/pull/909): Make `Bifunctor` universal +* [#905](https://github.com/typelevel/cats/pull/905): make `Unapply` serializable +* [#902](https://github.com/typelevel/cats/pull/902): Make table in `Kleisli` readable +* [#897](https://github.com/typelevel/cats/pull/897): Add `Prod` tests +* [#938](https://github.com/typelevel/cats/pull/938): Onward to scala 2.11.8 +* [#941](https://github.com/typelevel/cats/pull/941): Type class composition and `MonadState` tests +* [#949](https://github.com/typelevel/cats/pull/949): Add .ensime_cache to gitignore +* [#954](https://github.com/typelevel/cats/pull/954): Switch to use nodeJsEnv as default jsEnv to build scala.js +* [#956](https://github.com/typelevel/cats/pull/956): Upgrade scala.js from 0.6.7 -> 0.6.8 +* [#960](https://github.com/typelevel/cats/pull/960): More `Reducible` tests +* [#962](https://github.com/typelevel/cats/pull/962): Improving test coverage +* [#964](https://github.com/typelevel/cats/pull/964): Clarify stabilty guarantees; drop 'proof of concept' and 'experimental' +* [#972](https://github.com/typelevel/cats/pull/972): Fix swapped f and g in `invariant` docs +* [#979](https://github.com/typelevel/cats/pull/979): Fix outdated import for `cats.syntax.apply._` + + +As always thanks to everyone who filed issues, participated in the Cats Gitter +channel, submitted code, or helped review pull requests. + + + ## Version 0.4.1 > 2016 February 4 From a508a0485800b36361194c67db99f4d13b9398cc Mon Sep 17 00:00:00 2001 From: Markus Hauck Date: Sun, 17 Apr 2016 20:15:53 +0200 Subject: [PATCH 066/185] Add object `reducible` for reducible syntax --- core/src/main/scala/cats/syntax/package.scala | 1 + core/src/main/scala/cats/syntax/reducible.scala | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/syntax/package.scala b/core/src/main/scala/cats/syntax/package.scala index 668c2f7311..031d7aef05 100644 --- a/core/src/main/scala/cats/syntax/package.scala +++ b/core/src/main/scala/cats/syntax/package.scala @@ -27,6 +27,7 @@ package object syntax { object order extends OrderSyntax object partialOrder extends PartialOrderSyntax object profunctor extends ProfunctorSyntax + object reducible extends ReducibleSyntax object semigroup extends SemigroupSyntax object semigroupk extends SemigroupKSyntax object show extends Show.ToShowOps diff --git a/core/src/main/scala/cats/syntax/reducible.scala b/core/src/main/scala/cats/syntax/reducible.scala index 99ccd99f9a..ed687ce800 100644 --- a/core/src/main/scala/cats/syntax/reducible.scala +++ b/core/src/main/scala/cats/syntax/reducible.scala @@ -2,7 +2,7 @@ package cats package syntax trait ReducibleSyntax1 { - implicit def foldableSyntaxU[FA](fa: FA)(implicit U: Unapply[Reducible,FA]): Reducible.Ops[U.M, U.A] = + implicit def reducibleSyntaxU[FA](fa: FA)(implicit U: Unapply[Reducible,FA]): Reducible.Ops[U.M, U.A] = new Reducible.Ops[U.M, U.A] { val self = U.subst(fa) val typeClassInstance = U.TC From e3eb1d0a2d02846409f7eec8548a7b9fc326f23b Mon Sep 17 00:00:00 2001 From: Markus Hauck Date: Sat, 16 Apr 2016 11:00:58 +0200 Subject: [PATCH 067/185] Add Validated equivalent of Xor.ensure --- core/src/main/scala/cats/data/Validated.scala | 14 ++++++++++++++ .../test/scala/cats/tests/ValidatedTests.scala | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/core/src/main/scala/cats/data/Validated.scala b/core/src/main/scala/cats/data/Validated.scala index 22f97f78a6..69002d74c0 100644 --- a/core/src/main/scala/cats/data/Validated.scala +++ b/core/src/main/scala/cats/data/Validated.scala @@ -203,6 +203,20 @@ sealed abstract class Validated[+E, +A] extends Product with Serializable { case Valid(a) => Invalid(a) case Invalid(e) => Valid(e) } + + /** + * Ensure that a successful result passes the given predicate, + * falling back to an Invalid of `onFailure` if the predicate + * returns false. + * + * For example: + * {{{ + * scala> Validated.valid("").ensure(new IllegalArgumentException("Must not be empty"))(_.nonEmpty) + * res0: Validated[IllegalArgumentException,String] = Invalid(java.lang.IllegalArgumentException: Must not be empty) + * }}} + */ + def ensure[EE >: E](onFailure: => EE)(f: A => Boolean): Validated[EE, A] = + fold(_ => this, a => if (f(a)) this else Validated.invalid(onFailure)) } object Validated extends ValidatedInstances with ValidatedFunctions{ diff --git a/tests/src/test/scala/cats/tests/ValidatedTests.scala b/tests/src/test/scala/cats/tests/ValidatedTests.scala index e755b1719d..947a8b899a 100644 --- a/tests/src/test/scala/cats/tests/ValidatedTests.scala +++ b/tests/src/test/scala/cats/tests/ValidatedTests.scala @@ -188,4 +188,20 @@ class ValidatedTests extends CatsSuite { val z = x.map2(y)((i, b) => if (b) i + 1 else i) z should === (NonEmptyList("error 1", "error 2").invalid[Int]) } + + test("ensure on Invalid is identity") { + forAll { (x: Validated[Int,String], i: Int, p: String => Boolean) => + if (x.isInvalid) { + x.ensure(i)(p) should === (x) + } + } + } + + test("ensure should fail if predicate not satisfied") { + forAll { (x: Validated[String, Int], s: String, p: Int => Boolean) => + if (x.exists(!p(_))) { + x.ensure(s)(p) should === (Validated.invalid(s)) + } + } + } } From 2e7b4ff4a9fcb7803d60c44f6b1c1e387b489887 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Mon, 18 Apr 2016 08:19:06 -0400 Subject: [PATCH 068/185] Use more/bigger scalacheck samples on JVM than JS --- laws/src/main/scala/cats/laws/discipline/Eq.scala | 5 ++++- tests/src/test/scala/cats/tests/CatsSuite.scala | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/laws/src/main/scala/cats/laws/discipline/Eq.scala b/laws/src/main/scala/cats/laws/discipline/Eq.scala index b422081670..03d0cd51b8 100644 --- a/laws/src/main/scala/cats/laws/discipline/Eq.scala +++ b/laws/src/main/scala/cats/laws/discipline/Eq.scala @@ -2,6 +2,7 @@ package cats package laws package discipline +import catalysts.Platform import algebra.Eq import org.scalacheck.Arbitrary @@ -12,8 +13,10 @@ object eq { * and comparing the application of the two functions. */ implicit def function1Eq[A, B](implicit A: Arbitrary[A], B: Eq[B]): Eq[A => B] = new Eq[A => B] { + val sampleCnt: Int = if (Platform.isJvm) 50 else 5 + def eqv(f: A => B, g: A => B): Boolean = { - val samples = List.fill(10)(A.arbitrary.sample).collect{ + val samples = List.fill(sampleCnt)(A.arbitrary.sample).collect{ case Some(a) => a case None => sys.error("Could not generate arbitrary values to compare two functions") } diff --git a/tests/src/test/scala/cats/tests/CatsSuite.scala b/tests/src/test/scala/cats/tests/CatsSuite.scala index 3fde4a3d25..7a1d0314a8 100644 --- a/tests/src/test/scala/cats/tests/CatsSuite.scala +++ b/tests/src/test/scala/cats/tests/CatsSuite.scala @@ -20,10 +20,10 @@ trait TestSettings extends Configuration with Matchers { lazy val checkConfiguration: PropertyCheckConfiguration = PropertyCheckConfiguration( - minSuccessful = PosInt(5), + minSuccessful = if (Platform.isJvm) PosInt(50) else PosInt(5), maxDiscardedFactor = if (Platform.isJvm) PosZDouble(5.0) else PosZDouble(50.0), minSize = PosZInt(0), - sizeRange = PosZInt(5), + sizeRange = if (Platform.isJvm) PosZInt(10) else PosZInt(5), workers = PosInt(1)) lazy val slowCheckConfiguration: PropertyCheckConfiguration = From d3f261a4a8f3fc2fe7ac6e500ad90935682df099 Mon Sep 17 00:00:00 2001 From: Luke Wyman Date: Tue, 19 Apr 2016 12:39:59 -0600 Subject: [PATCH 069/185] updated scaladoc comment for cats.Id changes --- core/src/main/scala/cats/Comonad.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/Comonad.scala b/core/src/main/scala/cats/Comonad.scala index 95ac7715e7..7e8e9c6f88 100644 --- a/core/src/main/scala/cats/Comonad.scala +++ b/core/src/main/scala/cats/Comonad.scala @@ -21,7 +21,7 @@ import simulacrum.typeclass * {{{ * scala> import cats.Id * scala> import cats.Comonad - * scala> val id = Id.pure(3) + * scala> val id: Id[Int] = 3 * scala> Comonad[Id].extract(id) * res0: cats.Id[Int] = 3 * }}} From 3604e7255d6ca75a347bedb635945f8823f1a03b Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Sat, 23 Apr 2016 09:32:30 -0400 Subject: [PATCH 070/185] Separate free package into its own module It's quite possible that people are going to throw tomatoes at me for proposing this change. There has been much discussion about modularization in the past, and in fact free used to have its own module, which was removed in #760/#765. One significant change that has happened since then is that `State` has started to use `Eval` instead of `Trampoline` for trampolining. At this point, nothing outside of the `free` package in `cats-core` depends on `free` (which is why this PR was so easy to create). To me that makes it a fairly convenient border for modularization. There was a [brief discussion](https://gitter.im/typelevel/cats?at=5716322b548df1be102e3f3c) about this on Gitter in which @sellout supported this change on the basis that they prefer to use fixpoint types (such as `Mu`) for free structures. Putting `free` into a separate module may be better for people who want to use different variations of these structures. Of course in addition to these more recent updates, there are the usual arguments for modularization and decreased jar size (which I believe is especially important for `catsJS` users). --- build.sbt | 24 +++++++++++++------ .../src/main/scala/cats/free/Coyoneda.scala | 0 .../src/main/scala/cats/free/Free.scala | 0 .../scala/cats/free/FreeApplicative.scala | 0 .../src/main/scala/cats/free/Inject.scala | 0 .../src/main/scala/cats/free/Trampoline.scala | 0 .../src/main/scala/cats/free/Yoneda.scala | 0 .../src/main/scala/cats/free/package.scala | 0 .../test/scala/cats/free}/CoyonedaTests.scala | 4 ++-- .../cats/free}/FreeApplicativeTests.scala | 4 ++-- .../src/test/scala/cats/free}/FreeTests.scala | 4 ++-- .../test/scala/cats/free}/InjectTests.scala | 4 ++-- .../test/scala/cats/free}/YonedaTests.scala | 4 ++-- 13 files changed, 27 insertions(+), 17 deletions(-) rename {core => free}/src/main/scala/cats/free/Coyoneda.scala (100%) rename {core => free}/src/main/scala/cats/free/Free.scala (100%) rename {core => free}/src/main/scala/cats/free/FreeApplicative.scala (100%) rename {core => free}/src/main/scala/cats/free/Inject.scala (100%) rename {core => free}/src/main/scala/cats/free/Trampoline.scala (100%) rename {core => free}/src/main/scala/cats/free/Yoneda.scala (100%) rename {core => free}/src/main/scala/cats/free/package.scala (100%) rename {tests/src/test/scala/cats/tests => free/src/test/scala/cats/free}/CoyonedaTests.scala (96%) rename {tests/src/test/scala/cats/tests => free/src/test/scala/cats/free}/FreeApplicativeTests.scala (98%) rename {tests/src/test/scala/cats/tests => free/src/test/scala/cats/free}/FreeTests.scala (98%) rename {tests/src/test/scala/cats/tests => free/src/test/scala/cats/free}/InjectTests.scala (98%) rename {tests/src/test/scala/cats/tests => free/src/test/scala/cats/free}/YonedaTests.scala (95%) diff --git a/build.sbt b/build.sbt index e0f4d915ab..713916373f 100644 --- a/build.sbt +++ b/build.sbt @@ -97,7 +97,7 @@ def noDocProjects(sv: String): Seq[ProjectReference] = CrossVersion.partialVersi lazy val docSettings = Seq( autoAPIMappings := true, unidocProjectFilter in (ScalaUnidoc, unidoc) := - inProjects(coreJVM) -- inProjects(noDocProjects(scalaVersion.value): _*), + inProjects(coreJVM, freeJVM) -- inProjects(noDocProjects(scalaVersion.value): _*), site.addMappingsToSiteDir(mappings in (ScalaUnidoc, packageDoc), "api"), site.addMappingsToSiteDir(tut, "_tut"), ghpagesNoJekyll := false, @@ -123,7 +123,7 @@ lazy val docs = project .settings(tutSettings) .settings(tutScalacOptions ~= (_.filterNot(Set("-Ywarn-unused-import", "-Ywarn-dead-code")))) .settings(commonJvmSettings) - .dependsOn(coreJVM) + .dependsOn(coreJVM, freeJVM) lazy val cats = project.in(file(".")) .settings(moduleName := "root") @@ -136,15 +136,15 @@ lazy val catsJVM = project.in(file(".catsJVM")) .settings(moduleName := "cats") .settings(catsSettings) .settings(commonJvmSettings) - .aggregate(macrosJVM, coreJVM, lawsJVM, testsJVM, jvm, docs, bench) - .dependsOn(macrosJVM, coreJVM, lawsJVM, testsJVM % "test-internal -> test", jvm, bench % "compile-internal;test-internal -> test") + .aggregate(macrosJVM, coreJVM, lawsJVM, freeJVM, testsJVM, jvm, docs, bench) + .dependsOn(macrosJVM, coreJVM, lawsJVM, freeJVM, testsJVM % "test-internal -> test", jvm, bench % "compile-internal;test-internal -> test") lazy val catsJS = project.in(file(".catsJS")) .settings(moduleName := "cats") .settings(catsSettings) .settings(commonJsSettings) - .aggregate(macrosJS, coreJS, lawsJS, testsJS, js) - .dependsOn(macrosJS, coreJS, lawsJS, testsJS % "test-internal -> test", js) + .aggregate(macrosJS, coreJS, lawsJS, freeJS, testsJS, js) + .dependsOn(macrosJS, coreJS, lawsJS, freeJS, testsJS % "test-internal -> test", js) .enablePlugins(ScalaJSPlugin) @@ -187,6 +187,16 @@ lazy val laws = crossProject.crossType(CrossType.Pure) lazy val lawsJVM = laws.jvm lazy val lawsJS = laws.js +lazy val free = crossProject.crossType(CrossType.Pure) + .dependsOn(macros, core, tests % "test-internal -> test") + .settings(moduleName := "cats-free") + .settings(catsSettings:_*) + .jsSettings(commonJsSettings:_*) + .jvmSettings(commonJvmSettings:_*) + +lazy val freeJVM = free.jvm +lazy val freeJS = free.js + lazy val tests = crossProject.crossType(CrossType.Pure) .dependsOn(macros, core, laws) .settings(moduleName := "cats-tests") @@ -210,7 +220,7 @@ lazy val jvm = project .settings(commonJvmSettings:_*) // bench is currently JVM-only -lazy val bench = project.dependsOn(macrosJVM, coreJVM, lawsJVM) +lazy val bench = project.dependsOn(macrosJVM, coreJVM, freeJVM, lawsJVM) .settings(moduleName := "cats-bench") .settings(catsSettings) .settings(noPublishSettings) diff --git a/core/src/main/scala/cats/free/Coyoneda.scala b/free/src/main/scala/cats/free/Coyoneda.scala similarity index 100% rename from core/src/main/scala/cats/free/Coyoneda.scala rename to free/src/main/scala/cats/free/Coyoneda.scala diff --git a/core/src/main/scala/cats/free/Free.scala b/free/src/main/scala/cats/free/Free.scala similarity index 100% rename from core/src/main/scala/cats/free/Free.scala rename to free/src/main/scala/cats/free/Free.scala diff --git a/core/src/main/scala/cats/free/FreeApplicative.scala b/free/src/main/scala/cats/free/FreeApplicative.scala similarity index 100% rename from core/src/main/scala/cats/free/FreeApplicative.scala rename to free/src/main/scala/cats/free/FreeApplicative.scala diff --git a/core/src/main/scala/cats/free/Inject.scala b/free/src/main/scala/cats/free/Inject.scala similarity index 100% rename from core/src/main/scala/cats/free/Inject.scala rename to free/src/main/scala/cats/free/Inject.scala diff --git a/core/src/main/scala/cats/free/Trampoline.scala b/free/src/main/scala/cats/free/Trampoline.scala similarity index 100% rename from core/src/main/scala/cats/free/Trampoline.scala rename to free/src/main/scala/cats/free/Trampoline.scala diff --git a/core/src/main/scala/cats/free/Yoneda.scala b/free/src/main/scala/cats/free/Yoneda.scala similarity index 100% rename from core/src/main/scala/cats/free/Yoneda.scala rename to free/src/main/scala/cats/free/Yoneda.scala diff --git a/core/src/main/scala/cats/free/package.scala b/free/src/main/scala/cats/free/package.scala similarity index 100% rename from core/src/main/scala/cats/free/package.scala rename to free/src/main/scala/cats/free/package.scala diff --git a/tests/src/test/scala/cats/tests/CoyonedaTests.scala b/free/src/test/scala/cats/free/CoyonedaTests.scala similarity index 96% rename from tests/src/test/scala/cats/tests/CoyonedaTests.scala rename to free/src/test/scala/cats/free/CoyonedaTests.scala index 1c9cbe3e90..26cbe38193 100644 --- a/tests/src/test/scala/cats/tests/CoyonedaTests.scala +++ b/free/src/test/scala/cats/free/CoyonedaTests.scala @@ -1,8 +1,8 @@ package cats -package tests +package free +import cats.tests.CatsSuite import cats.arrow.NaturalTransformation -import cats.free.Coyoneda import cats.laws.discipline.{FunctorTests, SerializableTests} import org.scalacheck.Arbitrary diff --git a/tests/src/test/scala/cats/tests/FreeApplicativeTests.scala b/free/src/test/scala/cats/free/FreeApplicativeTests.scala similarity index 98% rename from tests/src/test/scala/cats/tests/FreeApplicativeTests.scala rename to free/src/test/scala/cats/free/FreeApplicativeTests.scala index c8f6dd8861..1397cc178a 100644 --- a/tests/src/test/scala/cats/tests/FreeApplicativeTests.scala +++ b/free/src/test/scala/cats/free/FreeApplicativeTests.scala @@ -1,8 +1,8 @@ package cats -package tests +package free +import cats.tests.CatsSuite import cats.arrow.NaturalTransformation -import cats.free.FreeApplicative import cats.laws.discipline.{CartesianTests, ApplicativeTests, SerializableTests} import cats.laws.discipline.eq.{tuple3Eq, tuple2Eq} import cats.data.State diff --git a/tests/src/test/scala/cats/tests/FreeTests.scala b/free/src/test/scala/cats/free/FreeTests.scala similarity index 98% rename from tests/src/test/scala/cats/tests/FreeTests.scala rename to free/src/test/scala/cats/free/FreeTests.scala index 56fe308ac2..394f0fff98 100644 --- a/tests/src/test/scala/cats/tests/FreeTests.scala +++ b/free/src/test/scala/cats/free/FreeTests.scala @@ -1,8 +1,8 @@ package cats -package tests +package free +import cats.tests.CatsSuite import cats.arrow.NaturalTransformation -import cats.free.{Free, Trampoline} import cats.laws.discipline.{CartesianTests, MonadTests, SerializableTests} import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary.function0Arbitrary diff --git a/tests/src/test/scala/cats/tests/InjectTests.scala b/free/src/test/scala/cats/free/InjectTests.scala similarity index 98% rename from tests/src/test/scala/cats/tests/InjectTests.scala rename to free/src/test/scala/cats/free/InjectTests.scala index d174180d9c..eda62074e1 100644 --- a/tests/src/test/scala/cats/tests/InjectTests.scala +++ b/free/src/test/scala/cats/free/InjectTests.scala @@ -1,8 +1,8 @@ package cats -package tests +package free +import cats.tests.CatsSuite import cats.data.{Xor, Coproduct} -import cats.free.{Free, Inject,:<:} import org.scalacheck._ class InjectTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/YonedaTests.scala b/free/src/test/scala/cats/free/YonedaTests.scala similarity index 95% rename from tests/src/test/scala/cats/tests/YonedaTests.scala rename to free/src/test/scala/cats/free/YonedaTests.scala index cdddda3fe4..25886e2032 100644 --- a/tests/src/test/scala/cats/tests/YonedaTests.scala +++ b/free/src/test/scala/cats/free/YonedaTests.scala @@ -1,7 +1,7 @@ package cats -package tests +package free -import cats.free.Yoneda +import cats.tests.CatsSuite import cats.laws.discipline.{FunctorTests, SerializableTests} import org.scalacheck.Arbitrary From a6fe3284289b74929421a2a25aa9fd5290df78c6 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Sun, 24 Apr 2016 18:37:54 +0200 Subject: [PATCH 071/185] Move coverage away from bash --- .travis.yml | 3 +++ scripts/travis-publish.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b56b245cde..e6c9fd8867 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,9 @@ language: scala git: depth: 9999 +install: + - pip install --user codecov + scala: - 2.10.6 - 2.11.8 diff --git a/scripts/travis-publish.sh b/scripts/travis-publish.sh index 360f11eda4..ca96a2c817 100755 --- a/scripts/travis-publish.sh +++ b/scripts/travis-publish.sh @@ -27,7 +27,7 @@ fi sbt_cmd="sbt ++$TRAVIS_SCALA_VERSION" -coverage="$sbt_cmd coverage validateJVM coverageReport && bash <(curl -s https://codecov.io/bash)" +coverage="$sbt_cmd coverage validateJVM coverageReport && codecov" scala_js="$sbt_cmd macrosJS/compile coreJS/compile lawsJS/compile && $sbt_cmd testsJS/test && $sbt_cmd js/test" scala_jvm="$sbt_cmd validateJVM" From 63d186bc141cfb285f5238b773b807c235238ed3 Mon Sep 17 00:00:00 2001 From: aaron levin Date: Sun, 24 Apr 2016 19:00:48 +0200 Subject: [PATCH 072/185] Add SemigroupK instance for Xor + tests Xor's SemigroupK instance follows that of XorT. --- core/src/main/scala/cats/data/Xor.scala | 11 +++++++++++ tests/src/test/scala/cats/tests/XorTests.scala | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index 470fa448d4..c43a0d9cdf 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -168,6 +168,17 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { def combine(x: A Xor B, y: A Xor B): A Xor B = x combine y } + implicit def xorSemigroupK[L](implicit ev: Semigroup[L]): SemigroupK[Xor[L,?]] = + new SemigroupK[Xor[L,?]] { + def combineK[A](x: Xor[L,A], y: Xor[L,A]): Xor[L,A] = x match { + case Xor.Left(lx) => y match { + case Xor.Left(ly) => Xor.Left(ev.combine(lx,ly)) + case r @ Xor.Right(_) => r + } + case _ => x + } + } + implicit def xorBifunctor: Bitraverse[Xor] = new Bitraverse[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]] = diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index 58d0a90de2..39ca90a79d 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -3,6 +3,7 @@ package tests import cats.data.{NonEmptyList, Xor, XorT} import cats.data.Xor._ +import cats.laws.discipline.{SemigroupKTests} import cats.laws.discipline.arbitrary._ import cats.laws.discipline.{BitraverseTests, TraverseTests, MonadErrorTests, SerializableTests, CartesianTests} import cats.laws.discipline.eq.tuple3Eq @@ -46,6 +47,12 @@ class XorTests extends CatsSuite { checkAll("Eq[ListWrapper[String] Xor ListWrapper[Int]]", SerializableTests.serializable(Eq[ListWrapper[String] Xor ListWrapper[Int]])) } + { + implicit val L = ListWrapper.semigroup[String] + checkAll("Xor[ListWrapper[String], ?]", SemigroupKTests[Xor[ListWrapper[String], ?]].semigroupK[Int]) + checkAll("SemigroupK[Xor[ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[Xor[ListWrapper[String], ?]])) + } + implicit val arbitraryXor: Arbitrary[Xor[Int, String]] = Arbitrary { for { left <- arbitrary[Boolean] From fcfb5ca5fccd6d6829f43217b5547c54588d99b6 Mon Sep 17 00:00:00 2001 From: aaron levin Date: Sun, 24 Apr 2016 19:56:28 +0200 Subject: [PATCH 073/185] Add SemigroupK instance for Validated + tests --- core/src/main/scala/cats/data/Validated.scala | 12 +++++++++++- tests/src/test/scala/cats/tests/ValidatedTests.scala | 7 +++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/data/Validated.scala b/core/src/main/scala/cats/data/Validated.scala index 22f97f78a6..7232acca70 100644 --- a/core/src/main/scala/cats/data/Validated.scala +++ b/core/src/main/scala/cats/data/Validated.scala @@ -210,9 +210,19 @@ object Validated extends ValidatedInstances with ValidatedFunctions{ final case class Invalid[+E](e: E) extends Validated[E, Nothing] } - private[data] sealed abstract class ValidatedInstances extends ValidatedInstances1 { + implicit def validatedSemigroupK[A](implicit A: Semigroup[A]): SemigroupK[Validated[A,?]] = + new SemigroupK[Validated[A,?]] { + def combineK[B](x: Validated[A,B], y: Validated[A,B]): Validated[A,B] = x match { + case v @ Valid(_) => v + case Invalid(ix) => y match { + case Invalid(iy) => Invalid(A.combine(ix,iy)) + case v @ Valid(_) => v + } + } + } + implicit def validatedMonoid[A, B](implicit A: Semigroup[A], B: Monoid[B]): Monoid[Validated[A, B]] = new Monoid[Validated[A, B]] { def empty: Validated[A, B] = Valid(B.empty) def combine(x: Validated[A, B], y: Validated[A, B]): Validated[A, B] = x combine y diff --git a/tests/src/test/scala/cats/tests/ValidatedTests.scala b/tests/src/test/scala/cats/tests/ValidatedTests.scala index e755b1719d..16aa5e4b14 100644 --- a/tests/src/test/scala/cats/tests/ValidatedTests.scala +++ b/tests/src/test/scala/cats/tests/ValidatedTests.scala @@ -5,6 +5,7 @@ import cats.data.{NonEmptyList, Validated, ValidatedNel, Xor, XorT} import cats.data.Validated.{Valid, Invalid} import cats.laws.discipline.{BifunctorTests, TraverseTests, ApplicativeErrorTests, SerializableTests, CartesianTests} import org.scalacheck.Arbitrary._ +import cats.laws.discipline.{SemigroupKTests} import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq.tuple3Eq import algebra.laws.{OrderLaws, GroupLaws} @@ -33,6 +34,12 @@ class ValidatedTests extends CatsSuite { checkAll("Validated[String, NonEmptyList[Int]]", GroupLaws[Validated[String, NonEmptyList[Int]]].semigroup) + { + implicit val L = ListWrapper.semigroup[String] + checkAll("Validated[ListWrapper[String], ?]", SemigroupKTests[Validated[ListWrapper[String], ?]].semigroupK[Int]) + checkAll("SemigroupK[Validated[ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[Validated[ListWrapper[String], ?]])) + } + { implicit val S = ListWrapper.partialOrder[String] implicit val I = ListWrapper.partialOrder[Int] From 3e1317bfb08d771a104d3c591885ca28375939f8 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Mon, 25 Apr 2016 07:54:19 -0400 Subject: [PATCH 074/185] Combine install blocks in .travis.yml This is an experiment to test the theory stated in [this comment](https://github.com/typelevel/cats/pull/995#issuecomment-214280188). --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e6c9fd8867..272cd95825 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,6 @@ language: scala git: depth: 9999 -install: - - pip install --user codecov - scala: - 2.10.6 - 2.11.8 @@ -16,6 +13,7 @@ script: # http://austinpray.com/ops/2015/09/20/change-travis-node-version.html install: - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION + - pip install --user codecov notifications: webhooks: From 03aad13099ece0a56e7529ff4fded72fe07be7ce Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 10:01:21 -0400 Subject: [PATCH 075/185] Get algebra-core added to Cats. This commit sets up the basic SBT subproject configuration and moves the core algebra type classes into the cats.kernel package. --- build.sbt | 44 +++- kernel/src/main/scala/cats/kernel/Band.scala | 18 ++ .../cats/kernel/BoundedSemilattice.scala | 13 ++ .../scala/cats/kernel/CommutativeGroup.scala | 17 ++ .../scala/cats/kernel/CommutativeMonoid.scala | 17 ++ .../cats/kernel/CommutativeSemigroup.scala | 18 ++ kernel/src/main/scala/cats/kernel/Eq.scala | 138 ++++++++++++ kernel/src/main/scala/cats/kernel/Group.scala | 48 ++++ .../src/main/scala/cats/kernel/Monoid.scala | 52 +++++ kernel/src/main/scala/cats/kernel/Order.scala | 209 ++++++++++++++++++ .../main/scala/cats/kernel/PartialOrder.scala | 169 ++++++++++++++ .../src/main/scala/cats/kernel/Priority.scala | 65 ++++++ .../main/scala/cats/kernel/Semigroup.scala | 79 +++++++ .../main/scala/cats/kernel/Semilattice.scala | 62 ++++++ .../src/main/scala/cats/kernel/package.scala | 3 + 15 files changed, 948 insertions(+), 4 deletions(-) create mode 100644 kernel/src/main/scala/cats/kernel/Band.scala create mode 100644 kernel/src/main/scala/cats/kernel/BoundedSemilattice.scala create mode 100644 kernel/src/main/scala/cats/kernel/CommutativeGroup.scala create mode 100644 kernel/src/main/scala/cats/kernel/CommutativeMonoid.scala create mode 100644 kernel/src/main/scala/cats/kernel/CommutativeSemigroup.scala create mode 100644 kernel/src/main/scala/cats/kernel/Eq.scala create mode 100644 kernel/src/main/scala/cats/kernel/Group.scala create mode 100644 kernel/src/main/scala/cats/kernel/Monoid.scala create mode 100644 kernel/src/main/scala/cats/kernel/Order.scala create mode 100644 kernel/src/main/scala/cats/kernel/PartialOrder.scala create mode 100644 kernel/src/main/scala/cats/kernel/Priority.scala create mode 100644 kernel/src/main/scala/cats/kernel/Semigroup.scala create mode 100644 kernel/src/main/scala/cats/kernel/Semilattice.scala create mode 100644 kernel/src/main/scala/cats/kernel/package.scala diff --git a/build.sbt b/build.sbt index e0f4d915ab..1eccc559a3 100644 --- a/build.sbt +++ b/build.sbt @@ -24,6 +24,17 @@ lazy val catsDoctestSettings = Seq( doctestWithDependencies := false ) ++ doctestSettings +lazy val kernelSettings = Seq( + scalacOptions ++= commonScalacOptions, + resolvers ++= Seq( + "bintray/non" at "http://dl.bintray.com/non/maven", + Resolver.sonatypeRepo("releases"), + Resolver.sonatypeRepo("snapshots") + ), + parallelExecution in Test := false, + scalacOptions in (Compile, doc) := (scalacOptions in (Compile, doc)).value.filter(_ != "-Xfatal-warnings") +) ++ warnUnusedImport + lazy val commonSettings = Seq( scalacOptions ++= commonScalacOptions, resolvers ++= Seq( @@ -136,15 +147,15 @@ lazy val catsJVM = project.in(file(".catsJVM")) .settings(moduleName := "cats") .settings(catsSettings) .settings(commonJvmSettings) - .aggregate(macrosJVM, coreJVM, lawsJVM, testsJVM, jvm, docs, bench) - .dependsOn(macrosJVM, coreJVM, lawsJVM, testsJVM % "test-internal -> test", jvm, bench % "compile-internal;test-internal -> test") + .aggregate(macrosJVM, kernelJVM, kernelLawsJVM, coreJVM, lawsJVM, testsJVM, jvm, docs, bench) + .dependsOn(macrosJVM, kernelJVM, kernelLawsJVM, coreJVM, lawsJVM, testsJVM % "test-internal -> test", jvm, bench % "compile-internal;test-internal -> test") lazy val catsJS = project.in(file(".catsJS")) .settings(moduleName := "cats") .settings(catsSettings) .settings(commonJsSettings) - .aggregate(macrosJS, coreJS, lawsJS, testsJS, js) - .dependsOn(macrosJS, coreJS, lawsJS, testsJS % "test-internal -> test", js) + .aggregate(macrosJS, kernelJS, kernelLawsJS, coreJS, lawsJS, testsJS, js) + .dependsOn(macrosJS, kernelJS, kernelLawsJS, coreJS, lawsJS, testsJS % "test-internal -> test", js) .enablePlugins(ScalaJSPlugin) @@ -158,6 +169,31 @@ lazy val macros = crossProject.crossType(CrossType.Pure) lazy val macrosJVM = macros.jvm lazy val macrosJS = macros.js +lazy val kernel = crossProject.crossType(CrossType.Pure) + .in(file("kernel")) + .settings(moduleName := "cats-kernel") + .settings(kernelSettings: _*) + .settings(buildSettings: _*) + .settings(publishSettings: _*) + .settings(scoverageSettings: _*) + .jsSettings(commonJsSettings:_*) + .jvmSettings(commonJvmSettings:_*) + +lazy val kernelJVM = kernel.jvm +lazy val kernelJS = kernel.js + +lazy val kernelLaws = crossProject.crossType(CrossType.Pure) + .in(file("kernel-laws")) + .settings(moduleName := "cats-kernel-laws") + .settings(kernelSettings: _*) + .settings(buildSettings: _*) + .settings(publishSettings: _*) + .settings(scoverageSettings: _*) + .jsSettings(commonJsSettings:_*) + .jvmSettings(commonJvmSettings:_*) + +lazy val kernelLawsJVM = kernelLaws.jvm +lazy val kernelLawsJS = kernelLaws.js lazy val core = crossProject.crossType(CrossType.Pure) .dependsOn(macros) diff --git a/kernel/src/main/scala/cats/kernel/Band.scala b/kernel/src/main/scala/cats/kernel/Band.scala new file mode 100644 index 0000000000..5404005633 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/Band.scala @@ -0,0 +1,18 @@ +package cats.kernel + +import scala.{specialized => sp} + + +/** + * Bands are semigroups whose operation + * (i.e. combine) is also idempotent. + */ +trait Band[@sp(Int, Long, Float, Double) A] extends Any with Semigroup[A] + +object Band { + + /** + * Access an implicit `Band[A]`. + */ + @inline final def apply[@sp(Int, Long, Float, Double) A](implicit ev: Band[A]): Band[A] = ev +} diff --git a/kernel/src/main/scala/cats/kernel/BoundedSemilattice.scala b/kernel/src/main/scala/cats/kernel/BoundedSemilattice.scala new file mode 100644 index 0000000000..b9497ec63e --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/BoundedSemilattice.scala @@ -0,0 +1,13 @@ +package cats.kernel + +import scala.{specialized => sp} + +trait BoundedSemilattice[@sp(Int, Long, Float, Double) A] extends Any with Semilattice[A] with CommutativeMonoid[A] + +object BoundedSemilattice { + + /** + * Access an implicit `BoundedSemilattice[A]`. + */ + @inline final def apply[@sp(Int, Long, Float, Double) A](implicit ev: BoundedSemilattice[A]): BoundedSemilattice[A] = ev +} diff --git a/kernel/src/main/scala/cats/kernel/CommutativeGroup.scala b/kernel/src/main/scala/cats/kernel/CommutativeGroup.scala new file mode 100644 index 0000000000..6072cecd96 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/CommutativeGroup.scala @@ -0,0 +1,17 @@ +package cats.kernel + +import scala.{ specialized => sp } + +/** + * An abelian group is a group whose operation is commutative. + */ +trait CommutativeGroup[@sp(Int, Long, Float, Double) A] extends Any with Group[A] with CommutativeMonoid[A] + +object CommutativeGroup extends GroupFunctions[CommutativeGroup] { + + /** + * Access an implicit `CommutativeGroup[A]`. + */ + @inline final def apply[A](implicit ev: CommutativeGroup[A]): CommutativeGroup[A] = ev +} + diff --git a/kernel/src/main/scala/cats/kernel/CommutativeMonoid.scala b/kernel/src/main/scala/cats/kernel/CommutativeMonoid.scala new file mode 100644 index 0000000000..c8bb7a3657 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/CommutativeMonoid.scala @@ -0,0 +1,17 @@ +package cats.kernel + +import scala.{ specialized => sp } + +/** + * CommutativeMonoid represents a commutative monoid. + * + * A monoid is commutative if for all x and y, x |+| y === y |+| x. + */ +trait CommutativeMonoid[@sp(Int, Long, Float, Double) A] extends Any with Monoid[A] with CommutativeSemigroup[A] + +object CommutativeMonoid extends MonoidFunctions[CommutativeMonoid] { + /** + * Access an implicit `CommutativeMonoid[A]`. + */ + @inline final def apply[A](implicit ev: CommutativeMonoid[A]): CommutativeMonoid[A] = ev +} diff --git a/kernel/src/main/scala/cats/kernel/CommutativeSemigroup.scala b/kernel/src/main/scala/cats/kernel/CommutativeSemigroup.scala new file mode 100644 index 0000000000..2874a6b5d8 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/CommutativeSemigroup.scala @@ -0,0 +1,18 @@ +package cats.kernel + +import scala.{ specialized => sp } + +/** + * CommutativeSemigroup represents a commutative semigroup. + * + * A semigroup is commutative if for all x and y, x |+| y === y |+| x. + */ +trait CommutativeSemigroup[@sp(Int, Long, Float, Double) A] extends Any with Semigroup[A] + +object CommutativeSemigroup extends SemigroupFunctions[CommutativeSemigroup] { + + /** + * Access an implicit `CommutativeSemigroup[A]`. + */ + @inline final def apply[A](implicit ev: CommutativeSemigroup[A]): CommutativeSemigroup[A] = ev +} diff --git a/kernel/src/main/scala/cats/kernel/Eq.scala b/kernel/src/main/scala/cats/kernel/Eq.scala new file mode 100644 index 0000000000..c76f51ad28 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/Eq.scala @@ -0,0 +1,138 @@ +package cats.kernel + +import scala.{specialized => sp} + +import scala.math.Equiv + +/** + * A type class used to determine equality between 2 instances of the same + * type. Any 2 instances `x` and `y` are equal if `eqv(x, y)` is `true`. + * Moreover, `eqv` should form an equivalence relation. + */ +trait Eq[@sp A] extends Any with Serializable { self => + + /** + * Returns `true` if `x` and `y` are equivalent, `false` otherwise. + */ + def eqv(x: A, y: A): Boolean + + /** + * Returns `false` if `x` and `y` are equivalent, `true` otherwise. + */ + def neqv(x: A, y: A): Boolean = !eqv(x, y) + + /** + * Constructs a new `Eq` instance for type `B` where 2 elements are + * equivalent iff `eqv(f(x), f(y))`. + */ + def on[@sp B](f: B => A): Eq[B] = + new Eq[B] { + def eqv(x: B, y: B): Boolean = self.eqv(f(x), f(y)) + } + + /** + * Return an Eq that gives the result of the and of this and that + * note this is idempotent + */ + def and(that: Eq[A]): Eq[A] = + new Eq[A] { + def eqv(x: A, y: A) = self.eqv(x, y) && that.eqv(x, y) + } + /** + * Return an Eq that gives the result of the or of this and that + * Note this is idempotent + */ + def or(that: Eq[A]): Eq[A] = + new Eq[A] { + def eqv(x: A, y: A) = self.eqv(x, y) || that.eqv(x, y) + } +} + +trait EqFunctions { + def eqv[@sp A](x: A, y: A)(implicit ev: Eq[A]): Boolean = + ev.eqv(x, y) + + def neqv[@sp A](x: A, y: A)(implicit ev: Eq[A]): Boolean = + ev.neqv(x, y) +} + +object Eq extends EqFunctions { + + /** + * Access an implicit `Eq[A]`. + */ + @inline final def apply[A](implicit ev: Eq[A]): Eq[A] = ev + + /** + * Convert an implicit `Eq[B]` to an `Eq[A]` using the given + * function `f`. + */ + def by[@sp A, @sp B](f: A => B)(implicit ev: Eq[B]): Eq[A] = + ev.on(f) + + /** + * This gives compatibility with scala's Equiv trait + */ + implicit def equiv[A](implicit ev: Eq[A]): Equiv[A] = + new Equiv[A] { + def equiv(a: A, b: A) = ev.eqv(a, b) + } + + /** + * Create an `Eq` instance from an `eqv` implementation. + */ + def instance[A](f: (A, A) => Boolean): Eq[A] = + new Eq[A] { + def eqv(x: A, y: A) = f(x, y) + } + + /** + * An `Eq[A]` that delegates to universal equality (`==`). + * + * This can be useful for case classes, which have reasonable `equals` + * implementations + */ + def fromUniversalEquals[A]: Eq[A] = + new Eq[A] { + def eqv(x: A, y: A) = x == y + } + + /** + * Everything is the same + */ + def allEqual[A]: Eq[A] = new Eq[A] { + def eqv(x: A, y: A) = true + } + + /** + * This is a monoid that creates an Eq that + * checks that all equality checks pass + */ + def allEqualBoundedSemilattice[A]: BoundedSemilattice[Eq[A]] = new BoundedSemilattice[Eq[A]] { + def empty = allEqual[A] + def combine(e1: Eq[A], e2: Eq[A]): Eq[A] = e1.and(e2) + override def combineAllOption(es: TraversableOnce[Eq[A]]): Option[Eq[A]] = + if (es.isEmpty) None + else { + val materialized = es.toVector + Some(new Eq[A] { + def eqv(x: A, y: A) = materialized.forall(_.eqv(x, y)) + }) + } + } + /** + * This is a monoid that creates an Eq that + * checks that at least one equality check passes + */ + def anyEqualSemilattice[A]: Semilattice[Eq[A]] = new Semilattice[Eq[A]] { + def combine(e1: Eq[A], e2: Eq[A]): Eq[A] = e1.or(e2) + override def combineAllOption(es: TraversableOnce[Eq[A]]): Option[Eq[A]] = + if (es.isEmpty) None + else { + val materialized = es.toVector + Some(new Eq[A] { + def eqv(x: A, y: A) = materialized.exists(_.eqv(x, y)) + }) + } + } +} diff --git a/kernel/src/main/scala/cats/kernel/Group.scala b/kernel/src/main/scala/cats/kernel/Group.scala new file mode 100644 index 0000000000..2a3412dbbd --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/Group.scala @@ -0,0 +1,48 @@ +package cats.kernel + +import scala.{ specialized => sp } + +/** + * A group is a monoid where each element has an inverse. + */ +trait Group[@sp(Int, Long, Float, Double) A] extends Any with Monoid[A] { + + /** + * Find the inverse of `a`. + * + * `combine(a, inverse(a))` = `combine(inverse(a), a)` = `empty`. + */ + def inverse(a: A): A + + /** + * Remove the element `b` from `a`. + * + * Equivalent to `combine(a, inverse(a))` + */ + def remove(a: A, b: A): A = combine(a, inverse(b)) + + /** + * Return `a` appended to itself `n` times. If `n` is negative, then + * this returns `inverse(a)` appended to itself `n` times. + */ + override def combineN(a: A, n: Int): A = + if (n > 0) repeatedCombineN(a, n) + else if (n == 0) empty + else if (n == Int.MinValue) combineN(inverse(combine(a, a)), 1073741824) + else repeatedCombineN(inverse(a), -n) +} + +trait GroupFunctions[G[T] <: Group[T]] extends MonoidFunctions[Group] { + def inverse[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: G[A]): A = + ev.inverse(a) + def remove[@sp(Int, Long, Float, Double) A](x: A, y: A)(implicit ev: G[A]): A = + ev.remove(x, y) +} + +object Group extends GroupFunctions[Group] { + + /** + * Access an implicit `Group[A]`. + */ + @inline final def apply[A](implicit ev: Group[A]): Group[A] = ev +} diff --git a/kernel/src/main/scala/cats/kernel/Monoid.scala b/kernel/src/main/scala/cats/kernel/Monoid.scala new file mode 100644 index 0000000000..aa2cbaf317 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/Monoid.scala @@ -0,0 +1,52 @@ +package cats.kernel + +import scala.{ specialized => sp } + +/** + * A monoid is a semigroup with an identity. A monoid is a specialization of a + * semigroup, so its operation must be associative. Additionally, + * `combine(x, empty) == combine(empty, x) == x`. For example, if we have `Monoid[String]`, + * with `combine` as string concatenation, then `empty = ""`. + */ +trait Monoid[@sp(Int, Long, Float, Double) A] extends Any with Semigroup[A] { + + /** + * Return the identity element for this monoid. + */ + def empty: A + + /** + * Tests if `a` is the identity. + */ + def isEmpty(a: A)(implicit ev: Eq[A]) = ev.eqv(a, empty) + + /** + * Return `a` appended to itself `n` times. + */ + override def combineN(a: A, n: Int): A = + if (n < 0) throw new IllegalArgumentException("Repeated combining for monoids must have n >= 0") + else if (n == 0) empty + else repeatedCombineN(a, n) + + /** + * Given a sequence of `as`, sum them using the monoid and return the total. + */ + def combineAll(as: TraversableOnce[A]): A = + as.foldLeft(empty)(combine) +} + +trait MonoidFunctions[M[T] <: Monoid[T]] extends SemigroupFunctions[M] { + def empty[@sp(Int, Long, Float, Double) A](implicit ev: M[A]): A = + ev.empty + + def combineAll[@sp(Int, Long, Float, Double) A](as: TraversableOnce[A])(implicit ev: M[A]): A = + ev.combineAll(as) +} + +object Monoid extends MonoidFunctions[Monoid] { + + /** + * Access an implicit `Monoid[A]`. + */ + @inline final def apply[A](implicit ev: Monoid[A]): Monoid[A] = ev +} diff --git a/kernel/src/main/scala/cats/kernel/Order.scala b/kernel/src/main/scala/cats/kernel/Order.scala new file mode 100644 index 0000000000..7381684b5c --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/Order.scala @@ -0,0 +1,209 @@ +package cats.kernel + +import scala.{specialized => sp} + +/** + * The `Order` type class is used to define a total ordering on some type `A`. + * An order is defined by a relation <=, which obeys the following laws: + * + * - either x <= y or y <= x (totality) + * - if x <= y and y <= x, then x == y (antisymmetry) + * - if x <= y and y <= z, then x <= z (transitivity) + * + * The truth table for compare is defined as follows: + * + * x <= y x >= y Int + * true true = 0 (corresponds to x == y) + * true false < 0 (corresponds to x < y) + * false true > 0 (corresponds to x > y) + * + * By the totality law, x <= y and y <= x cannot be both false. + */ +trait Order[@sp A] extends Any with PartialOrder[A] { self => + + /** + * Result of comparing `x` with `y`. Returns an Int whose sign is: + * - negative iff `x < y` + * - zero iff `x = y` + * - positive iff `x > y` + */ + def compare(x: A, y: A): Int + + def partialCompare(x: A, y: A): Double = compare(x, y).toDouble + + /** + * If x <= y, return x, else return y. + */ + def min(x: A, y: A): A = if (lt(x, y)) x else y + + /** + * If x >= y, return x, else return y. + */ + def max(x: A, y: A): A = if (gt(x, y)) x else y + + /** + * Defines an order on `B` by mapping `B` to `A` using `f` and using `A`s + * order to order `B`. + */ + override def on[@sp B](f: B => A): Order[B] = + new Order[B] { + def compare(x: B, y: B): Int = self.compare(f(x), f(y)) + } + + /** + * Defines an ordering on `A` where all arrows switch direction. + */ + override def reverse: Order[A] = + new Order[A] { + def compare(x: A, y: A): Int = self.compare(y, x) + + override def reverse: Order[A] = self + } + + // The following may be overridden for performance: + + /** + * Returns true if `x` = `y`, false otherwise. + */ + override def eqv(x: A, y: A): Boolean = + compare(x, y) == 0 + + /** + * Returns true if `x` != `y`, false otherwise. + */ + override def neqv(x: A, y: A): Boolean = + compare(x, y) != 0 + + /** + * Returns true if `x` <= `y`, false otherwise. + */ + override def lteqv(x: A, y: A): Boolean = + compare(x, y) <= 0 + + /** + * Returns true if `x` < `y`, false otherwise. + */ + override def lt(x: A, y: A): Boolean = + compare(x, y) < 0 + + /** + * Returns true if `x` >= `y`, false otherwise. + */ + override def gteqv(x: A, y: A): Boolean = + compare(x, y) >= 0 + + /** + * Returns true if `x` > `y`, false otherwise. + */ + override def gt(x: A, y: A): Boolean = + compare(x, y) > 0 + + /** + * Returns a new `Order[A]` instance that first compares by the original + * `Order` instance and uses the provided `Order` instance to "break ties". + * + * That is, `x.whenEqual(y)` creates an `Order` that first orders by `x` and + * then (if two elements are equal) falls back to `y` for the comparison. + */ + def whenEqual(o: Order[A]): Order[A] = new Order[A] { + def compare(x: A, y: A) = { + val c = self.compare(x, y) + if (c == 0) o.compare(x, y) + else c + } + } + + /** + * Convert a `Order[A]` to a `scala.math.Ordering[A]` + * instance. + */ + def toOrdering: Ordering[A] = new Ordering[A] { + def compare(x: A, y: A): Int = self.compare(x, y) + } +} + +trait OrderFunctions { + def compare[@sp A](x: A, y: A)(implicit ev: Order[A]): Int = + ev.compare(x, y) + + def eqv[@sp A](x: A, y: A)(implicit ev: Order[A]): Boolean = + ev.eqv(x, y) + def neqv[@sp A](x: A, y: A)(implicit ev: Order[A]): Boolean = + ev.neqv(x, y) + def gt[@sp A](x: A, y: A)(implicit ev: Order[A]): Boolean = + ev.gt(x, y) + def gteqv[@sp A](x: A, y: A)(implicit ev: Order[A]): Boolean = + ev.gteqv(x, y) + def lt[@sp A](x: A, y: A)(implicit ev: Order[A]): Boolean = + ev.lt(x, y) + def lteqv[@sp A](x: A, y: A)(implicit ev: Order[A]): Boolean = + ev.lteqv(x, y) + + def min[@sp A](x: A, y: A)(implicit ev: Order[A]): A = + ev.min(x, y) + def max[@sp A](x: A, y: A)(implicit ev: Order[A]): A = + ev.max(x, y) +} + +object Order extends OrderFunctions { + + /** + * Access an implicit `Eq[A]`. + */ + @inline final def apply[A](implicit ev: Order[A]) = ev + + /** + * Convert an implicit `Order[A]` to an `Order[B]` using the given + * function `f`. + */ + def by[@sp A, @sp B](f: A => B)(implicit ev: Order[B]): Order[A] = + ev.on(f) + + /** + * Define an `Order[A]` using the given function `f`. + */ + def from[@sp A](f: (A, A) => Int): Order[A] = + new Order[A] { + def compare(x: A, y: A) = f(x, y) + } + + /** + * Implicitly convert a `Order[A]` to a `scala.math.Ordering[A]` + * instance. + */ + implicit def ordering[A](implicit ev: Order[A]): Ordering[A] = + ev.toOrdering + + /** + * An `Order` instance that considers all `A` instances to be equal. + */ + def allEqual[A]: Order[A] = new Order[A] { + def compare(x: A, y: A): Int = 0 + } + + + /** + * A `Monoid[Order[A]]` can be generated for all `A` with the following + * properties: + * + * `empty` returns a trivial `Order[A]` which considers all `A` instances to + * be equal. + * + * `combine(x: Order[A], y: Order[A])` creates an `Order[A]` that first + * orders by `x` and then (if two elements are equal) falls back to `y`. + * + * @see [[Order.whenEqual]] + */ + def whenEqualMonoid[A]: Monoid[Order[A]] = + new Monoid[Order[A]] { + val empty: Order[A] = allEqual[A] + + def combine(x: Order[A], y: Order[A]): Order[A] = x whenEqual y + } + + def fromOrdering[A](implicit ev: Ordering[A]): Order[A] = new Order[A] { + def compare(x: A, y: A): Int = ev.compare(x, y) + + override def toOrdering: Ordering[A] = ev + } +} diff --git a/kernel/src/main/scala/cats/kernel/PartialOrder.scala b/kernel/src/main/scala/cats/kernel/PartialOrder.scala new file mode 100644 index 0000000000..e3adbe0f9a --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/PartialOrder.scala @@ -0,0 +1,169 @@ +package cats.kernel + +import scala.{specialized => sp} + +/** + * The `PartialOrder` type class is used to define a partial ordering on some type `A`. + * + * A partial order is defined by a relation <=, which obeys the following laws: + * + * - x <= x (reflexivity) + * - if x <= y and y <= x, then x = y (anti-symmetry) + * - if x <= y and y <= z, then x <= z (transitivity) + * + * To compute both <= and >= at the same time, we use a Double number + * to encode the result of the comparisons x <= y and x >= y. + * The truth table is defined as follows: + * + * x <= y x >= y Double + * true true = 0.0 (corresponds to x = y) + * false false = NaN (x and y cannot be compared) + * true false = -1.0 (corresponds to x < y) + * false true = 1.0 (corresponds to x > y) + */ +trait PartialOrder[@sp A] extends Any with Eq[A] { self => + + /** + * Result of comparing `x` with `y`. Returns NaN if operands are not + * comparable. If operands are comparable, returns a Double whose + * sign is: + * - negative iff `x < y` + * - zero iff `x = y` + * - positive iff `x > y` + */ + def partialCompare(x: A, y: A): Double + + /** + * Result of comparing `x` with `y`. Returns None if operands are + * not comparable. If operands are comparable, returns Some[Int] + * where the Int sign is: + * - negative iff `x < y` + * - zero iff `x = y` + * - positive iff `x > y` + */ + def tryCompare(x: A, y: A): Option[Int] = { + val c = partialCompare(x, y) + if (c.isNaN) None else Some(c.signum) + } + + /** + * Returns Some(x) if x <= y, Some(y) if x > y, otherwise None. + */ + def pmin(x: A, y: A): Option[A] = { + val c = partialCompare(x, y) + if (c <= 0) Some(x) + else if (c > 0) Some(y) + else None + } + + /** + * Returns Some(x) if x >= y, Some(y) if x < y, otherwise None. + */ + def pmax(x: A, y: A): Option[A] = { + val c = partialCompare(x, y) + if (c >= 0) Some(x) + else if (c < 0) Some(y) + else None + } + + /** + * Defines a partial order on `B` by mapping `B` to `A` using `f` + * and using `A`s order to order `B`. + */ + override def on[@sp B](f: B => A): PartialOrder[B] = + new PartialOrder[B] { + def partialCompare(x: B, y: B): Double = self.partialCompare(f(x), f(y)) + } + + /** + * Defines a partial order on `A` where all arrows switch direction. + */ + def reverse: PartialOrder[A] = + new PartialOrder[A] { + def partialCompare(x: A, y: A): Double = self.partialCompare(y, x) + } + + // The following may be overridden for performance: + + /** + * Returns true if `x` = `y`, false otherwise. + */ + def eqv(x: A, y: A): Boolean = partialCompare(x, y) == 0 + + /** + * Returns true if `x` <= `y`, false otherwise. + */ + def lteqv(x: A, y: A): Boolean = partialCompare(x, y) <= 0 + + /** + * Returns true if `x` < `y`, false otherwise. + */ + def lt(x: A, y: A): Boolean = partialCompare(x, y) < 0 + + /** + * Returns true if `x` >= `y`, false otherwise. + */ + def gteqv(x: A, y: A): Boolean = partialCompare(x, y) >= 0 + + /** + * Returns true if `x` > `y`, false otherwise. + */ + def gt(x: A, y: A): Boolean = partialCompare(x, y) > 0 +} + +trait PartialOrderFunctions { + + def partialCompare[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Double = + ev.partialCompare(x, y) + def tryCompare[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Option[Int] = + ev.tryCompare(x, y) + + def pmin[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Option[A] = + ev.pmin(x, y) + def pmax[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Option[A] = + ev.pmax(x, y) + + def eqv[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Boolean = + ev.eqv(x, y) + def lteqv[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Boolean = + ev.lteqv(x, y) + def lt[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Boolean = + ev.lt(x, y) + def gteqv[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Boolean = + ev.gteqv(x, y) + def gt[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Boolean = + ev.gt(x, y) +} + +object PartialOrder extends PartialOrderFunctions { + + /** + * Access an implicit `Eq[A]`. + */ + @inline final def apply[A](implicit ev: PartialOrder[A]) = ev + + /** + * Convert an implicit `PartialOrder[A]` to an `PartialOrder[B]` + * using the given function `f`. + */ + def by[@sp A, @sp B](f: A => B)(implicit ev: PartialOrder[B]): PartialOrder[A] = + ev.on(f) + + /** + * Define a `PartialOrder[A]` using the given function `f`. + */ + def from[@sp A](f: (A, A) => Double): PartialOrder[A] = + new PartialOrder[A] { + def partialCompare(x: A, y: A) = f(x, y) + } + + /** + * Implicitly convert a `PartialOrder[A]` to a + * `scala.math.PartialOrdering[A]` instance. + */ + implicit def partialOrdering[A](implicit ev: PartialOrder[A]): PartialOrdering[A] = + new PartialOrdering[A] { + def tryCompare(x: A, y: A): Option[Int] = ev.tryCompare(x, y) + def lteq(x: A, y: A): Boolean = ev.lteqv(x, y) + } +} diff --git a/kernel/src/main/scala/cats/kernel/Priority.scala b/kernel/src/main/scala/cats/kernel/Priority.scala new file mode 100644 index 0000000000..a9021c5c83 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/Priority.scala @@ -0,0 +1,65 @@ +package cats.kernel + +/** + * Priority is a type class for prioritized implicit search. + * + * This type class will attempt to provide an implicit instance of `P` + * (the preferred type). If that type is not available it will + * fallback to `F` (the fallback type). If neither type is available + * then a `Priority[P, F]` instance will not be available. + * + * This type can be useful for problems where multiple algorithms can + * be used, depending on the type classes available. + */ +sealed trait Priority[+P, +F] { + + import Priority.{Preferred, Fallback} + + def fold[B](f1: P => B)(f2: F => B): B = + this match { + case Preferred(x) => f1(x) + case Fallback(y) => f2(y) + } + + def join[U >: P with F]: U = + fold(_.asInstanceOf[U])(_.asInstanceOf[U]) + + def bimap[P2, F2](f1: P => P2)(f2: F => F2): Priority[P2, F2] = + this match { + case Preferred(x) => Preferred(f1(x)) + case Fallback(y) => Fallback(f2(y)) + } + + def toEither: Either[P, F] = + fold[Either[P, F]](p => Left(p))(f => Right(f)) + + def isPreferred: Boolean = + fold(_ => true)(_ => false) + + def isFallback: Boolean = + fold(_ => false)(_ => true) + + def getPreferred: Option[P] = + fold[Option[P]](p => Some(p))(_ => None) + + def getFallback: Option[F] = + fold[Option[F]](_ => None)(f => Some(f)) +} + +object Priority extends FindPreferred { + + case class Preferred[P](get: P) extends Priority[P, Nothing] + case class Fallback[F](get: F) extends Priority[Nothing, F] + + def apply[P, F](implicit ev: Priority[P, F]): Priority[P, F] = ev +} + +private[kernel] trait FindPreferred extends FindFallback { + implicit def preferred[P](implicit ev: P): Priority[P, Nothing] = + Priority.Preferred(ev) +} + +private[kernel] trait FindFallback { + implicit def fallback[F](implicit ev: F): Priority[Nothing, F] = + Priority.Fallback(ev) +} diff --git a/kernel/src/main/scala/cats/kernel/Semigroup.scala b/kernel/src/main/scala/cats/kernel/Semigroup.scala new file mode 100644 index 0000000000..8e17529793 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/Semigroup.scala @@ -0,0 +1,79 @@ +package cats.kernel + +import scala.{ specialized => sp } +import scala.annotation.{ tailrec } + +/** + * A semigroup is any set `A` with an associative operation (`combine`). + */ +trait Semigroup[@sp(Int, Long, Float, Double) A] extends Any with Serializable { + + /** + * Associative operation taking which combines two values. + */ + def combine(x: A, y: A): A + + /** + * Return `a` combined with itself `n` times. + */ + def combineN(a: A, n: Int): A = + if (n <= 0) throw new IllegalArgumentException("Repeated combining for semigroups must have n > 0") + else repeatedCombineN(a, n) + + /** + * Return `a` combined with itself more than once. + */ + protected[this] def repeatedCombineN(a: A, n: Int): A = { + @tailrec def loop(b: A, k: Int, extra: A): A = + if (k == 1) combine(b, extra) else { + val x = if ((k & 1) == 1) combine(b, extra) else extra + loop(combine(b, b), k >>> 1, x) + } + if (n == 1) a else loop(a, n - 1, a) + } + + /** + * Given a sequence of `as`, combine them and return the total. + * + * If the sequence is empty, returns None. Otherwise, returns Some(total). + */ + def combineAllOption(as: TraversableOnce[A]): Option[A] = + as.reduceOption(combine) +} + +trait SemigroupFunctions[S[T] <: Semigroup[T]] { + def combine[@sp(Int, Long, Float, Double) A](x: A, y: A)(implicit ev: S[A]): A = + ev.combine(x, y) + + def maybeCombine[@sp(Int, Long, Float, Double) A](ox: Option[A], y: A)(implicit ev: S[A]): A = + ox match { + case Some(x) => ev.combine(x, y) + case None => y + } + + def maybeCombine[@sp(Int, Long, Float, Double) A](x: A, oy: Option[A])(implicit ev: S[A]): A = + oy match { + case Some(y) => ev.combine(x, y) + case None => x + } + + def isCommutative[A](implicit ev: S[A]): Boolean = + ev.isInstanceOf[CommutativeSemigroup[_]] + + def isIdempotent[A](implicit ev: S[A]): Boolean = + ev.isInstanceOf[Band[_]] + + def combineN[@sp(Int, Long, Float, Double) A](a: A, n: Int)(implicit ev: S[A]): A = + ev.combineN(a, n) + + def combineAllOption[A](as: TraversableOnce[A])(implicit ev: S[A]): Option[A] = + ev.combineAllOption(as) +} + +object Semigroup extends SemigroupFunctions[Semigroup] { + + /** + * Access an implicit `Semigroup[A]`. + */ + @inline final def apply[A](implicit ev: Semigroup[A]) = ev +} diff --git a/kernel/src/main/scala/cats/kernel/Semilattice.scala b/kernel/src/main/scala/cats/kernel/Semilattice.scala new file mode 100644 index 0000000000..92cc80db86 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/Semilattice.scala @@ -0,0 +1,62 @@ +package cats.kernel + +import scala.{specialized => sp} + +/** + * Semilattices are commutative semigroups whose operation + * (i.e. combine) is also idempotent. + */ +trait Semilattice[@sp(Int, Long, Float, Double) A] extends Any + with Band[A] + with CommutativeSemigroup[A] { self => + + /** + * Given Eq[A], return a PartialOrder[A] using the `combine` + * operator to determine the partial ordering. This method assumes + * `combine` functions as `meet` (that is, as a lower bound). + * + * This method returns: + * + * 0.0 if x = y + * -1.0 if x = combine(x, y) + * 1.0 if y = combine(x, y) + * NaN otherwise + */ + def asMeetPartialOrder(implicit ev: Eq[A]): PartialOrder[A] = + new PartialOrder[A] { + def partialCompare(x: A, y: A): Double = + if (ev.eqv(x, y)) 0.0 else { + val z = self.combine(x, y) + if (ev.eqv(x, z)) -1.0 else if (ev.eqv(y, z)) 1.0 else Double.NaN + } + } + + /** + * Given Eq[A], return a PartialOrder[A] using the `combine` + * operator to determine the partial ordering. This method assumes + * `combine` functions as `join` (that is, as an upper bound). + * + * This method returns: + * + * 0.0 if x = y + * -1.0 if y = combine(x, y) + * 1.0 if x = combine(x, y) + * NaN otherwise + */ + def asJoinPartialOrder(implicit ev: Eq[A]): PartialOrder[A] = + new PartialOrder[A] { + def partialCompare(x: A, y: A): Double = + if (ev.eqv(x, y)) 0.0 else { + val z = self.combine(x, y) + if (ev.eqv(y, z)) -1.0 else if (ev.eqv(x, z)) 1.0 else Double.NaN + } + } +} + +object Semilattice { + + /** + * Access an implicit `Semilattice[A]`. + */ + @inline final def apply[@sp(Int, Long, Float, Double) A](implicit ev: Semilattice[A]): Semilattice[A] = ev +} diff --git a/kernel/src/main/scala/cats/kernel/package.scala b/kernel/src/main/scala/cats/kernel/package.scala new file mode 100644 index 0000000000..80fd3067b8 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/package.scala @@ -0,0 +1,3 @@ +package cats +package object kernel { +} From 38a3dc8e9a1222e26b0d2f208366b1ba851a0d2f Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 10:33:14 -0400 Subject: [PATCH 076/185] Port relevant instances from algebra.std to cats.kernel.std. Algebra doesn't currently provide generic semigroups for number types, so this commit doesn't have any of those either. Cats is already working around this so that shouldn't be a big change. --- build.sbt | 5 +- .../src/main/scala/cats/kernel/std/Util.scala | 181 ++++++++++++++++++ .../src/main/scala/cats/kernel/std/all.scala | 23 +++ .../main/scala/cats/kernel/std/array.scala | 70 +++++++ .../main/scala/cats/kernel/std/bigInt.scala | 24 +++ .../main/scala/cats/kernel/std/boolean.scala | 24 +++ .../src/main/scala/cats/kernel/std/byte.scala | 26 +++ .../src/main/scala/cats/kernel/std/char.scala | 19 ++ .../main/scala/cats/kernel/std/double.scala | 26 +++ .../main/scala/cats/kernel/std/float.scala | 32 ++++ .../src/main/scala/cats/kernel/std/int.scala | 26 +++ .../src/main/scala/cats/kernel/std/list.scala | 95 +++++++++ .../src/main/scala/cats/kernel/std/long.scala | 27 +++ .../src/main/scala/cats/kernel/std/map.scala | 43 +++++ .../main/scala/cats/kernel/std/option.scala | 68 +++++++ .../src/main/scala/cats/kernel/std/set.scala | 18 ++ .../main/scala/cats/kernel/std/short.scala | 27 +++ .../main/scala/cats/kernel/std/string.scala | 24 +++ .../main/scala/cats/kernel/std/tuple.scala | 4 + .../src/main/scala/cats/kernel/std/unit.scala | 29 +++ project/KernelBoiler.scala | 172 +++++++++++++++++ 21 files changed, 960 insertions(+), 3 deletions(-) create mode 100644 kernel/src/main/scala/cats/kernel/std/Util.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/all.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/array.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/bigInt.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/boolean.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/byte.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/char.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/double.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/float.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/int.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/list.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/long.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/map.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/option.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/set.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/short.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/string.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/tuple.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/unit.scala create mode 100644 project/KernelBoiler.scala diff --git a/build.sbt b/build.sbt index 1eccc559a3..177acefb6b 100644 --- a/build.sbt +++ b/build.sbt @@ -176,6 +176,7 @@ lazy val kernel = crossProject.crossType(CrossType.Pure) .settings(buildSettings: _*) .settings(publishSettings: _*) .settings(scoverageSettings: _*) + .settings(sourceGenerators in Compile <+= (sourceManaged in Compile).map(KernelBoiler.gen)) .jsSettings(commonJsSettings:_*) .jvmSettings(commonJvmSettings:_*) @@ -199,9 +200,7 @@ lazy val core = crossProject.crossType(CrossType.Pure) .dependsOn(macros) .settings(moduleName := "cats-core") .settings(catsSettings:_*) - .settings( - sourceGenerators in Compile <+= (sourceManaged in Compile).map(Boilerplate.gen) - ) + .settings(sourceGenerators in Compile <+= (sourceManaged in Compile).map(Boilerplate.gen)) .settings(libraryDependencies += "org.scalacheck" %%% "scalacheck" % scalacheckVersion % "test") .jsSettings(commonJsSettings:_*) .jvmSettings(commonJvmSettings:_*) diff --git a/kernel/src/main/scala/cats/kernel/std/Util.scala b/kernel/src/main/scala/cats/kernel/std/Util.scala new file mode 100644 index 0000000000..93655d09e0 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/Util.scala @@ -0,0 +1,181 @@ +package cats.kernel.std.util + +import java.lang.Double.{ longBitsToDouble, doubleToLongBits } +import java.lang.Float.{ intBitsToFloat, floatToIntBits } +import java.lang.Long.{ numberOfTrailingZeros, numberOfLeadingZeros } +import java.lang.Math +import scala.annotation.tailrec +import scala.collection.mutable + +object StaticMethods { + + /** + * Implementation of the binary GCD algorithm. + */ + final def gcd(x0: Long, y0: Long): Long = { + // if either argument is 0, just return the other. + if (x0 == 0L) return y0 + if (y0 == 0L) return x0 + + val xz = numberOfTrailingZeros(x0) + val yz = numberOfTrailingZeros(y0) + + // Math.abs is safe because Long.MinValue (0x8000000000000000) + // will be shifted over 63 bits (due to trailing zeros). + var x = Math.abs(x0 >> xz) + var y = Math.abs(y0 >> yz) + + while (x != y) { + if (x > y) { + x -= y + x >>= numberOfTrailingZeros(x) + } else { + y -= x + y >>= numberOfTrailingZeros(y) + } + } + + // trailing zeros mean powers of two -- if both numbers had + // trailing zeros, those are part of the common divsor as well. + if (xz < yz) x << xz else x << yz + } + + /** + * GCD for Float values. + */ + final def gcd(a: Float, b: Float): Float = { + import java.lang.Integer.{ numberOfTrailingZeros, numberOfLeadingZeros } + + def value(bits: Int): Int = bits & 0x007FFFFF | 0x00800000 + + def exp(bits: Int): Int = ((bits >> 23) & 0xFF).toInt + + def gcd0(val0: Int, exp0: Int, val1: Int, exp1: Int): Float = { + val tz0 = numberOfTrailingZeros(val0) + val tz1 = numberOfTrailingZeros(val1) + val tzShared = Math.min(tz0, tz1 + exp1 - exp0) + val n = gcd(val0.toLong >>> tz0, val1.toLong >>> tz1).toInt << tzShared + + val shift = numberOfLeadingZeros(n) - 8 // Number of bits to move 1 to bit 23 + val mantissa = (n << shift) & 0x007FFFFF + val exp = (exp0 - shift) + if (exp < 0) 0F else intBitsToFloat((exp << 23) | mantissa) + } + + if (a == 0F) b + else if (b == 0F) a + else { + val aBits = floatToIntBits(a) + val aVal = value(aBits) + val aExp = exp(aBits) + + val bBits = floatToIntBits(b) + val bVal = value(bBits) + val bExp = exp(bBits) + + if (aExp < bExp) gcd0(aVal, aExp, bVal, bExp) + else gcd0(bVal, bExp, aVal, aExp) + } + } + + /** + * GCD for Double values. + */ + final def gcd(a: Double, b: Double): Double = { + def value(bits: Long): Long = bits & 0x000FFFFFFFFFFFFFL | 0x0010000000000000L + + def exp(bits: Long): Int = ((bits >> 52) & 0x7FF).toInt + + def gcd0(val0: Long, exp0: Int, val1: Long, exp1: Int): Double = { + val tz0 = numberOfTrailingZeros(val0) + val tz1 = numberOfTrailingZeros(val1) + val tzShared = Math.min(tz0, tz1 + exp1 - exp0) + val n = gcd(val0 >>> tz0, val1 >>> tz1) << tzShared + + val shift = numberOfLeadingZeros(n) - 11 // Number of bits to move 1 to bit 52 + val mantissa = (n << shift) & 0x000FFFFFFFFFFFFFL + val exp = (exp0 - shift).toLong + if (exp < 0) 0.0 else longBitsToDouble((exp << 52) | mantissa) + } + + if (a == 0D) b + else if (b == 0D) a + else { + val aBits = doubleToLongBits(a) + val aVal = value(aBits) + val aExp = exp(aBits) + + val bBits = doubleToLongBits(b) + val bVal = value(bBits) + val bExp = exp(bBits) + + if (aExp < bExp) gcd0(aVal, aExp, bVal, bExp) + else gcd0(bVal, bExp, aVal, aExp) + } + } + + /** + * Exponentiation function, e.g. x^y + * + * If base^ex doesn't fit in a Long, the result will overflow (unlike + * Math.pow which will return +/- Infinity). + */ + final def pow(base: Long, exponent: Long): Long = { + @tailrec def loop(t: Long, b: Long, e: Long): Long = + if (e == 0L) t + else if ((e & 1) == 1) loop(t * b, b * b, e >>> 1L) + else loop(t, b * b, e >>> 1L) + + if (exponent >= 0L) loop(1L, base, exponent) else { + if(base == 0L) throw new ArithmeticException("zero can't be raised to negative power") + else if (base == 1L) 1L + else if (base == -1L) if ((exponent & 1L) == 0L) -1L else 1L + else 0L + } + } + + def initMutableMap[K, V](m: Map[K, V]): mutable.Map[K, V] = { + val result = mutable.Map.empty[K, V] + m.foreach { case (k, v) => result(k) = v } + result + } + + def wrapMutableMap[K, V](m: mutable.Map[K, V]): Map[K, V] = + new WrappedMutableMap(m) + + private[kernel] class WrappedMutableMap[K, V](m: mutable.Map[K, V]) extends Map[K, V] { + override def size: Int = m.size + def get(k: K): Option[V] = m.get(k) + def iterator: Iterator[(K, V)] = m.iterator + def +[V2 >: V](kv: (K, V2)): Map[K, V2] = m.toMap + kv + def -(key: K): Map[K, V] = m.toMap - key + } + + def addMap[K, V](x: Map[K, V], y: Map[K, V])(f: (V, V) => V): Map[K, V] = { + val (small, big, g) = + if (x.size <= y.size) (x, y, f) + else (y, x, (v1: V, v2: V) => f(v2, v1)) + + val m = initMutableMap(big) + small.foreach { case (k, v1) => + m(k) = m.get(k) match { + case Some(v2) => g(v1, v2) + case None => v1 + } + } + wrapMutableMap(m) + } + + def subtractMap[K, V](x: Map[K, V], y: Map[K, V])(subtract: (V, V) => V)(negate: V => V): Map[K, V] = { + // even if x is smaller, we'd need to call map/foreach on y to + // negate all its values, so this is just as fast or faster. + val m = initMutableMap(x) + y.foreach { case (k, v2) => + m(k) = m.get(k) match { + case Some(v1) => subtract(v1, v2) + case None => negate(v2) + } + } + wrapMutableMap(m) + } +} diff --git a/kernel/src/main/scala/cats/kernel/std/all.scala b/kernel/src/main/scala/cats/kernel/std/all.scala new file mode 100644 index 0000000000..233e7d6c9c --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/all.scala @@ -0,0 +1,23 @@ +package cats.kernel +package std + +package object all extends AllInstances + +trait AllInstances + extends BigIntInstances + with BooleanInstances + with ByteInstances + with CharInstances + with DoubleInstances + with FloatInstances + with IntInstances + with ListInstances + with LongInstances + with MapInstances + with OptionInstances + with SetInstances + with ShortInstances + with StringInstances + with TupleInstances + with UnitInstances + with ArrayInstances diff --git a/kernel/src/main/scala/cats/kernel/std/array.scala b/kernel/src/main/scala/cats/kernel/std/array.scala new file mode 100644 index 0000000000..1cad7e6185 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/array.scala @@ -0,0 +1,70 @@ +package cats.kernel +package std + +import scala.{ specialized => sp } + +package object array extends ArrayInstances + +trait ArrayInstances { + implicit def arrayEq[@sp A: Eq]: Eq[Array[A]] = + new ArrayEq[A] + implicit def arrayOrder[@sp A: Order]: Order[Array[A]] = + new ArrayOrder[A] + implicit def arrayPartialOrder[@sp A: PartialOrder]: PartialOrder[Array[A]] = + new ArrayPartialOrder[A] +} + +object ArraySupport { + + private def signum(x: Int): Int = + if(x < 0) -1 + else if(x > 0) 1 + else 0 + + def eqv[@sp A: Eq](x: Array[A], y: Array[A]): Boolean = { + var i = 0 + if (x.length != y.length) return false + while (i < x.length && i < y.length && Eq.eqv(x(i), y(i))) i += 1 + i == x.length + } + + def compare[@sp A: Order](x: Array[A], y: Array[A]): Int = { + var i = 0 + while (i < x.length && i < y.length) { + val cmp = Order.compare(x(i), y(i)) + if (cmp != 0) return cmp + i += 1 + } + signum(x.length - y.length) + } + + def partialCompare[@sp A: PartialOrder](x: Array[A], y: Array[A]): Double = { + var i = 0 + while (i < x.length && i < y.length) { + val cmp = PartialOrder.partialCompare(x(i), y(i)) + // Double.NaN is also != 0.0 + if (cmp != 0.0) return cmp + i += 1 + } + signum(x.length - y.length).toDouble + } +} + +final class ArrayEq[@sp A: Eq] extends Eq[Array[A]] { + def eqv(x: Array[A], y: Array[A]): Boolean = + ArraySupport.eqv(x, y) +} + +final class ArrayOrder[@sp A: Order] extends Order[Array[A]] { + override def eqv(x: Array[A], y: Array[A]): Boolean = + ArraySupport.eqv(x, y) + def compare(x: Array[A], y: Array[A]): Int = + ArraySupport.compare(x, y) +} + +final class ArrayPartialOrder[@sp A: PartialOrder] extends PartialOrder[Array[A]] { + override def eqv(x: Array[A], y: Array[A]): Boolean = + ArraySupport.eqv(x, y) + override def partialCompare(x: Array[A], y: Array[A]): Double = + ArraySupport.partialCompare(x, y) +} diff --git a/kernel/src/main/scala/cats/kernel/std/bigInt.scala b/kernel/src/main/scala/cats/kernel/std/bigInt.scala new file mode 100644 index 0000000000..13afc51058 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/bigInt.scala @@ -0,0 +1,24 @@ +package cats.kernel +package std + +package object bigInt extends BigIntInstances + +trait BigIntInstances { + implicit val bigIntOrder: Order[BigInt] = + new BigIntOrder +} + +class BigIntOrder extends Order[BigInt] { + + def compare(x: BigInt, y: BigInt): Int = x compare y + + override def eqv(x:BigInt, y:BigInt): Boolean = x == y + override def neqv(x:BigInt, y:BigInt): Boolean = x != y + override def gt(x: BigInt, y: BigInt): Boolean = x > y + override def gteqv(x: BigInt, y: BigInt): Boolean = x >= y + override def lt(x: BigInt, y: BigInt): Boolean = x < y + override def lteqv(x: BigInt, y: BigInt): Boolean = x <= y + + override def min(x: BigInt, y: BigInt): BigInt = x min y + override def max(x: BigInt, y: BigInt): BigInt = x max y +} diff --git a/kernel/src/main/scala/cats/kernel/std/boolean.scala b/kernel/src/main/scala/cats/kernel/std/boolean.scala new file mode 100644 index 0000000000..8f83996118 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/boolean.scala @@ -0,0 +1,24 @@ +package cats.kernel +package std + +package object boolean extends BooleanInstances + +trait BooleanInstances { + implicit val booleanOrder: BooleanOrder = + new BooleanOrder +} + +class BooleanOrder extends Order[Boolean] { + def compare(x: Boolean, y: Boolean): Int = + if (x == y) 0 else if (x) 1 else -1 + + override def eqv(x:Boolean, y:Boolean): Boolean = x == y + override def neqv(x:Boolean, y:Boolean): Boolean = x != y + override def gt(x: Boolean, y: Boolean): Boolean = x && !y + override def lt(x: Boolean, y: Boolean): Boolean = !x && y + override def gteqv(x: Boolean, y: Boolean): Boolean = x == y || x + override def lteqv(x: Boolean, y: Boolean): Boolean = x == y || y + + override def min(x: Boolean, y: Boolean): Boolean = x && y + override def max(x: Boolean, y: Boolean): Boolean = x || y +} diff --git a/kernel/src/main/scala/cats/kernel/std/byte.scala b/kernel/src/main/scala/cats/kernel/std/byte.scala new file mode 100644 index 0000000000..0b1a948ff8 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/byte.scala @@ -0,0 +1,26 @@ +package cats.kernel +package std + +package object byte extends ByteInstances + +trait ByteInstances { + implicit val byteOrder: Order[Byte] = new ByteOrder +} + +class ByteOrder extends Order[Byte] { + + def compare(x: Byte, y: Byte): Int = + if (x < y) -1 else if (x > y) 1 else 0 + + override def eqv(x: Byte, y: Byte) = x == y + override def neqv(x: Byte, y: Byte) = x != y + override def gt(x: Byte, y: Byte) = x > y + override def gteqv(x: Byte, y: Byte) = x >= y + override def lt(x: Byte, y: Byte) = x < y + override def lteqv(x: Byte, y: Byte) = x <= y + + override def min(x: Byte, y: Byte): Byte = + java.lang.Math.min(x.toInt, y.toInt).toByte + override def max(x: Byte, y: Byte): Byte = + java.lang.Math.max(x.toInt, y.toInt).toByte +} diff --git a/kernel/src/main/scala/cats/kernel/std/char.scala b/kernel/src/main/scala/cats/kernel/std/char.scala new file mode 100644 index 0000000000..7a863a8cab --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/char.scala @@ -0,0 +1,19 @@ +package cats.kernel +package std + +package object char extends CharInstances + +trait CharInstances { + implicit val charOrder = new CharOrder +} + +class CharOrder extends Order[Char] { + def compare(x: Char, y: Char) = + if (x < y) -1 else if (x > y) 1 else 0 + override def eqv(x:Char, y:Char) = x == y + override def neqv(x:Char, y:Char) = x != y + override def gt(x: Char, y: Char) = x > y + override def gteqv(x: Char, y: Char) = x >= y + override def lt(x: Char, y: Char) = x < y + override def lteqv(x: Char, y: Char) = x <= y +} diff --git a/kernel/src/main/scala/cats/kernel/std/double.scala b/kernel/src/main/scala/cats/kernel/std/double.scala new file mode 100644 index 0000000000..32945635c0 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/double.scala @@ -0,0 +1,26 @@ +package cats.kernel +package std + +import java.lang.Math + +trait DoubleInstances { + implicit val doubleOrder: Order[Double] = new DoubleOrder +} + +class DoubleOrder extends Order[Double] { + + def compare(x: Double, y: Double): Int = + java.lang.Double.compare(x, y) + + override def eqv(x:Double, y:Double) = x == y + override def neqv(x:Double, y:Double) = x != y + override def gt(x: Double, y: Double) = x > y + override def gteqv(x: Double, y: Double) = x >= y + override def lt(x: Double, y: Double) = x < y + override def lteqv(x: Double, y: Double) = x <= y + + override def min(x: Double, y: Double): Double = + Math.min(x, y) + override def max(x: Double, y: Double): Double = + Math.max(x, y) +} diff --git a/kernel/src/main/scala/cats/kernel/std/float.scala b/kernel/src/main/scala/cats/kernel/std/float.scala new file mode 100644 index 0000000000..f50e96f1fc --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/float.scala @@ -0,0 +1,32 @@ +package cats.kernel +package std + +trait FloatInstances { + implicit val floatOrder = new FloatOrder +} + +/** + * Due to the way floating-point equality works, this instance is not + * lawful under equality, but is correct when taken as an + * approximation of an exact value. + * + * If you would prefer an absolutely lawful fractional value, you'll + * need to investigate rational numbers or more exotic types. + */ +class FloatOrder extends Order[Float] { + + def compare(x: Float, y: Float) = + java.lang.Float.compare(x, y) + + override def eqv(x:Float, y:Float) = x == y + override def neqv(x:Float, y:Float) = x != y + override def gt(x: Float, y: Float) = x > y + override def gteqv(x: Float, y: Float) = x >= y + override def lt(x: Float, y: Float) = x < y + override def lteqv(x: Float, y: Float) = x <= y + + override def min(x: Float, y: Float): Float = + java.lang.Math.min(x, y) + override def max(x: Float, y: Float): Float = + java.lang.Math.max(x, y) +} diff --git a/kernel/src/main/scala/cats/kernel/std/int.scala b/kernel/src/main/scala/cats/kernel/std/int.scala new file mode 100644 index 0000000000..59fb8474ff --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/int.scala @@ -0,0 +1,26 @@ +package cats.kernel +package std + +package object int extends IntInstances + +trait IntInstances { + implicit val intOrder: Order[Int] = new IntOrder +} + +class IntOrder extends Order[Int] { + + def compare(x: Int, y: Int): Int = + if (x < y) -1 else if (x > y) 1 else 0 + + override def eqv(x: Int, y: Int) = x == y + override def neqv(x: Int, y: Int) = x != y + override def gt(x: Int, y: Int) = x > y + override def gteqv(x: Int, y: Int) = x >= y + override def lt(x: Int, y: Int) = x < y + override def lteqv(x: Int, y: Int) = x <= y + + override def min(x: Int, y: Int): Int = + java.lang.Math.min(x, y) + override def max(x: Int, y: Int): Int = + java.lang.Math.max(x, y) +} diff --git a/kernel/src/main/scala/cats/kernel/std/list.scala b/kernel/src/main/scala/cats/kernel/std/list.scala new file mode 100644 index 0000000000..ee717ac78e --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/list.scala @@ -0,0 +1,95 @@ +package cats.kernel +package std + +import scala.annotation.tailrec +import scala.collection.mutable + +package object list extends ListInstances + +trait ListInstances extends ListInstances1 { + implicit def listOrder[A: Order] = new ListOrder[A] + implicit def listMonoid[A] = new ListMonoid[A] +} + +trait ListInstances1 extends ListInstances2 { + implicit def listPartialOrder[A: PartialOrder] = new ListPartialOrder[A] +} + +trait ListInstances2 { + implicit def listEq[A: Eq] = new ListEq[A] +} + +class ListOrder[A](implicit ev: Order[A]) extends Order[List[A]] { + def compare(xs: List[A], ys: List[A]): Int = { + @tailrec def loop(xs: List[A], ys: List[A]): Int = + xs match { + case Nil => + if (ys.isEmpty) 0 else -1 + case x :: xs => + ys match { + case Nil => 1 + case y :: ys => + val n = ev.compare(x, y) + if (n != 0) n else loop(xs, ys) + } + } + loop(xs, ys) + } +} + +class ListPartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[List[A]] { + def partialCompare(xs: List[A], ys: List[A]): Double = { + @tailrec def loop(xs: List[A], ys: List[A]): Double = + xs match { + case Nil => + if (ys.isEmpty) 0.0 else -1.0 + case x :: xs => + ys match { + case Nil => 1.0 + case y :: ys => + val n = ev.partialCompare(x, y) + if (n != 0.0) n else loop(xs, ys) + } + } + loop(xs, ys) + } +} + +class ListEq[A](implicit ev: Eq[A]) extends Eq[List[A]] { + def eqv(x: List[A], y: List[A]): Boolean = { + def loop(xs: List[A], ys: List[A]): Boolean = + xs match { + case Nil => + ys.isEmpty + case x :: xs => + ys match { + case y :: ys => + if (ev.eqv(x, y)) loop(xs, ys) else false + case Nil => + false + } + } + loop(x, y) + } +} + +class ListMonoid[A] extends Monoid[List[A]] { + def empty: List[A] = Nil + def combine(x: List[A], y: List[A]): List[A] = x ::: y + + override def combineN(x: List[A], n: Int): List[A] = { + val buf = mutable.ListBuffer.empty[A] + var i = n + while (i > 0) { + buf ++= x + i -= 1 + } + buf.toList + } + + override def combineAll(xs: TraversableOnce[List[A]]): List[A] = { + val buf = mutable.ListBuffer.empty[A] + xs.foreach(buf ++= _) + buf.toList + } +} diff --git a/kernel/src/main/scala/cats/kernel/std/long.scala b/kernel/src/main/scala/cats/kernel/std/long.scala new file mode 100644 index 0000000000..8d978062cc --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/long.scala @@ -0,0 +1,27 @@ +package cats.kernel +package std + +package object long extends LongInstances + +trait LongInstances { + implicit val longOrder = new LongOrder +} + +class LongOrder extends Order[Long] { + + // use java.lang.Long.compare if we can rely on java >= 1.7 + def compare(x: Long, y: Long): Int = + if (x < y) -1 else if (x > y) 1 else 0 + + override def eqv(x: Long, y: Long) = x == y + override def neqv(x: Long, y: Long) = x != y + override def gt(x: Long, y: Long) = x > y + override def gteqv(x: Long, y: Long) = x >= y + override def lt(x: Long, y: Long) = x < y + override def lteqv(x: Long, y: Long) = x <= y + + override def min(x: Long, y: Long): Long = + java.lang.Math.min(x, y) + override def max(x: Long, y: Long): Long = + java.lang.Math.max(x, y) +} diff --git a/kernel/src/main/scala/cats/kernel/std/map.scala b/kernel/src/main/scala/cats/kernel/std/map.scala new file mode 100644 index 0000000000..912b7bab48 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/map.scala @@ -0,0 +1,43 @@ +package cats.kernel +package std + +import cats.kernel.std.util.StaticMethods.{ addMap, subtractMap } + +package object map extends MapInstances + +trait MapInstances extends MapInstances0 { + implicit def mapGroup[K, V: Group]: MapGroup[K, V] = + new MapGroup[K, V] +} + +trait MapInstances0 { + implicit def mapEq[K, V: Eq]: Eq[Map[K, V]] = + new MapEq[K, V] + implicit def mapMonoid[K, V: Semigroup]: MapMonoid[K, V] = + new MapMonoid[K, V] +} + +class MapEq[K, V](implicit V: Eq[V]) extends Eq[Map[K, V]] { + def eqv(x: Map[K, V], y: Map[K, V]): Boolean = + x.size == y.size && x.forall { case (k, v1) => + y.get(k) match { + case Some(v2) => V.eqv(v1, v2) + case None => false + } + } +} + +class MapMonoid[K, V](implicit V: Semigroup[V]) extends Monoid[Map[K, V]] { + def empty: Map[K, V] = Map.empty + + def combine(x: Map[K, V], y: Map[K, V]): Map[K, V] = + addMap(x, y)(V.combine) +} + +class MapGroup[K, V](implicit V: Group[V]) extends MapMonoid[K, V] with Group[Map[K, V]] { + def inverse(x: Map[K, V]): Map[K, V] = + x.map { case (k, v) => (k, V.inverse(v)) } + + override def remove(x: Map[K, V], y: Map[K, V]): Map[K, V] = + subtractMap(x, y)(V.remove)(V.inverse) +} diff --git a/kernel/src/main/scala/cats/kernel/std/option.scala b/kernel/src/main/scala/cats/kernel/std/option.scala new file mode 100644 index 0000000000..db0c8a7126 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/option.scala @@ -0,0 +1,68 @@ +package cats.kernel +package std + +package object option extends OptionInstances + +trait OptionInstances extends OptionInstances1 { + implicit def optionOrder[A: Order] = new OptionOrder[A] + implicit def optionMonoid[A: Semigroup] = new OptionMonoid[A] +} + +trait OptionInstances1 extends OptionInstances0 { + implicit def optionPartialOrder[A: PartialOrder] = new OptionPartialOrder[A] +} + +trait OptionInstances0 { + implicit def optionEq[A: Eq] = new OptionEq[A] +} + +class OptionOrder[A](implicit A: Order[A]) extends Order[Option[A]] { + def compare(x: Option[A], y: Option[A]): Int = + x match { + case None => + if (y.isEmpty) 0 else -1 + case Some(a) => + y match { + case None => 1 + case Some(b) => A.compare(a, b) + } + } +} + +class OptionPartialOrder[A](implicit A: PartialOrder[A]) extends PartialOrder[Option[A]] { + def partialCompare(x: Option[A], y: Option[A]): Double = + x match { + case None => + if (y.isEmpty) 0.0 else -1.0 + case Some(a) => + y match { + case None => 1.0 + case Some(b) => A.partialCompare(a, b) + } + } +} + +class OptionEq[A](implicit A: Eq[A]) extends Eq[Option[A]] { + def eqv(x: Option[A], y: Option[A]): Boolean = + x match { + case None => y.isEmpty + case Some(a) => + y match { + case None => false + case Some(b) => A.eqv(a, b) + } + } +} + +class OptionMonoid[A](implicit A: Semigroup[A]) extends Monoid[Option[A]] { + def empty: Option[A] = None + def combine(x: Option[A], y: Option[A]): Option[A] = + x match { + case None => y + case Some(a) => + y match { + case None => x + case Some(b) => Some(A.combine(a, b)) + } + } +} diff --git a/kernel/src/main/scala/cats/kernel/std/set.scala b/kernel/src/main/scala/cats/kernel/std/set.scala new file mode 100644 index 0000000000..ab5e178d3c --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/set.scala @@ -0,0 +1,18 @@ +package cats.kernel +package std + +package object set extends SetInstances + +trait SetInstances { + implicit def setPartialOrder[A]: PartialOrder[Set[A]] = new SetPartialOrder[A] +} + +class SetPartialOrder[A] extends PartialOrder[Set[A]] { + def partialCompare(x: Set[A], y: Set[A]): Double = + if (x.size < y.size) if (x.subsetOf(y)) -1.0 else Double.NaN + else if (y.size < x.size) -partialCompare(y, x) + else if (x == y) 0.0 + else Double.NaN + + override def eqv(x: Set[A], y: Set[A]): Boolean = x == y +} diff --git a/kernel/src/main/scala/cats/kernel/std/short.scala b/kernel/src/main/scala/cats/kernel/std/short.scala new file mode 100644 index 0000000000..ba3582205c --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/short.scala @@ -0,0 +1,27 @@ +package cats.kernel +package std + +package object short extends ShortInstances + +trait ShortInstances { + implicit val shortOrder: Order[Short] = new ShortOrder +} + +class ShortOrder extends Order[Short] { + + // use java.lang.Short.compare if we can rely on java >= 1.7 + def compare(x: Short, y: Short): Int = + if (x < y) -1 else if (x > y) 1 else 0 + + override def eqv(x: Short, y: Short) = x == y + override def neqv(x: Short, y: Short) = x != y + override def gt(x: Short, y: Short) = x > y + override def gteqv(x: Short, y: Short) = x >= y + override def lt(x: Short, y: Short) = x < y + override def lteqv(x: Short, y: Short) = x <= y + + override def min(x: Short, y: Short): Short = + java.lang.Math.min(x.toInt, y.toInt).toShort + override def max(x: Short, y: Short): Short = + java.lang.Math.max(x.toInt, y.toInt).toShort +} diff --git a/kernel/src/main/scala/cats/kernel/std/string.scala b/kernel/src/main/scala/cats/kernel/std/string.scala new file mode 100644 index 0000000000..52ac8de129 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/string.scala @@ -0,0 +1,24 @@ +package cats.kernel +package std + +package object string extends StringInstances + +trait StringInstances { + implicit val stringOrder: Order[String] = new StringOrder + implicit val stringMonoid = new StringMonoid +} + +class StringOrder extends Order[String] { + def compare(x: String, y: String): Int = x compare y +} + +class StringMonoid extends Monoid[String] { + def empty: String = "" + def combine(x: String, y: String): String = x + y + + override def combineAll(xs: TraversableOnce[String]): String = { + val sb = new StringBuilder + xs.foreach(sb.append) + sb.toString + } +} diff --git a/kernel/src/main/scala/cats/kernel/std/tuple.scala b/kernel/src/main/scala/cats/kernel/std/tuple.scala new file mode 100644 index 0000000000..ce1cb89643 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/tuple.scala @@ -0,0 +1,4 @@ +package cats.kernel +package std + +package object tuple extends TupleInstances diff --git a/kernel/src/main/scala/cats/kernel/std/unit.scala b/kernel/src/main/scala/cats/kernel/std/unit.scala new file mode 100644 index 0000000000..8068b246de --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/unit.scala @@ -0,0 +1,29 @@ +package cats.kernel +package std + +package object unit extends UnitInstances + +trait UnitInstances { + implicit val unitAlgebra: Order[Unit] with BoundedSemilattice[Unit] = + new UnitAlgebra +} + +class UnitAlgebra extends Order[Unit] with BoundedSemilattice[Unit] { + def compare(x: Unit, y: Unit): Int = 0 + + override def eqv(x: Unit, y: Unit): Boolean = true + override def neqv(x: Unit, y: Unit): Boolean = false + override def gt(x: Unit, y: Unit): Boolean = false + override def lt(x: Unit, y: Unit): Boolean = false + override def gteqv(x: Unit, y: Unit): Boolean = true + override def lteqv(x: Unit, y: Unit): Boolean = true + + override def min(x: Unit, y: Unit): Unit = () + override def max(x: Unit, y: Unit): Unit = () + + def empty: Unit = () + def combine(x: Unit, y: Unit): Unit = () + override protected[this] def repeatedCombineN(a: Unit, n: Int): Unit = () + override def combineAllOption(as: TraversableOnce[Unit]): Option[Unit] = + if (as.isEmpty) None else Some(()) +} diff --git a/project/KernelBoiler.scala b/project/KernelBoiler.scala new file mode 100644 index 0000000000..69a46537fe --- /dev/null +++ b/project/KernelBoiler.scala @@ -0,0 +1,172 @@ +import sbt._ + +/** + * Generate a range of boilerplate classes that would be tedious to write and maintain by hand. + * + * Copied, with some modifications, from + * [[https://github.com/milessabin/shapeless/blob/master/project/Boilerplate.scala Shapeless]]. + * + * @author Miles Sabin + * @author Kevin Wright + */ +object KernelBoiler { + import scala.StringContext._ + + implicit class BlockHelper(val sc: StringContext) extends AnyVal { + def block(args: Any*): String = { + val interpolated = sc.standardInterpolator(treatEscapes, args) + val rawLines = interpolated.split('\n') + val trimmedLines = rawLines.map(_.dropWhile(_.isWhitespace)) + trimmedLines.mkString("\n") + } + } + + val templates: Seq[Template] = Seq( + GenTupleInstances + ) + + val header = "// auto-generated boilerplate" + val maxArity = 22 + + /** + * Return a sequence of the generated files. + * + * As a side-effect, it actually generates them... + */ + def gen(dir: File): Seq[File] = templates.map { template => + val tgtFile = template.filename(dir) + IO.write(tgtFile, template.body) + tgtFile + } + + class TemplateVals(val arity: Int) { + val synTypes = (0 until arity).map(n => s"A$n") + val synVals = (0 until arity).map(n => s"a$n") + val `A..N` = synTypes.mkString(", ") + val `a..n` = synVals.mkString(", ") + val `_.._` = Seq.fill(arity)("_").mkString(", ") + val `(A..N)` = if (arity == 1) "Tuple1[A0]" else synTypes.mkString("(", ", ", ")") + val `(_.._)` = if (arity == 1) "Tuple1[_]" else Seq.fill(arity)("_").mkString("(", ", ", ")") + val `(a..n)` = if (arity == 1) "Tuple1(a)" else synVals.mkString("(", ", ", ")") + } + + /** + * Blocks in the templates below use a custom interpolator, combined with post-processing to + * produce the body. + * + * - The contents of the `header` val is output first + * - Then the first block of lines beginning with '|' + * - Then the block of lines beginning with '-' is replicated once for each arity, + * with the `templateVals` already pre-populated with relevant relevant vals for that arity + * - Then the last block of lines prefixed with '|' + * + * The block otherwise behaves as a standard interpolated string with regards to variable + * substitution. + */ + trait Template { + def filename(root: File): File + def content(tv: TemplateVals): String + def range: IndexedSeq[Int] = 1 to maxArity + def body: String = { + val headerLines = header.split('\n') + val raw = range.map(n => content(new TemplateVals(n)).split('\n').filterNot(_.isEmpty)) + val preBody = raw.head.takeWhile(_.startsWith("|")).map(_.tail) + val instances = raw.flatMap(_.filter(_.startsWith("-")).map(_.tail)) + val postBody = raw.head.dropWhile(_.startsWith("|")).dropWhile(_.startsWith("-")).map(_.tail) + (headerLines ++ preBody ++ instances ++ postBody).mkString("\n") + } + } + + object GenTupleInstances extends Template { + override def range: IndexedSeq[Int] = 1 to maxArity + + def filename(root: File): File = root / "cats" / "kernel" / "std" / "TupleAlgebra.scala" + + def content(tv: TemplateVals): String = { + import tv._ + + def constraints(constraint: String) = + synTypes.map(tpe => s"${tpe}: ${constraint}[${tpe}]").mkString(", ") + + def tuple(results: TraversableOnce[String]) = { + val resultsVec = results.toVector + val a = synTypes.size + val r = s"${0.until(a).map(i => resultsVec(i)).mkString(", ")}" + if (a == 1) "Tuple1(" ++ r ++ ")" + else s"(${r})" + } + + def binMethod(name: String) = + synTypes.zipWithIndex.iterator.map { + case (tpe, i) => + val j = i + 1 + s"${tpe}.${name}(x._${j}, y._${j})" + } + + def binTuple(name: String) = + tuple(binMethod(name)) + + def unaryTuple(name: String) = { + val m = synTypes.zipWithIndex.map { case (tpe, i) => s"${tpe}.${name}(x._${i + 1})" } + tuple(m) + } + + def nullaryTuple(name: String) = { + val m = synTypes.map(tpe => s"${tpe}.${name}") + tuple(m) + } + + block""" + |package cats.kernel + |package std + | + |trait TupleInstances { + - implicit def tuple${arity}Band[${`A..N`}](implicit ${constraints("Band")}): Band[${`(A..N)`}] = + - new Band[${`(A..N)`}] { + - def combine(x: ${`(A..N)`}, y: ${`(A..N)`}): ${`(A..N)`} = ${binTuple("combine")} + - } + - + - implicit def tuple${arity}Group[${`A..N`}](implicit ${constraints("Group")}): Group[${`(A..N)`}] = + - new Group[${`(A..N)`}] { + - def combine(x: ${`(A..N)`}, y: ${`(A..N)`}): ${`(A..N)`} = ${binTuple("combine")} + - def empty: ${`(A..N)`} = ${nullaryTuple("empty")} + - def inverse(x: ${`(A..N)`}): ${`(A..N)`} = ${unaryTuple("inverse")} + - } + - + - implicit def tuple${arity}Eq[${`A..N`}](implicit ${constraints("Eq")}): Eq[${`(A..N)`}] = + - new Eq[${`(A..N)`}] { + - def eqv(x: ${`(A..N)`}, y: ${`(A..N)`}): Boolean = ${binMethod("eqv").mkString(" && ")} + - } + - + - implicit def tuple${arity}Monoid[${`A..N`}](implicit ${constraints("Monoid")}): Monoid[${`(A..N)`}] = + - new Monoid[${`(A..N)`}] { + - def combine(x: ${`(A..N)`}, y: ${`(A..N)`}): ${`(A..N)`} = ${binTuple("combine")} + - def empty: ${`(A..N)`} = ${nullaryTuple("empty")} + - } + - + - implicit def tuple${arity}Order[${`A..N`}](implicit ${constraints("Order")}): Order[${`(A..N)`}] = + - new Order[${`(A..N)`}] { + - def compare(x: ${`(A..N)`}, y: ${`(A..N)`}): Int = + - ${binMethod("compare").find(_ != 0).getOrElse(0)} + - } + - + - implicit def tuple${arity}PartialOrder[${`A..N`}](implicit ${constraints("PartialOrder")}): PartialOrder[${`(A..N)`}] = + - new PartialOrder[${`(A..N)`}] { + - def partialCompare(x: ${`(A..N)`}, y: ${`(A..N)`}): Double = + - ${binMethod("partialCompare").find(_ != 0.0).getOrElse(0.0)} + - } + - + - implicit def tuple${arity}Semigroup[${`A..N`}](implicit ${constraints("Semigroup")}): Semigroup[${`(A..N)`}] = + - new Semigroup[${`(A..N)`}] { + - def combine(x: ${`(A..N)`}, y: ${`(A..N)`}): ${`(A..N)`} = ${binTuple("combine")} + - } + - + - implicit def tuple${arity}Semilattice[${`A..N`}](implicit ${constraints("Semilattice")}): Semilattice[${`(A..N)`}] = + - new Semilattice[${`(A..N)`}] { + - def combine(x: ${`(A..N)`}, y: ${`(A..N)`}): ${`(A..N)`} = ${binTuple("combine")} + - } + |} + """ + } + } +} From 157710f9f64308247b9f6d4e373a22127d0d235b Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 11:00:56 -0400 Subject: [PATCH 077/185] Get the relevant bits of algebra.laws ported over. There are a few things here: 1. Many of algebra's interesting laws apply to rings/lattices. 2. We introduce a (laws-only) dependency on catalysts-platform. 3. Unlike Cats, we'll run the tests in kernel-laws' tests. --- build.sbt | 14 +- .../scala/cats/kernel/laws/BaseLaws.scala | 27 ++++ .../scala/cats/kernel/laws/CheckSupport.scala | 12 ++ .../scala/cats/kernel/laws/GroupLaws.scala | 142 ++++++++++++++++++ .../cats/kernel/laws/IsSerializable.scala | 14 ++ .../scala/cats/kernel/laws/OrderLaws.scala | 79 ++++++++++ .../main/scala/cats/kernel/laws/Rules.scala | 128 ++++++++++++++++ .../main/scala/cats/kernel/laws/package.scala | 45 ++++++ 8 files changed, 456 insertions(+), 5 deletions(-) create mode 100644 kernel-laws/src/main/scala/cats/kernel/laws/BaseLaws.scala create mode 100644 kernel-laws/src/main/scala/cats/kernel/laws/CheckSupport.scala create mode 100644 kernel-laws/src/main/scala/cats/kernel/laws/GroupLaws.scala create mode 100644 kernel-laws/src/main/scala/cats/kernel/laws/IsSerializable.scala create mode 100644 kernel-laws/src/main/scala/cats/kernel/laws/OrderLaws.scala create mode 100644 kernel-laws/src/main/scala/cats/kernel/laws/Rules.scala create mode 100644 kernel-laws/src/main/scala/cats/kernel/laws/package.scala diff --git a/build.sbt b/build.sbt index 177acefb6b..76c13a1792 100644 --- a/build.sbt +++ b/build.sbt @@ -91,8 +91,11 @@ lazy val scalacheckVersion = "1.12.5" lazy val disciplineDependencies = Seq( libraryDependencies += "org.scalacheck" %%% "scalacheck" % scalacheckVersion, - libraryDependencies += "org.typelevel" %%% "discipline" % "0.4" -) + libraryDependencies += "org.typelevel" %%% "discipline" % "0.4") + +lazy val testingDependencies = Seq( + libraryDependencies += "org.typelevel" %%% "catalysts-platform" % "0.0.2", + libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.0-M7" % "test") /** * Remove 2.10 projects from doc generation, as the macros used in the projects @@ -190,8 +193,11 @@ lazy val kernelLaws = crossProject.crossType(CrossType.Pure) .settings(buildSettings: _*) .settings(publishSettings: _*) .settings(scoverageSettings: _*) + .settings(disciplineDependencies: _*) + .settings(testingDependencies: _*) .jsSettings(commonJsSettings:_*) .jvmSettings(commonJvmSettings:_*) + .dependsOn(kernel) lazy val kernelLawsJVM = kernelLaws.jvm lazy val kernelLawsJS = kernelLaws.js @@ -228,9 +234,7 @@ lazy val tests = crossProject.crossType(CrossType.Pure) .settings(catsSettings:_*) .settings(disciplineDependencies:_*) .settings(noPublishSettings:_*) - .settings(libraryDependencies ++= Seq( - "org.scalatest" %%% "scalatest" % "3.0.0-M7" % "test", - "org.typelevel" %%% "catalysts-platform" % "0.0.2" % "test")) + .settings(testingDependencies: _*) .jsSettings(commonJsSettings:_*) .jvmSettings(commonJvmSettings:_*) diff --git a/kernel-laws/src/main/scala/cats/kernel/laws/BaseLaws.scala b/kernel-laws/src/main/scala/cats/kernel/laws/BaseLaws.scala new file mode 100644 index 0000000000..54bda4b136 --- /dev/null +++ b/kernel-laws/src/main/scala/cats/kernel/laws/BaseLaws.scala @@ -0,0 +1,27 @@ +package cats.kernel.laws + +import cats.kernel._ + +import org.typelevel.discipline.Laws + +import org.scalacheck.{Arbitrary, Prop} + +object BaseLaws { + def apply[A : Eq : Arbitrary] = new BaseLaws[A] { + def Equ = Eq[A] + def Arb = implicitly[Arbitrary[A]] + } +} + +trait BaseLaws[A] extends Laws { + + implicit def Equ: Eq[A] + implicit def Arb: Arbitrary[A] + + class BaseRuleSet( + val name: String, + val parent: Option[RuleSet], + val bases: Seq[(String, Laws#RuleSet)], + val props: (String, Prop)* + ) extends RuleSet with HasOneParent +} diff --git a/kernel-laws/src/main/scala/cats/kernel/laws/CheckSupport.scala b/kernel-laws/src/main/scala/cats/kernel/laws/CheckSupport.scala new file mode 100644 index 0000000000..ad80b28a19 --- /dev/null +++ b/kernel-laws/src/main/scala/cats/kernel/laws/CheckSupport.scala @@ -0,0 +1,12 @@ +// package cats.kernel.laws +// +// /** +// * This object contains Arbitrary instances for types defined in +// * cats.kernel.std, as well as anything else we'd like to import to assist +// * in running ScalaCheck tests. +// * +// * (Since cats.kernel-std has no dependencies, its types can't define +// * Arbitrary instances in companions.) +// */ +// object CheckSupport { +// } diff --git a/kernel-laws/src/main/scala/cats/kernel/laws/GroupLaws.scala b/kernel-laws/src/main/scala/cats/kernel/laws/GroupLaws.scala new file mode 100644 index 0000000000..f12ed7d2e8 --- /dev/null +++ b/kernel-laws/src/main/scala/cats/kernel/laws/GroupLaws.scala @@ -0,0 +1,142 @@ +package cats.kernel.laws + +import cats.kernel._ + +import org.typelevel.discipline.Laws + +import org.scalacheck.{Arbitrary, Prop} +import org.scalacheck.Prop._ + +object GroupLaws { + def apply[A : Eq : Arbitrary] = new GroupLaws[A] { + def Equ = Eq[A] + def Arb = implicitly[Arbitrary[A]] + } +} + +trait GroupLaws[A] extends Laws { + + implicit def Equ: Eq[A] + implicit def Arb: Arbitrary[A] + + // groups + + def semigroup(implicit A: Semigroup[A]) = new GroupProperties( + name = "semigroup", + parents = Nil, + Rules.serializable(A), + Rules.associativity(A.combine), + Rules.repeat1("combineN")(A.combineN), + Rules.repeat2("combineN", "|+|")(A.combineN)(A.combine) + ) + + def band(implicit A: Band[A]) = new GroupProperties( + name = "band", + parents = List(semigroup), + Rules.idempotence(A.combine), + "isIdempotent" -> Semigroup.isIdempotent[A] + ) + + def commutativeSemigroup(implicit A: CommutativeSemigroup[A]) = new GroupProperties( + name = "commutative semigroup", + parents = List(semigroup), + Rules.commutative(A.combine) + ) + + def semilattice(implicit A: Semilattice[A]) = new GroupProperties( + name = "semilattice", + parents = List(band, commutativeSemigroup) + ) + + def monoid(implicit A: Monoid[A]) = new GroupProperties( + name = "monoid", + parents = List(semigroup), + Rules.leftIdentity(A.empty)(A.combine), + Rules.rightIdentity(A.empty)(A.combine), + Rules.repeat0("combineN", "id", A.empty)(A.combineN), + Rules.collect0("combineAll", "id", A.empty)(A.combineAll), + Rules.isId("isEmpty", A.empty)(A.isEmpty) + ) + + def commutativeMonoid(implicit A: CommutativeMonoid[A]) = new GroupProperties( + name = "commutative monoid", + parents = List(monoid, commutativeSemigroup) + ) + + def boundedSemilattice(implicit A: BoundedSemilattice[A]) = new GroupProperties( + name = "boundedSemilattice", + parents = List(commutativeMonoid, semilattice) + ) + + def group(implicit A: Group[A]) = new GroupProperties( + name = "group", + parents = List(monoid), + Rules.leftInverse(A.empty)(A.combine)(A.inverse), + Rules.rightInverse(A.empty)(A.combine)(A.inverse), + Rules.consistentInverse("remove")(A.remove)(A.combine)(A.inverse) + ) + + def commutativeGroup(implicit A: CommutativeGroup[A]) = new GroupProperties( + name = "commutative group", + parents = List(group, commutativeMonoid) + ) + + // // additive groups + // + // def additiveSemigroup(implicit A: AdditiveSemigroup[A]) = new AdditiveProperties( + // base = semigroup(A.additive), + // parents = Nil, + // Rules.serializable(A), + // Rules.repeat1("sumN")(A.sumN), + // Rules.repeat2("sumN", "+")(A.sumN)(A.plus) + // ) + // + // def additiveCommutativeSemigroup(implicit A: AdditiveCommutativeSemigroup[A]) = new AdditiveProperties( + // base = commutativeSemigroup(A.additive), + // parents = List(additiveSemigroup) + // ) + // + // def additiveMonoid(implicit A: AdditiveMonoid[A]) = new AdditiveProperties( + // base = monoid(A.additive), + // parents = List(additiveSemigroup), + // Rules.repeat0("sumN", "zero", A.zero)(A.sumN), + // Rules.collect0("sum", "zero", A.zero)(A.sum) + // ) + // + // def additiveCommutativeMonoid(implicit A: AdditiveCommutativeMonoid[A]) = new AdditiveProperties( + // base = commutativeMonoid(A.additive), + // parents = List(additiveMonoid) + // ) + // + // def additiveGroup(implicit A: AdditiveGroup[A]) = new AdditiveProperties( + // base = group(A.additive), + // parents = List(additiveMonoid), + // Rules.consistentInverse("subtract")(A.minus)(A.plus)(A.negate) + // ) + // + // def additiveCommutativeGroup(implicit A: AdditiveCommutativeGroup[A]) = new AdditiveProperties( + // base = commutativeGroup(A.additive), + // parents = List(additiveGroup) + // ) + + + // property classes + + class GroupProperties( + val name: String, + val parents: Seq[GroupProperties], + val props: (String, Prop)* + ) extends RuleSet { + val bases = Nil + } + + // class AdditiveProperties( + // val base: GroupProperties, + // val parents: Seq[AdditiveProperties], + // val props: (String, Prop)* + // ) extends RuleSet { + // val name = base.name + // val bases = List("base" -> base) + // } + +} diff --git a/kernel-laws/src/main/scala/cats/kernel/laws/IsSerializable.scala b/kernel-laws/src/main/scala/cats/kernel/laws/IsSerializable.scala new file mode 100644 index 0000000000..ac64a97407 --- /dev/null +++ b/kernel-laws/src/main/scala/cats/kernel/laws/IsSerializable.scala @@ -0,0 +1,14 @@ +package cats.kernel.laws + +import catalysts.Platform + +import scala.util.DynamicVariable + +/** + * Object with a dynamic variable that allows users to skip the + * serialization tests for certain instances. + */ +private[laws] object IsSerializable { + val runTests = new DynamicVariable[Boolean](true) + def apply(): Boolean = (!Platform.isJs) && runTests.value +} diff --git a/kernel-laws/src/main/scala/cats/kernel/laws/OrderLaws.scala b/kernel-laws/src/main/scala/cats/kernel/laws/OrderLaws.scala new file mode 100644 index 0000000000..fcb2bde554 --- /dev/null +++ b/kernel-laws/src/main/scala/cats/kernel/laws/OrderLaws.scala @@ -0,0 +1,79 @@ +package cats.kernel +package laws + +import org.typelevel.discipline.Laws + +import org.scalacheck.{Arbitrary, Prop} +import org.scalacheck.Prop._ + +import cats.kernel.std.boolean._ + +object OrderLaws { + def apply[A: Eq: Arbitrary] = new OrderLaws[A] { + def Equ = Eq[A] + def Arb = implicitly[Arbitrary[A]] + } +} + +trait OrderLaws[A] extends Laws { + + implicit def Equ: Eq[A] + implicit def Arb: Arbitrary[A] + + def eqv = new OrderProperties( + name = "eq", + parent = None, + Rules.serializable(Equ), + "reflexitivity-eq" -> forAll { (x: A) => + x ?== x + }, + "symmetry-eq" -> forAll { (x: A, y: A) => + Equ.eqv(x, y) ?== Equ.eqv(y, x) + }, + "antisymmetry-eq" -> forAll { (x: A, y: A, f: A => A) => + !Equ.eqv(x, y) ?|| Equ.eqv(f(x), f(y)) + }, + "transitivity-eq" -> forAll { (x: A, y: A, z: A) => + !(Equ.eqv(x, y) && Equ.eqv(y, z)) ?|| Equ.eqv(x, z) + } + ) + + def partialOrder(implicit A: PartialOrder[A]) = new OrderProperties( + name = "partialOrder", + parent = Some(eqv), + Rules.serializable(A), + "reflexitivity" -> forAll { (x: A) => + x ?<= x + }, + "antisymmetry" -> forAll { (x: A, y: A) => + !(A.lteqv(x, y) && A.lteqv(y, x)) ?|| A.eqv(x, y) + }, + "transitivity" -> forAll { (x: A, y: A, z: A) => + !(A.lteqv(x, y) && A.lteqv(y, z)) ?|| A.lteqv(x, z) + }, + "gteqv" -> forAll { (x: A, y: A) => + A.lteqv(x, y) ?== A.gteqv(y, x) + }, + "lt" -> forAll { (x: A, y: A) => + A.lt(x, y) ?== (A.lteqv(x, y) && A.neqv(x, y)) + }, + "gt" -> forAll { (x: A, y: A) => + A.lt(x, y) ?== A.gt(y, x) + } + ) + + def order(implicit A: Order[A]) = new OrderProperties( + name = "order", + parent = Some(partialOrder), + "totality" -> forAll { (x: A, y: A) => + A.lteqv(x, y) ?|| A.lteqv(y, x) + } + ) + + class OrderProperties( + name: String, + parent: Option[RuleSet], + props: (String, Prop)* + ) extends DefaultRuleSet(name, parent, props: _*) + +} diff --git a/kernel-laws/src/main/scala/cats/kernel/laws/Rules.scala b/kernel-laws/src/main/scala/cats/kernel/laws/Rules.scala new file mode 100644 index 0000000000..156a39a774 --- /dev/null +++ b/kernel-laws/src/main/scala/cats/kernel/laws/Rules.scala @@ -0,0 +1,128 @@ +package cats.kernel +package laws + +import org.scalacheck.Prop +import org.scalacheck.Prop._ +import scala.util.control.NonFatal + +import org.scalacheck.{Arbitrary, Prop} + +import cats.kernel.std.boolean._ + +object Rules { + + // Comparison operators for testing are supplied by CheckEqOps and + // CheckOrderOps in package.scala. They are: + // + // ?== Ensure that x equals y + // ?!= Ensure that x does not equal y + // ?< Ensure that x < y + // ?<= Ensure that x <= y + // ?> Ensure that x > y + // ?>= Ensure that x >= y + // + // The reason to prefer these operators is that when tests fail, we + // will get more detaild output about what the failing values were + // (in addition to the input values generated by ScalaCheck). + + def associativity[A: Arbitrary: Eq](f: (A, A) => A): (String, Prop) = + "associativity" -> forAll { (x: A, y: A, z: A) => + f(f(x, y), z) ?== f(x, f(y, z)) + } + + def leftIdentity[A: Arbitrary: Eq](id: A)(f: (A, A) => A): (String, Prop) = + "leftIdentity" -> forAll { (x: A) => + f(id, x) ?== x + } + + def rightIdentity[A: Arbitrary: Eq](id: A)(f: (A, A) => A): (String, Prop) = + "rightIdentity" -> forAll { (x: A) => + f(x, id) ?== x + } + + def leftInverse[A: Arbitrary: Eq](id: A)(f: (A, A) => A)(inv: A => A): (String, Prop) = + "left inverse" -> forAll { (x: A) => + id ?== f(inv(x), x) + } + + def rightInverse[A: Arbitrary: Eq](id: A)(f: (A, A) => A)(inv: A => A): (String, Prop) = + "right inverse" -> forAll { (x: A) => + id ?== f(x, inv(x)) + } + + def commutative[A: Arbitrary: Eq](f: (A, A) => A): (String, Prop) = + "commutative" -> forAll { (x: A, y: A) => + f(x, y) ?== f(y, x) + } + + def idempotence[A: Arbitrary: Eq](f: (A, A) => A): (String, Prop) = + "idempotence" -> forAll { (x: A) => + f(x, x) ?== x + } + + def consistentInverse[A: Arbitrary: Eq](name: String)(m: (A, A) => A)(f: (A, A) => A)(inv: A => A): (String, Prop) = + s"consistent $name" -> forAll { (x: A, y: A) => + m(x, y) ?== f(x, inv(y)) + } + + def repeat0[A: Arbitrary: Eq](name: String, sym: String, id: A)(r: (A, Int) => A): (String, Prop) = + s"$name(a, 0) == $sym" -> forAll { (a: A) => + r(a, 0) ?== id + } + + def repeat1[A: Arbitrary: Eq](name: String)(r: (A, Int) => A): (String, Prop) = + s"$name(a, 1) == a" -> forAll { (a: A) => + r(a, 1) ?== a + } + + def repeat2[A: Arbitrary: Eq](name: String, sym: String)(r: (A, Int) => A)(f: (A, A) => A): (String, Prop) = + s"$name(a, 2) == a $sym a" -> forAll { (a: A) => + r(a, 2) ?== f(a, a) + } + + def collect0[A: Arbitrary: Eq](name: String, sym: String, id: A)(c: Seq[A] => A): (String, Prop) = + s"$name(Nil) == $sym" -> forAll { (a: A) => + c(Nil) ?== id + } + + def isId[A: Arbitrary: Eq](name: String, id: A)(p: A => Boolean): (String, Prop) = + name -> forAll { (x: A) => + Eq.eqv(x, id) ?== p(x) + } + + def distributive[A: Arbitrary: Eq](a: (A, A) => A)(m: (A, A) => A): (String, Prop) = + "distributive" → forAll { (x: A, y: A, z: A) => + (m(x, a(y, z)) ?== a(m(x, y), m(x, z))) && + (m(a(x, y), z) ?== a(m(x, z), m(y, z))) + } + + // ugly platform-specific code follows + + def serializable[M](m: M): (String, Prop) = + "serializable" -> (IsSerializable() match { + case false => + Prop(_ => Result(status = Proof)) + case true => + Prop { _ => + import java.io._ + val baos = new ByteArrayOutputStream() + val oos = new ObjectOutputStream(baos) + var ois: ObjectInputStream = null + try { + oos.writeObject(m) + oos.close() + val bais = new ByteArrayInputStream(baos.toByteArray()) + ois = new ObjectInputStream(bais) + val m2 = ois.readObject() // just ensure we can read it back + ois.close() + Result(status = Proof) + } catch { case NonFatal(t) => + Result(status = Exception(t)) + } finally { + oos.close() + if (ois != null) ois.close() + } + } + }) + +} diff --git a/kernel-laws/src/main/scala/cats/kernel/laws/package.scala b/kernel-laws/src/main/scala/cats/kernel/laws/package.scala new file mode 100644 index 0000000000..4e576499df --- /dev/null +++ b/kernel-laws/src/main/scala/cats/kernel/laws/package.scala @@ -0,0 +1,45 @@ +package cats.kernel + +import org.typelevel.discipline.Predicate + +import org.scalacheck._ +import org.scalacheck.util.Pretty +import Prop.{False, Proof, Result} + +package object laws { + + implicit def PredicateFromMonoid[A](implicit ev: Eq[A], A: Monoid[A]): Predicate[A] = + new Predicate[A] { + def apply(a: A) = ev.neqv(a, A.empty) + } + + lazy val proved = Prop(Result(status = Proof)) + + lazy val falsified = Prop(Result(status = False)) + + object Ops { + def run[A](sym: String)(lhs: A, rhs: A)(f: (A, A) => Boolean): Prop = + if (f(lhs, rhs)) proved else falsified :| { + val exp = Pretty.pretty(lhs, Pretty.Params(0)) + val got = Pretty.pretty(rhs, Pretty.Params(0)) + s"($exp $sym $got) failed" + } + } + + implicit class CheckEqOps[A](lhs: A)(implicit ev: Eq[A], pp: A => Pretty) { + def ?==(rhs: A): Prop = Ops.run("?==")(lhs, rhs)(ev.eqv) + def ?!=(rhs: A): Prop = Ops.run("?!=")(lhs, rhs)(ev.neqv) + } + + implicit class CheckOrderOps[A](lhs: A)(implicit ev: PartialOrder[A], pp: A => Pretty) { + def ?<(rhs: A): Prop = Ops.run("?<")(lhs, rhs)(ev.lt) + def ?<=(rhs: A): Prop = Ops.run("?<=")(lhs, rhs)(ev.lteqv) + def ?>(rhs: A): Prop = Ops.run("?>")(lhs, rhs)(ev.gt) + def ?>=(rhs: A): Prop = Ops.run("?>=")(lhs, rhs)(ev.gteqv) + } + + implicit class BooleanOps[A](lhs: Boolean)(implicit pp: Boolean => Pretty) { + def ?&&(rhs: Boolean): Prop = Ops.run("?&&")(lhs, rhs)(_ && _) + def ?||(rhs: Boolean): Prop = Ops.run("?||")(lhs, rhs)(_ || _) + } +} From ceb94cc088138c0c7e927a34ab4cb1a425960fbd Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 11:56:12 -0400 Subject: [PATCH 078/185] Get law tests compiling but not running. This commit ports over the law tests which make sense in the context of cats-kernel. For some reason the tests aren't being run right now, but at least they compile. --- build.sbt | 1 + .../scala/cats/kernel/laws/LawTests.scala | 155 ++++++++++++++++++ .../src/main/scala/cats/kernel/package.scala | 3 - 3 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala delete mode 100644 kernel/src/main/scala/cats/kernel/package.scala diff --git a/build.sbt b/build.sbt index 76c13a1792..51e176f642 100644 --- a/build.sbt +++ b/build.sbt @@ -95,6 +95,7 @@ lazy val disciplineDependencies = Seq( lazy val testingDependencies = Seq( libraryDependencies += "org.typelevel" %%% "catalysts-platform" % "0.0.2", + libraryDependencies += "org.typelevel" %%% "catalysts-macros" % "0.0.2" % "test", libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.0-M7" % "test") /** diff --git a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala new file mode 100644 index 0000000000..2380a4e09f --- /dev/null +++ b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala @@ -0,0 +1,155 @@ +package cats.kernel +package laws + +import catalysts.Platform +import catalysts.macros.TypeTagM + +import cats.kernel.std.all._ + +import org.typelevel.discipline.{ Laws } +import org.typelevel.discipline.scalatest.Discipline +import org.scalacheck.{ Arbitrary } +import Arbitrary.arbitrary +import org.scalatest.FunSuite +import scala.util.Random + +trait LawTests extends FunSuite with Discipline { + + implicit def orderLaws[A: Eq: Arbitrary] = OrderLaws[A] + implicit def groupLaws[A: Eq: Arbitrary] = GroupLaws[A] + + laws[OrderLaws, List[HasEq[Int]]].check(_.eqv) + laws[OrderLaws, List[HasEq[String]]].check(_.eqv) + laws[OrderLaws, Option[HasEq[Int]]].check(_.eqv) + laws[OrderLaws, Option[HasEq[String]]].check(_.eqv) + laws[OrderLaws, Map[Char, Int]].check(_.eqv) + laws[OrderLaws, Map[Int, BigInt]].check(_.eqv) + + laws[OrderLaws, Option[HasPartialOrder[Int]]].check(_.partialOrder) + laws[OrderLaws, Option[HasPartialOrder[String]]].check(_.partialOrder) + laws[OrderLaws, List[HasPartialOrder[Int]]].check(_.partialOrder) + laws[OrderLaws, List[HasPartialOrder[String]]].check(_.partialOrder) + laws[OrderLaws, Set[Int]].check(_.partialOrder) + laws[OrderLaws, Array[Int]].check(_.partialOrder) + + laws[OrderLaws, Unit].check(_.order) + laws[OrderLaws, Boolean].check(_.order) + laws[OrderLaws, String].check(_.order) + laws[OrderLaws, Byte].check(_.order) + laws[OrderLaws, Short].check(_.order) + laws[OrderLaws, Char].check(_.order) + laws[OrderLaws, Int].check(_.order) + laws[OrderLaws, Long].check(_.order) + laws[OrderLaws, List[Int]].check(_.order) + laws[OrderLaws, Option[String]].check(_.order) + laws[OrderLaws, List[String]].check(_.order) + laws[OrderLaws, Array[Int]].check(_.order) + laws[OrderLaws, Int]("fromOrdering").check(_.order(Order.fromOrdering[Int])) + + laws[GroupLaws, Int].check(_.monoid) + laws[GroupLaws, String].check(_.monoid) + laws[GroupLaws, Option[Int]].check(_.monoid) + laws[GroupLaws, Option[String]].check(_.monoid) + laws[GroupLaws, List[Int]].check(_.monoid) + laws[GroupLaws, List[String]].check(_.monoid) + + laws[GroupLaws, (Int, Int)].check(_.band) + + implicit val intGroup: Group[Int] = + new Group[Int] { + def empty: Int = 0 + def combine(x: Int, y: Int): Int = x + y + def inverse(x: Int): Int = -x + } + + implicit val band: Band[(Int, Int)] = + new Band[(Int, Int)] { + def combine(a: (Int, Int), b: (Int, Int)) = (a._1, b._2) + } + + { + // In order to check the monoid laws for `Order[N]`, we need + // `Arbitrary[Order[N]]` and `Eq[Order[N]]` instances. + // Here we have a bit of a hack to create these instances. + val nMax: Int = 13 + final case class N(n: Int) { require(n >= 0 && n < nMax) } + // The arbitrary `Order[N]` values are created by mapping N values to random + // integers. + implicit val arbNOrder: Arbitrary[Order[N]] = Arbitrary(arbitrary[Int].map { seed => + val order = new Random(seed).shuffle(Vector.range(0, nMax)) + Order.by { (n: N) => order(n.n) } + }) + // The arbitrary `Eq[N]` values are created by mapping N values to random + // integers. + implicit val arbNEq: Arbitrary[Eq[N]] = Arbitrary(arbitrary[Int].map { seed => + val mapping = new Random(seed).shuffle(Vector.range(0, nMax)) + Eq.by { (n: N) => mapping(n.n) } + }) + // needed because currently we don't have Vector instances + implicit val vectorNEq: Eq[Vector[N]] = Eq.fromUniversalEquals + // The `Eq[Order[N]]` instance enumerates all possible `N` values in a + // `Vector` and considers two `Order[N]` instances to be equal if they + // result in the same sorting of that vector. + implicit val NOrderEq: Eq[Order[N]] = Eq.by { order: Order[N] => + Vector.tabulate(nMax)(N).sorted(order.toOrdering) + } + implicit val NEqEq: Eq[Eq[N]] = new Eq[Eq[N]] { + def eqv(a: Eq[N], b: Eq[N]) = + Iterator.tabulate(nMax)(N) + .flatMap { x => Iterator.tabulate(nMax)(N).map((x, _)) } + .forall { case (x, y) => a.eqv(x, y) == b.eqv(x, y) } + } + + implicit val monoidOrderN: Monoid[Order[N]] = Order.whenEqualMonoid[N] + laws[GroupLaws, Order[N]].check(_.monoid) + + { + implicit val bsEqN: BoundedSemilattice[Eq[N]] = Eq.allEqualBoundedSemilattice[N] + laws[GroupLaws, Eq[N]].check(_.boundedSemilattice) + } + { + implicit val sEqN: Semilattice[Eq[N]] = Eq.anyEqualSemilattice[N] + laws[GroupLaws, Eq[N]].check(_.semilattice) + } + } + + + // esoteric machinery follows... + + + // The scalacheck defaults (100,100) are too high for scala-js. + val PropMaxSize = if (Platform.isJs) 10 else 100 + val PropMinSuccessful = if (Platform.isJs) 10 else 100 + + implicit override val generatorDrivenConfig: PropertyCheckConfiguration = + PropertyCheckConfig(maxSize = PropMaxSize, minSuccessful = PropMinSuccessful) + + case class HasEq[A](a: A) + + object HasEq { + implicit def hasEq[A: Eq]: Eq[HasEq[A]] = + Eq[A].on(_.a) + implicit def hasEqArbitrary[A: Arbitrary]: Arbitrary[HasEq[A]] = + Arbitrary(arbitrary[A].map(HasEq(_))) + } + + case class HasPartialOrder[A](a: A) + + object HasPartialOrder { + implicit def hasPartialOrder[A: PartialOrder]: PartialOrder[HasPartialOrder[A]] = + PartialOrder[A].on(_.a) + implicit def hasPartialOrderArbitrary[A: Arbitrary]: Arbitrary[HasPartialOrder[A]] = + Arbitrary(arbitrary[A].map(HasPartialOrder(_))) + } + + case class LawChecker[L <: Laws](name: String, laws: L) { + def check(f: L => L#RuleSet): Unit = checkAll(name, f(laws)) + } + + private[laws] def laws[L[_] <: Laws, A](implicit lws: L[A], tag: TypeTagM[A]): LawChecker[L[A]] = + laws[L, A]("") + + private[laws] def laws[L[_] <: Laws, A](extraTag: String)(implicit laws: L[A], tag: TypeTagM[A]): LawChecker[L[A]] = + LawChecker("[" + tag.name.toString + (if(extraTag != "") "@@" + extraTag else "") + "]", laws) + +} diff --git a/kernel/src/main/scala/cats/kernel/package.scala b/kernel/src/main/scala/cats/kernel/package.scala deleted file mode 100644 index 80fd3067b8..0000000000 --- a/kernel/src/main/scala/cats/kernel/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package cats -package object kernel { -} From 673d0454bf4fbbba369dbf9b6dbdbc95a1195755 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 12:03:50 -0400 Subject: [PATCH 079/185] Get tests running and passing. --- .../scala/cats/kernel/laws/LawTests.scala | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala index 2380a4e09f..9064c9b1ef 100644 --- a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala +++ b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala @@ -13,7 +13,14 @@ import Arbitrary.arbitrary import org.scalatest.FunSuite import scala.util.Random -trait LawTests extends FunSuite with Discipline { +class LawTests extends FunSuite with Discipline { + + // The scalacheck defaults (100,100) are too high for scala-js. + final val PropMaxSize = if (Platform.isJs) 10 else 100 + final val PropMinSuccessful = if (Platform.isJs) 10 else 100 + + implicit override val generatorDrivenConfig: PropertyCheckConfiguration = + PropertyCheckConfig(maxSize = PropMaxSize, minSuccessful = PropMinSuccessful) implicit def orderLaws[A: Eq: Arbitrary] = OrderLaws[A] implicit def groupLaws[A: Eq: Arbitrary] = GroupLaws[A] @@ -55,14 +62,16 @@ trait LawTests extends FunSuite with Discipline { laws[GroupLaws, (Int, Int)].check(_.band) - implicit val intGroup: Group[Int] = + // esoteric machinery follows... + + implicit lazy val intGroup: Group[Int] = new Group[Int] { def empty: Int = 0 def combine(x: Int, y: Int): Int = x + y def inverse(x: Int): Int = -x } - implicit val band: Band[(Int, Int)] = + implicit lazy val band: Band[(Int, Int)] = new Band[(Int, Int)] { def combine(a: (Int, Int), b: (Int, Int)) = (a._1, b._2) } @@ -113,17 +122,6 @@ trait LawTests extends FunSuite with Discipline { } } - - // esoteric machinery follows... - - - // The scalacheck defaults (100,100) are too high for scala-js. - val PropMaxSize = if (Platform.isJs) 10 else 100 - val PropMinSuccessful = if (Platform.isJs) 10 else 100 - - implicit override val generatorDrivenConfig: PropertyCheckConfiguration = - PropertyCheckConfig(maxSize = PropMaxSize, minSuccessful = PropMinSuccessful) - case class HasEq[A](a: A) object HasEq { From d19a098e9a1c4626bd015859967f0bad4a24ea74 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 12:13:18 -0400 Subject: [PATCH 080/185] Fix tests in JS. --- .../cats/kernel/laws/IsSerializable.scala | 26 ++++++++++++++- .../main/scala/cats/kernel/laws/Rules.scala | 32 +++---------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/kernel-laws/src/main/scala/cats/kernel/laws/IsSerializable.scala b/kernel-laws/src/main/scala/cats/kernel/laws/IsSerializable.scala index ac64a97407..2ed2e92739 100644 --- a/kernel-laws/src/main/scala/cats/kernel/laws/IsSerializable.scala +++ b/kernel-laws/src/main/scala/cats/kernel/laws/IsSerializable.scala @@ -1,7 +1,9 @@ package cats.kernel.laws import catalysts.Platform - +import org.scalacheck.Prop +import org.scalacheck.Prop._ +import scala.util.control.NonFatal import scala.util.DynamicVariable /** @@ -11,4 +13,26 @@ import scala.util.DynamicVariable private[laws] object IsSerializable { val runTests = new DynamicVariable[Boolean](true) def apply(): Boolean = (!Platform.isJs) && runTests.value + + def testSerialization[M](m: M): Prop.Result = + if (Platform.isJs) Result(status = Proof) else { + import java.io._ + val baos = new ByteArrayOutputStream() + val oos = new ObjectOutputStream(baos) + var ois: ObjectInputStream = null + try { + oos.writeObject(m) + oos.close() + val bais = new ByteArrayInputStream(baos.toByteArray()) + ois = new ObjectInputStream(bais) + val m2 = ois.readObject() // just ensure we can read it back + ois.close() + Result(status = Proof) + } catch { case NonFatal(t) => + Result(status = Exception(t)) + } finally { + oos.close() + if (ois != null) ois.close() + } + } } diff --git a/kernel-laws/src/main/scala/cats/kernel/laws/Rules.scala b/kernel-laws/src/main/scala/cats/kernel/laws/Rules.scala index 156a39a774..abeb08d0a5 100644 --- a/kernel-laws/src/main/scala/cats/kernel/laws/Rules.scala +++ b/kernel-laws/src/main/scala/cats/kernel/laws/Rules.scala @@ -3,10 +3,7 @@ package laws import org.scalacheck.Prop import org.scalacheck.Prop._ -import scala.util.control.NonFatal - import org.scalacheck.{Arbitrary, Prop} - import cats.kernel.std.boolean._ object Rules { @@ -99,30 +96,9 @@ object Rules { // ugly platform-specific code follows def serializable[M](m: M): (String, Prop) = - "serializable" -> (IsSerializable() match { - case false => - Prop(_ => Result(status = Proof)) - case true => - Prop { _ => - import java.io._ - val baos = new ByteArrayOutputStream() - val oos = new ObjectOutputStream(baos) - var ois: ObjectInputStream = null - try { - oos.writeObject(m) - oos.close() - val bais = new ByteArrayInputStream(baos.toByteArray()) - ois = new ObjectInputStream(bais) - val m2 = ois.readObject() // just ensure we can read it back - ois.close() - Result(status = Proof) - } catch { case NonFatal(t) => - Result(status = Exception(t)) - } finally { - oos.close() - if (ois != null) ois.close() - } - } + "serializable" -> (if (IsSerializable()) { + Prop(_ => Result(status = Proof)) + } else { + Prop(_ => IsSerializable.testSerialization(m)) }) - } From 3378af34e2a31feb461c3a8f0fca54ad72044004 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 13:15:21 -0400 Subject: [PATCH 081/185] Add missing instances to cats.kernel.std. --- .../src/main/scala/cats/kernel/std/Util.scala | 184 +++++------------- .../src/main/scala/cats/kernel/std/set.scala | 14 +- .../main/scala/cats/kernel/std/stream.scala | 55 ++++++ .../main/scala/cats/kernel/std/vector.scala | 55 ++++++ 4 files changed, 175 insertions(+), 133 deletions(-) create mode 100644 kernel/src/main/scala/cats/kernel/std/stream.scala create mode 100644 kernel/src/main/scala/cats/kernel/std/vector.scala diff --git a/kernel/src/main/scala/cats/kernel/std/Util.scala b/kernel/src/main/scala/cats/kernel/std/Util.scala index 93655d09e0..5b0859e0da 100644 --- a/kernel/src/main/scala/cats/kernel/std/Util.scala +++ b/kernel/src/main/scala/cats/kernel/std/Util.scala @@ -1,139 +1,10 @@ -package cats.kernel.std.util +package cats.kernel +package std.util -import java.lang.Double.{ longBitsToDouble, doubleToLongBits } -import java.lang.Float.{ intBitsToFloat, floatToIntBits } -import java.lang.Long.{ numberOfTrailingZeros, numberOfLeadingZeros } -import java.lang.Math -import scala.annotation.tailrec import scala.collection.mutable object StaticMethods { - /** - * Implementation of the binary GCD algorithm. - */ - final def gcd(x0: Long, y0: Long): Long = { - // if either argument is 0, just return the other. - if (x0 == 0L) return y0 - if (y0 == 0L) return x0 - - val xz = numberOfTrailingZeros(x0) - val yz = numberOfTrailingZeros(y0) - - // Math.abs is safe because Long.MinValue (0x8000000000000000) - // will be shifted over 63 bits (due to trailing zeros). - var x = Math.abs(x0 >> xz) - var y = Math.abs(y0 >> yz) - - while (x != y) { - if (x > y) { - x -= y - x >>= numberOfTrailingZeros(x) - } else { - y -= x - y >>= numberOfTrailingZeros(y) - } - } - - // trailing zeros mean powers of two -- if both numbers had - // trailing zeros, those are part of the common divsor as well. - if (xz < yz) x << xz else x << yz - } - - /** - * GCD for Float values. - */ - final def gcd(a: Float, b: Float): Float = { - import java.lang.Integer.{ numberOfTrailingZeros, numberOfLeadingZeros } - - def value(bits: Int): Int = bits & 0x007FFFFF | 0x00800000 - - def exp(bits: Int): Int = ((bits >> 23) & 0xFF).toInt - - def gcd0(val0: Int, exp0: Int, val1: Int, exp1: Int): Float = { - val tz0 = numberOfTrailingZeros(val0) - val tz1 = numberOfTrailingZeros(val1) - val tzShared = Math.min(tz0, tz1 + exp1 - exp0) - val n = gcd(val0.toLong >>> tz0, val1.toLong >>> tz1).toInt << tzShared - - val shift = numberOfLeadingZeros(n) - 8 // Number of bits to move 1 to bit 23 - val mantissa = (n << shift) & 0x007FFFFF - val exp = (exp0 - shift) - if (exp < 0) 0F else intBitsToFloat((exp << 23) | mantissa) - } - - if (a == 0F) b - else if (b == 0F) a - else { - val aBits = floatToIntBits(a) - val aVal = value(aBits) - val aExp = exp(aBits) - - val bBits = floatToIntBits(b) - val bVal = value(bBits) - val bExp = exp(bBits) - - if (aExp < bExp) gcd0(aVal, aExp, bVal, bExp) - else gcd0(bVal, bExp, aVal, aExp) - } - } - - /** - * GCD for Double values. - */ - final def gcd(a: Double, b: Double): Double = { - def value(bits: Long): Long = bits & 0x000FFFFFFFFFFFFFL | 0x0010000000000000L - - def exp(bits: Long): Int = ((bits >> 52) & 0x7FF).toInt - - def gcd0(val0: Long, exp0: Int, val1: Long, exp1: Int): Double = { - val tz0 = numberOfTrailingZeros(val0) - val tz1 = numberOfTrailingZeros(val1) - val tzShared = Math.min(tz0, tz1 + exp1 - exp0) - val n = gcd(val0 >>> tz0, val1 >>> tz1) << tzShared - - val shift = numberOfLeadingZeros(n) - 11 // Number of bits to move 1 to bit 52 - val mantissa = (n << shift) & 0x000FFFFFFFFFFFFFL - val exp = (exp0 - shift).toLong - if (exp < 0) 0.0 else longBitsToDouble((exp << 52) | mantissa) - } - - if (a == 0D) b - else if (b == 0D) a - else { - val aBits = doubleToLongBits(a) - val aVal = value(aBits) - val aExp = exp(aBits) - - val bBits = doubleToLongBits(b) - val bVal = value(bBits) - val bExp = exp(bBits) - - if (aExp < bExp) gcd0(aVal, aExp, bVal, bExp) - else gcd0(bVal, bExp, aVal, aExp) - } - } - - /** - * Exponentiation function, e.g. x^y - * - * If base^ex doesn't fit in a Long, the result will overflow (unlike - * Math.pow which will return +/- Infinity). - */ - final def pow(base: Long, exponent: Long): Long = { - @tailrec def loop(t: Long, b: Long, e: Long): Long = - if (e == 0L) t - else if ((e & 1) == 1) loop(t * b, b * b, e >>> 1L) - else loop(t, b * b, e >>> 1L) - - if (exponent >= 0L) loop(1L, base, exponent) else { - if(base == 0L) throw new ArithmeticException("zero can't be raised to negative power") - else if (base == 1L) 1L - else if (base == -1L) if ((exponent & 1L) == 0L) -1L else 1L - else 0L - } - } - def initMutableMap[K, V](m: Map[K, V]): mutable.Map[K, V] = { val result = mutable.Map.empty[K, V] m.foreach { case (k, v) => result(k) = v } @@ -178,4 +49,55 @@ object StaticMethods { } wrapMutableMap(m) } + + def iteratorCompare[A](xs: Iterator[A], ys: Iterator[A])(implicit ev: Order[A]): Int = { + while (true) { + if (xs.hasNext) { + if (ys.hasNext) { + val x = xs.next + val y = ys.next + val cmp = ev.compare(x, y) + if (cmp != 0) return cmp + } else { + return 1 + } + } else { + return if (ys.hasNext) -1 else 0 + } + } + 0 + } + + def iteratorPartialCompare[A](xs: Iterator[A], ys: Iterator[A])(implicit ev: PartialOrder[A]): Double = { + while (true) { + if (xs.hasNext) { + if (ys.hasNext) { + val x = xs.next + val y = ys.next + val cmp = ev.partialCompare(x, y) + if (cmp != 0.0) return cmp + } else { + return 1.0 + } + } else { + return if (ys.hasNext) -1.0 else 0.0 + } + } + 0.0 + } + + def iteratorEq[A](xs: Iterator[A], ys: Iterator[A])(implicit ev: Eq[A]): Boolean = { + while (true) { + if (xs.hasNext) { + if (ys.hasNext) { + if (ev.eqv(xs.next, ys.next)) return true + } else { + return false + } + } else { + return !ys.hasNext + } + } + true + } } diff --git a/kernel/src/main/scala/cats/kernel/std/set.scala b/kernel/src/main/scala/cats/kernel/std/set.scala index ab5e178d3c..844fab2fe5 100644 --- a/kernel/src/main/scala/cats/kernel/std/set.scala +++ b/kernel/src/main/scala/cats/kernel/std/set.scala @@ -4,7 +4,11 @@ package std package object set extends SetInstances trait SetInstances { - implicit def setPartialOrder[A]: PartialOrder[Set[A]] = new SetPartialOrder[A] + implicit def setPartialOrder[A]: PartialOrder[Set[A]] = + new SetPartialOrder[A] + + implicit def setMonoid[A]: Monoid[Set[A]] = + new SetMonoid[A] } class SetPartialOrder[A] extends PartialOrder[Set[A]] { @@ -14,5 +18,11 @@ class SetPartialOrder[A] extends PartialOrder[Set[A]] { else if (x == y) 0.0 else Double.NaN - override def eqv(x: Set[A], y: Set[A]): Boolean = x == y + override def eqv(x: Set[A], y: Set[A]): Boolean = + x == y +} + +class SetMonoid[A] extends Monoid[Set[A]] { + def empty: Set[A] = Set.empty + def combine(x: Set[A], y: Set[A]): Set[A] = x | y } diff --git a/kernel/src/main/scala/cats/kernel/std/stream.scala b/kernel/src/main/scala/cats/kernel/std/stream.scala new file mode 100644 index 0000000000..899cc58d72 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/stream.scala @@ -0,0 +1,55 @@ +package cats.kernel +package std + +import cats.kernel.std.util.StaticMethods + +package object stream extends StreamInstances + +trait StreamInstances extends StreamInstances1 { + implicit def streamOrder[A: Order] = new StreamOrder[A] + implicit def streamMonoid[A] = new StreamMonoid[A] +} + +trait StreamInstances1 extends StreamInstances2 { + implicit def streamPartialOrder[A: PartialOrder] = new StreamPartialOrder[A] +} + +trait StreamInstances2 { + implicit def streamEq[A: Eq] = new StreamEq[A] +} + +class StreamOrder[A](implicit ev: Order[A]) extends Order[Stream[A]] { + def compare(xs: Stream[A], ys: Stream[A]): Int = + StaticMethods.iteratorCompare(xs.iterator, ys.iterator) +} + +class StreamPartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[Stream[A]] { + def partialCompare(xs: Stream[A], ys: Stream[A]): Double = + StaticMethods.iteratorPartialCompare(xs.iterator, ys.iterator) +} + +class StreamEq[A](implicit ev: Eq[A]) extends Eq[Stream[A]] { + def eqv(xs: Stream[A], ys: Stream[A]): Boolean = + StaticMethods.iteratorEq(xs.iterator, ys.iterator) +} + +class StreamMonoid[A] extends Monoid[Stream[A]] { + def empty: Stream[A] = Stream.empty + def combine(x: Stream[A], y: Stream[A]): Stream[A] = x ++ y + + override def combineN(x: Stream[A], n: Int): Stream[A] = { + val buf = Stream.newBuilder[A] + var i = n + while (i > 0) { + buf ++= x + i -= 1 + } + buf.result + } + + override def combineAll(xs: TraversableOnce[Stream[A]]): Stream[A] = { + val buf = Stream.newBuilder[A] + xs.foreach(buf ++= _) + buf.result + } +} diff --git a/kernel/src/main/scala/cats/kernel/std/vector.scala b/kernel/src/main/scala/cats/kernel/std/vector.scala new file mode 100644 index 0000000000..50c13c5174 --- /dev/null +++ b/kernel/src/main/scala/cats/kernel/std/vector.scala @@ -0,0 +1,55 @@ +package cats.kernel +package std + +import cats.kernel.std.util.StaticMethods + +package object vector extends VectorInstances + +trait VectorInstances extends VectorInstances1 { + implicit def vectorOrder[A: Order] = new VectorOrder[A] + implicit def vectorMonoid[A] = new VectorMonoid[A] +} + +trait VectorInstances1 extends VectorInstances2 { + implicit def vectorPartialOrder[A: PartialOrder] = new VectorPartialOrder[A] +} + +trait VectorInstances2 { + implicit def vectorEq[A: Eq] = new VectorEq[A] +} + +class VectorOrder[A](implicit ev: Order[A]) extends Order[Vector[A]] { + def compare(xs: Vector[A], ys: Vector[A]): Int = + StaticMethods.iteratorCompare(xs.iterator, ys.iterator) +} + +class VectorPartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[Vector[A]] { + def partialCompare(xs: Vector[A], ys: Vector[A]): Double = + StaticMethods.iteratorPartialCompare(xs.iterator, ys.iterator) +} + +class VectorEq[A](implicit ev: Eq[A]) extends Eq[Vector[A]] { + def eqv(xs: Vector[A], ys: Vector[A]): Boolean = + StaticMethods.iteratorEq(xs.iterator, ys.iterator) +} + +class VectorMonoid[A] extends Monoid[Vector[A]] { + def empty: Vector[A] = Vector.empty + def combine(x: Vector[A], y: Vector[A]): Vector[A] = x ++ y + + override def combineN(x: Vector[A], n: Int): Vector[A] = { + val buf = Vector.newBuilder[A] + var i = n + while (i > 0) { + buf ++= x + i -= 1 + } + buf.result + } + + override def combineAll(xs: TraversableOnce[Vector[A]]): Vector[A] = { + val buf = Vector.newBuilder[A] + xs.foreach(buf ++= _) + buf.result + } +} From 34e206af4b061730587095399ee2d3f0e8e0ed97 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 13:15:37 -0400 Subject: [PATCH 082/185] Remove algebra. After this commit, the intention is that none of the algebra types are used. The tests pass and things seem to work. Next step is to actually remove the algebra dependency. --- .../src/main/scala/cats/bench/FoldBench.scala | 2 +- build.sbt | 4 +- core/src/main/scala/cats/package.scala | 24 ++--- core/src/main/scala/cats/std/anyval.scala | 89 +++++++------------ core/src/main/scala/cats/std/bigInt.scala | 2 +- core/src/main/scala/cats/std/function.scala | 2 +- core/src/main/scala/cats/std/list.scala | 53 +---------- core/src/main/scala/cats/std/map.scala | 18 +--- core/src/main/scala/cats/std/option.scala | 46 +--------- core/src/main/scala/cats/std/set.scala | 5 +- core/src/main/scala/cats/std/stream.scala | 23 +---- core/src/main/scala/cats/std/string.scala | 2 +- core/src/main/scala/cats/std/vector.scala | 16 +--- .../cats/laws/discipline/CartesianTests.scala | 2 +- .../main/scala/cats/laws/discipline/Eq.scala | 5 +- .../scala/cats/laws/discipline/package.scala | 2 +- .../test/scala/cats/tests/CategoryTests.scala | 2 +- .../test/scala/cats/tests/ComposeTest.scala | 2 +- .../test/scala/cats/tests/ConstTests.scala | 2 +- .../scala/cats/tests/CoproductTests.scala | 3 +- .../test/scala/cats/tests/EitherTests.scala | 2 +- .../src/test/scala/cats/tests/EvalTests.scala | 2 +- .../src/test/scala/cats/tests/FreeTests.scala | 1 + .../test/scala/cats/tests/FunctionTests.scala | 2 +- .../test/scala/cats/tests/KleisliTests.scala | 2 +- .../test/scala/cats/tests/OneAndTests.scala | 2 +- .../src/test/scala/cats/tests/SetTests.scala | 2 +- .../scala/cats/tests/ValidatedTests.scala | 2 +- .../test/scala/cats/tests/WriterTTests.scala | 2 +- .../src/test/scala/cats/tests/XorTTests.scala | 2 +- .../src/test/scala/cats/tests/XorTests.scala | 2 +- 31 files changed, 77 insertions(+), 248 deletions(-) diff --git a/bench/src/main/scala/cats/bench/FoldBench.scala b/bench/src/main/scala/cats/bench/FoldBench.scala index d6388e868e..8f1d43bdc8 100644 --- a/bench/src/main/scala/cats/bench/FoldBench.scala +++ b/bench/src/main/scala/cats/bench/FoldBench.scala @@ -1,7 +1,7 @@ package cats.bench -import algebra.std.string._ import cats.data.Const +import cats.std.string._ import cats.std.list._ import cats.{Foldable, Traverse} import org.openjdk.jmh.annotations.{Benchmark, Scope, State} diff --git a/build.sbt b/build.sbt index 51e176f642..0a6820fe72 100644 --- a/build.sbt +++ b/build.sbt @@ -204,7 +204,7 @@ lazy val kernelLawsJVM = kernelLaws.jvm lazy val kernelLawsJS = kernelLaws.js lazy val core = crossProject.crossType(CrossType.Pure) - .dependsOn(macros) + .dependsOn(macros, kernel) .settings(moduleName := "cats-core") .settings(catsSettings:_*) .settings(sourceGenerators in Compile <+= (sourceManaged in Compile).map(Boilerplate.gen)) @@ -216,7 +216,7 @@ lazy val coreJVM = core.jvm lazy val coreJS = core.js lazy val laws = crossProject.crossType(CrossType.Pure) - .dependsOn(macros, core) + .dependsOn(macros, kernel, core, kernelLaws) .settings(moduleName := "cats-laws") .settings(catsSettings:_*) .settings(disciplineDependencies:_*) diff --git a/core/src/main/scala/cats/package.scala b/core/src/main/scala/cats/package.scala index 393ecc7004..d1e7ad5730 100644 --- a/core/src/main/scala/cats/package.scala +++ b/core/src/main/scala/cats/package.scala @@ -45,17 +45,17 @@ package object cats { f(a) } - type Eq[A] = algebra.Eq[A] - type PartialOrder[A] = algebra.PartialOrder[A] - type Order[A] = algebra.Order[A] - type Semigroup[A] = algebra.Semigroup[A] - type Monoid[A] = algebra.Monoid[A] - type Group[A] = algebra.Group[A] + type Eq[A] = cats.kernel.Eq[A] + type PartialOrder[A] = cats.kernel.PartialOrder[A] + type Order[A] = cats.kernel.Order[A] + type Semigroup[A] = cats.kernel.Semigroup[A] + type Monoid[A] = cats.kernel.Monoid[A] + type Group[A] = cats.kernel.Group[A] - val Eq = algebra.Eq - val PartialOrder = algebra.PartialOrder - val Order = algebra.Order - val Semigroup = algebra.Semigroup - val Monoid = algebra.Monoid - val Group = algebra.Group + val Eq = cats.kernel.Eq + val PartialOrder = cats.kernel.PartialOrder + val Order = cats.kernel.Order + val Semigroup = cats.kernel.Semigroup + val Monoid = cats.kernel.Monoid + val Group = cats.kernel.Group } diff --git a/core/src/main/scala/cats/std/anyval.scala b/core/src/main/scala/cats/std/anyval.scala index 7964aa18e9..41110f5377 100644 --- a/core/src/main/scala/cats/std/anyval.scala +++ b/core/src/main/scala/cats/std/anyval.scala @@ -1,8 +1,7 @@ package cats package std -import algebra.CommutativeGroup -import algebra.ring.AdditiveCommutativeGroup +import cats.kernel.CommutativeGroup trait AnyValInstances extends IntInstances @@ -15,129 +14,103 @@ trait AnyValInstances with BooleanInstances with UnitInstances -trait IntInstances extends algebra.std.IntInstances { +trait IntInstances extends cats.kernel.std.IntInstances { implicit val intShow: Show[Int] = Show.fromToString[Int] implicit val intGroup: CommutativeGroup[Int] = - AdditiveCommutativeGroup[Int].additive - + new CommutativeGroup[Int] { + def combine(x: Int, y: Int): Int = x + y + def empty: Int = 0 + def inverse(x: Int): Int = -x + override def remove(x: Int, y: Int): Int = x - y + } } -trait ByteInstances /* missing algebra type classes */ { +trait ByteInstances extends cats.kernel.std.ByteInstances { implicit val byteShow: Show[Byte] = Show.fromToString[Byte] - // TODO: replace this minimal algebra with one from the algebra project - implicit val byteAlgebra: CommutativeGroup[Byte] with Order[Byte] = - new CommutativeGroup[Byte] with Order[Byte] { + implicit val byteGroup: CommutativeGroup[Byte] = + new CommutativeGroup[Byte] { def combine(x: Byte, y: Byte): Byte = (x + y).toByte def empty: Byte = 0 def inverse(x: Byte): Byte = (-x).toByte - def compare(x: Byte, y: Byte): Int = - if (x < y) -1 else if (y < x) 1 else 0 + override def remove(x: Byte, y: Byte): Byte = (x - y).toByte } } -trait CharInstances /* missing algebra type classes */ { - +trait CharInstances extends cats.kernel.std.CharInstances { implicit val charShow: Show[Char] = Show.fromToString[Char] - - implicit val charOrder: Order[Char] = - new Order[Char] { - def compare(x: Char, y: Char): Int = - if (x < y) -1 else if (y < x) 1 else 0 - } } -trait ShortInstances /* missing algebra type classes */ { +trait ShortInstances extends cats.kernel.std.ShortInstances { implicit val shortShow: Show[Short] = Show.fromToString[Short] - // TODO: replace this minimal algebra with one from the algebra project - implicit val shortAlgebra: CommutativeGroup[Short] with Order[Short] = - new CommutativeGroup[Short] with Order[Short] { + implicit val shortGroup: CommutativeGroup[Short] = + new CommutativeGroup[Short] { def combine(x: Short, y: Short): Short = (x + y).toShort def empty: Short = 0 def inverse(x: Short): Short = (-x).toShort - def compare(x: Short, y: Short): Int = - if (x < y) -1 else if (y < x) 1 else 0 + override def remove(x: Short, y: Short): Short = (x - y).toShort } - } -trait LongInstances /* missing algebra type classes */ { +trait LongInstances extends cats.kernel.std.LongInstances { implicit val longShow: Show[Long] = Show.fromToString[Long] - // TODO: replace this minimal algebra with one from the algebra project - implicit val longAlgebra: CommutativeGroup[Long] with Order[Long] = - new CommutativeGroup[Long] with Order[Long] { + implicit val longGroup: CommutativeGroup[Long] = + new CommutativeGroup[Long] { def combine(x: Long, y: Long): Long = x + y def empty: Long = 0L def inverse(x: Long): Long = -x - def compare(x: Long, y: Long): Int = - if (x < y) -1 else if (y < x) 1 else 0 + override def remove(x: Long, y: Long): Long = x - y } } -trait FloatInstances /* missing algebra type classes */ { +trait FloatInstances extends cats.kernel.std.FloatInstances { implicit val floatShow: Show[Float] = Show.fromToString[Float] - // TODO: replace this minimal algebra with one from the algebra project - implicit val floatAlgebra: CommutativeGroup[Float] with Order[Float] = - new CommutativeGroup[Float] with Order[Float] { + implicit val floatGroup: CommutativeGroup[Float] = + new CommutativeGroup[Float] { def combine(x: Float, y: Float): Float = x + y def empty: Float = 0F def inverse(x: Float): Float = -x - def compare(x: Float, y: Float): Int = - java.lang.Float.compare(x, y) + override def remove(x: Float, y: Float): Float = x - y } - } -trait DoubleInstances /* missing algebra type classes */ { +trait DoubleInstances extends cats.kernel.std.DoubleInstances { implicit val doubleShow: Show[Double] = Show.fromToString[Double] - // TODO: replace this minimal algebra with one from the algebra project - implicit val doubleAlgebra: CommutativeGroup[Double] with Order[Double] = - new CommutativeGroup[Double] with Order[Double] { + implicit val doubleGroup: CommutativeGroup[Double] = + new CommutativeGroup[Double] { def combine(x: Double, y: Double): Double = x + y def empty: Double = 0D def inverse(x: Double): Double = -x - def compare(x: Double, y: Double): Int = - java.lang.Double.compare(x, y) + override def remove(x: Double, y: Double): Double = x - y } - } -trait BooleanInstances extends algebra.std.BooleanInstances { +trait BooleanInstances extends cats.kernel.std.BooleanInstances { implicit val booleanShow: Show[Boolean] = Show.fromToString[Boolean] - } -trait UnitInstances /* missing algebra type classes */ { +trait UnitInstances extends cats.kernel.std.UnitInstances { implicit val unitShow: Show[Unit] = Show.fromToString[Unit] - - implicit val unitAlgebra: CommutativeGroup[Unit] with Order[Unit] = - new CommutativeGroup[Unit] with Order[Unit] { - def combine(x: Unit, y: Unit): Unit = () - def empty: Unit = () - def inverse(x: Unit): Unit = () - def compare(x: Unit, y: Unit): Int = 0 - } - } diff --git a/core/src/main/scala/cats/std/bigInt.scala b/core/src/main/scala/cats/std/bigInt.scala index d075af3826..0c663f734e 100644 --- a/core/src/main/scala/cats/std/bigInt.scala +++ b/core/src/main/scala/cats/std/bigInt.scala @@ -1,7 +1,7 @@ package cats package std -trait BigIntInstances extends algebra.std.BigIntInstances { +trait BigIntInstances extends cats.kernel.std.BigIntInstances { implicit val bigIntShow: Show[BigInt] = Show.fromToString[BigInt] } diff --git a/core/src/main/scala/cats/std/function.scala b/core/src/main/scala/cats/std/function.scala index 0215fda5b9..f19d20a816 100644 --- a/core/src/main/scala/cats/std/function.scala +++ b/core/src/main/scala/cats/std/function.scala @@ -1,12 +1,12 @@ package cats package std -import algebra.Eq import cats.arrow.{Arrow, Choice} import cats.data.Xor import cats.functor.Contravariant private[std] sealed trait Function0Instances { + implicit val function0Instance: Bimonad[Function0] = new Bimonad[Function0] { def extract[A](x: () => A): A = x() diff --git a/core/src/main/scala/cats/std/list.scala b/core/src/main/scala/cats/std/list.scala index c79a3d006b..372e91ab5d 100644 --- a/core/src/main/scala/cats/std/list.scala +++ b/core/src/main/scala/cats/std/list.scala @@ -1,16 +1,13 @@ package cats package std -import algebra.Eq -import algebra.std.{ListMonoid, ListOrder} - -import cats.syntax.order._ import cats.syntax.show._ import scala.annotation.tailrec import scala.collection.mutable.ListBuffer -trait ListInstances extends ListInstances1 { +trait ListInstances extends cats.kernel.std.ListInstances { + implicit val listInstance: Traverse[List] with MonadCombine[List] with CoflatMap[List] = new Traverse[List] with MonadCombine[List] with CoflatMap[List] { @@ -65,54 +62,8 @@ trait ListInstances extends ListInstances1 { override def isEmpty[A](fa: List[A]): Boolean = fa.isEmpty } - implicit def listAlgebra[A]: Monoid[List[A]] = new ListMonoid[A] - implicit def listOrder[A: Order]: Order[List[A]] = new ListOrder[A] - implicit def listShow[A:Show]: Show[List[A]] = new Show[List[A]] { def show(fa: List[A]): String = fa.map(_.show).mkString("List(", ", ", ")") } } - -private[std] sealed trait ListInstances1 extends ListInstances2 { - implicit def partialOrderList[A: PartialOrder]: PartialOrder[List[A]] = - new PartialOrder[List[A]] { - def partialCompare(x: List[A], y: List[A]): Double = { - def loop(xs: List[A], ys: List[A]): Double = - xs match { - case a :: xs => - ys match { - case b :: ys => - val n = a partialCompare b - if (n != 0.0) n else loop(xs, ys) - case Nil => - 1.0 - } - case Nil => - if (ys.isEmpty) 0.0 else -1.0 - } - loop(x, y) - } - } -} - -private[std] sealed trait ListInstances2 { - implicit def eqList[A: Eq]: Eq[List[A]] = - new Eq[List[A]] { - def eqv(x: List[A], y: List[A]): Boolean = { - def loop(xs: List[A], ys: List[A]): Boolean = - xs match { - case a :: xs => - ys match { - case b :: ys => - if (a =!= b) false else loop(xs, ys) - case Nil => - false - } - case Nil => - ys.isEmpty - } - loop(x, y) - } - } -} diff --git a/core/src/main/scala/cats/std/map.scala b/core/src/main/scala/cats/std/map.scala index ee75ae27ee..9d4dd43b95 100644 --- a/core/src/main/scala/cats/std/map.scala +++ b/core/src/main/scala/cats/std/map.scala @@ -1,23 +1,7 @@ package cats package std -import cats.syntax.eq._ - -trait MapInstances extends algebra.std.MapInstances { - - implicit def MapEq[A, B: Eq]: Eq[Map[A, B]] = - new Eq[Map[A, B]] { - def eqv(lhs: Map[A, B], rhs: Map[A, B]): Boolean = { - def checkKeys: Boolean = - lhs.forall { case (k, v1) => - rhs.get(k) match { - case Some(v2) => v1 === v2 - case None => false - } - } - (lhs eq rhs) || (lhs.size == rhs.size && checkKeys) - } - } +trait MapInstances extends cats.kernel.std.MapInstances { implicit def MapShow[A, B](implicit showA: Show[A], showB: Show[B]): Show[Map[A, B]] = Show.show[Map[A, B]] { m => diff --git a/core/src/main/scala/cats/std/option.scala b/core/src/main/scala/cats/std/option.scala index 05df794fe2..d70e978ea4 100644 --- a/core/src/main/scala/cats/std/option.scala +++ b/core/src/main/scala/cats/std/option.scala @@ -1,9 +1,8 @@ package cats package std -import algebra.Eq +trait OptionInstances extends cats.kernel.std.OptionInstances { -trait OptionInstances extends OptionInstances1 { implicit val optionInstance: Traverse[Option] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] = new Traverse[Option] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] { @@ -53,33 +52,6 @@ trait OptionInstances extends OptionInstances1 { fa.isEmpty } - implicit def optionMonoid[A](implicit ev: Semigroup[A]): Monoid[Option[A]] = - new Monoid[Option[A]] { - def empty: Option[A] = None - def combine(x: Option[A], y: Option[A]): Option[A] = - x match { - case None => y - case Some(xx) => y match { - case None => x - case Some(yy) => Some(ev.combine(xx,yy)) - } - } - } - - implicit def orderOption[A](implicit ev: Order[A]): Order[Option[A]] = - new Order[Option[A]] { - def compare(x: Option[A], y: Option[A]): Int = - x match { - case Some(a) => - y match { - case Some(b) => ev.compare(a, b) - case None => 1 - } - case None => - if (y.isDefined) -1 else 0 - } - } - implicit def showOption[A](implicit A: Show[A]): Show[Option[A]] = new Show[Option[A]] { def show(fa: Option[A]): String = fa match { @@ -88,19 +60,3 @@ trait OptionInstances extends OptionInstances1 { } } } - -private[std] sealed trait OptionInstances1 extends OptionInstances2 { - implicit def partialOrderOption[A](implicit ev: PartialOrder[A]): PartialOrder[Option[A]] = - new PartialOrder[Option[A]] { - def partialCompare(x: Option[A], y: Option[A]): Double = - x.fold(if (y.isDefined) -1.0 else 0.0)(a => y.fold(1.0)(ev.partialCompare(_, a))) - } -} - -private[std] sealed trait OptionInstances2 { - implicit def eqOption[A](implicit ev: Eq[A]): Eq[Option[A]] = - new Eq[Option[A]] { - def eqv(x: Option[A], y: Option[A]): Boolean = - x.fold(y == None)(a => y.fold(false)(ev.eqv(_, a))) - } -} diff --git a/core/src/main/scala/cats/std/set.scala b/core/src/main/scala/cats/std/set.scala index a4ce9f0bbf..7ac35ef22e 100644 --- a/core/src/main/scala/cats/std/set.scala +++ b/core/src/main/scala/cats/std/set.scala @@ -3,7 +3,8 @@ package std import cats.syntax.show._ -trait SetInstances extends algebra.std.SetInstances { +trait SetInstances extends cats.kernel.std.SetInstances { + implicit val setInstance: Foldable[Set] with MonoidK[Set] = new Foldable[Set] with MonoidK[Set] { @@ -26,8 +27,6 @@ trait SetInstances extends algebra.std.SetInstances { override def isEmpty[A](fa: Set[A]): Boolean = fa.isEmpty } - implicit def setMonoid[A]: Monoid[Set[A]] = MonoidK[Set].algebra[A] - implicit def setShow[A:Show]: Show[Set[A]] = new Show[Set[A]] { def show(fa: Set[A]): String = fa.toIterator.map(_.show).mkString("Set(", ", ", ")") diff --git a/core/src/main/scala/cats/std/stream.scala b/core/src/main/scala/cats/std/stream.scala index 9406bcec96..d865c37555 100644 --- a/core/src/main/scala/cats/std/stream.scala +++ b/core/src/main/scala/cats/std/stream.scala @@ -1,10 +1,9 @@ package cats package std -import scala.collection.immutable.Stream.Empty import cats.syntax.show._ -trait StreamInstances { +trait StreamInstances extends cats.kernel.std.StreamInstances { implicit val streamInstance: Traverse[Stream] with MonadCombine[Stream] with CoflatMap[Stream] = new Traverse[Stream] with MonadCombine[Stream] with CoflatMap[Stream] { @@ -63,24 +62,4 @@ trait StreamInstances { new Show[Stream[A]] { def show(fa: Stream[A]): String = if(fa.isEmpty) "Stream()" else s"Stream(${fa.head.show}, ?)" } - - // TODO: eventually use algebra's instances (which will deal with - // implicit priority between Eq/PartialOrder/Order). - - implicit def eqStream[A](implicit ev: Eq[A]): Eq[Stream[A]] = - new Eq[Stream[A]] { - def eqv(x: Stream[A], y: Stream[A]): Boolean = { - def loop(xs: Stream[A], ys: Stream[A]): Boolean = - xs match { - case Empty => ys.isEmpty - case a #:: xs => - ys match { - case Empty => false - case b #:: ys => if (ev.neqv(a, b)) false else loop(xs, ys) - } - } - loop(x, y) - } - } - } diff --git a/core/src/main/scala/cats/std/string.scala b/core/src/main/scala/cats/std/string.scala index a79a814734..ca96850460 100644 --- a/core/src/main/scala/cats/std/string.scala +++ b/core/src/main/scala/cats/std/string.scala @@ -1,7 +1,7 @@ package cats package std -trait StringInstances extends algebra.std.StringInstances { +trait StringInstances extends cats.kernel.std.StringInstances { implicit val stringShow: Show[String] = Show.fromToString[String] } diff --git a/core/src/main/scala/cats/std/vector.scala b/core/src/main/scala/cats/std/vector.scala index 4df0044bdb..04d1b71133 100644 --- a/core/src/main/scala/cats/std/vector.scala +++ b/core/src/main/scala/cats/std/vector.scala @@ -7,7 +7,7 @@ import scala.annotation.tailrec import scala.collection.+: import scala.collection.immutable.VectorBuilder -trait VectorInstances { +trait VectorInstances extends cats.kernel.std.VectorInstances { implicit val vectorInstance: Traverse[Vector] with MonadCombine[Vector] with CoflatMap[Vector] = new Traverse[Vector] with MonadCombine[Vector] with CoflatMap[Vector] { @@ -57,18 +57,4 @@ trait VectorInstances { new Show[Vector[A]] { def show(fa: Vector[A]): String = fa.map(_.show).mkString("Vector(", ", ", ")") } - - // TODO: eventually use algebra's instances (which will deal with - // implicit priority between Eq/PartialOrder/Order). - - implicit def eqVector[A](implicit ev: Eq[A]): Eq[Vector[A]] = - new Eq[Vector[A]] { - def eqv(x: Vector[A], y: Vector[A]): Boolean = { - @tailrec def loop(to: Int): Boolean = - if(to == -1) true - else ev.eqv(x(to), y(to)) && loop(to - 1) - - (x.size == y.size) && loop(x.size - 1) - } - } } diff --git a/laws/src/main/scala/cats/laws/discipline/CartesianTests.scala b/laws/src/main/scala/cats/laws/discipline/CartesianTests.scala index 0f83256b41..989baeb7eb 100644 --- a/laws/src/main/scala/cats/laws/discipline/CartesianTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/CartesianTests.scala @@ -36,7 +36,7 @@ object CartesianTests { } object Isomorphisms { - import algebra.laws._ + import cats.kernel.laws._ implicit def invariant[F[_]](implicit F: functor.Invariant[F]): Isomorphisms[F] = new Isomorphisms[F] { def associativity[A, B, C](fs: (F[(A, (B, C))], F[((A, B), C)]))(implicit EqFABC: Eq[F[(A, B, C)]]) = diff --git a/laws/src/main/scala/cats/laws/discipline/Eq.scala b/laws/src/main/scala/cats/laws/discipline/Eq.scala index 730822231d..a7261833ef 100644 --- a/laws/src/main/scala/cats/laws/discipline/Eq.scala +++ b/laws/src/main/scala/cats/laws/discipline/Eq.scala @@ -3,7 +3,6 @@ package laws package discipline import catalysts.Platform -import algebra.Eq import cats.std.string._ import org.scalacheck.Arbitrary @@ -26,10 +25,12 @@ object eq { } /** Create an approximation of Eq[Show[A]] by using function1Eq[A, String] */ - implicit def showEq[A: Arbitrary]: Eq[Show[A]] = + implicit def showEq[A: Arbitrary]: Eq[Show[A]] = { + val xyz = function1Eq[A, String] Eq.by[Show[A], A => String] { showInstance => (a: A) => showInstance.show(a) } + } // Temporary, see https://github.com/non/algebra/pull/82 implicit def tuple2Eq[A, B](implicit A: Eq[A], B: Eq[B]): Eq[(A, B)] = diff --git a/laws/src/main/scala/cats/laws/discipline/package.scala b/laws/src/main/scala/cats/laws/discipline/package.scala index a015ad9bcd..6862b3036c 100644 --- a/laws/src/main/scala/cats/laws/discipline/package.scala +++ b/laws/src/main/scala/cats/laws/discipline/package.scala @@ -1,7 +1,7 @@ package cats package laws -import algebra.laws._ +import cats.kernel.laws._ import org.scalacheck.Prop diff --git a/tests/src/test/scala/cats/tests/CategoryTests.scala b/tests/src/test/scala/cats/tests/CategoryTests.scala index b93cc732d6..dc0de32190 100644 --- a/tests/src/test/scala/cats/tests/CategoryTests.scala +++ b/tests/src/test/scala/cats/tests/CategoryTests.scala @@ -1,7 +1,7 @@ package cats package tests -import algebra.laws.GroupLaws +import cats.kernel.laws.GroupLaws import cats.arrow.Category import cats.laws.discipline.{MonoidKTests, SerializableTests} diff --git a/tests/src/test/scala/cats/tests/ComposeTest.scala b/tests/src/test/scala/cats/tests/ComposeTest.scala index 74019444bb..d6694e7caf 100644 --- a/tests/src/test/scala/cats/tests/ComposeTest.scala +++ b/tests/src/test/scala/cats/tests/ComposeTest.scala @@ -1,7 +1,7 @@ package cats package tests -import algebra.laws.GroupLaws +import cats.kernel.laws.GroupLaws import cats.arrow.Compose import cats.laws.discipline.{SemigroupKTests, SerializableTests} diff --git a/tests/src/test/scala/cats/tests/ConstTests.scala b/tests/src/test/scala/cats/tests/ConstTests.scala index 20023b2735..1b487575ea 100644 --- a/tests/src/test/scala/cats/tests/ConstTests.scala +++ b/tests/src/test/scala/cats/tests/ConstTests.scala @@ -1,7 +1,7 @@ package cats package tests -import algebra.laws.{GroupLaws, OrderLaws} +import cats.kernel.laws.{GroupLaws, OrderLaws} import cats.data.{Const, NonEmptyList} import cats.functor.Contravariant diff --git a/tests/src/test/scala/cats/tests/CoproductTests.scala b/tests/src/test/scala/cats/tests/CoproductTests.scala index afdc58a221..6c0279efd2 100644 --- a/tests/src/test/scala/cats/tests/CoproductTests.scala +++ b/tests/src/test/scala/cats/tests/CoproductTests.scala @@ -1,8 +1,7 @@ package cats.tests -import algebra.Eq -import algebra.laws.OrderLaws import cats._ +import cats.kernel.laws.OrderLaws import cats.data.Coproduct import cats.functor.Contravariant import cats.laws.discipline._ diff --git a/tests/src/test/scala/cats/tests/EitherTests.scala b/tests/src/test/scala/cats/tests/EitherTests.scala index a104d21cbd..a5ef74d18c 100644 --- a/tests/src/test/scala/cats/tests/EitherTests.scala +++ b/tests/src/test/scala/cats/tests/EitherTests.scala @@ -3,7 +3,7 @@ package tests import cats.laws.discipline.{BitraverseTests, TraverseTests, MonadTests, SerializableTests, CartesianTests} import cats.laws.discipline.eq._ -import algebra.laws.OrderLaws +import cats.kernel.laws.OrderLaws class EitherTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/EvalTests.scala b/tests/src/test/scala/cats/tests/EvalTests.scala index 46c490898d..ba3ed945ad 100644 --- a/tests/src/test/scala/cats/tests/EvalTests.scala +++ b/tests/src/test/scala/cats/tests/EvalTests.scala @@ -6,7 +6,7 @@ import cats.laws.ComonadLaws import cats.laws.discipline.{CartesianTests, BimonadTests, SerializableTests} import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq._ -import algebra.laws.{GroupLaws, OrderLaws} +import cats.kernel.laws.{GroupLaws, OrderLaws} class EvalTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/FreeTests.scala b/tests/src/test/scala/cats/tests/FreeTests.scala index 56fe308ac2..ad84dc6578 100644 --- a/tests/src/test/scala/cats/tests/FreeTests.scala +++ b/tests/src/test/scala/cats/tests/FreeTests.scala @@ -6,6 +6,7 @@ import cats.free.{Free, Trampoline} import cats.laws.discipline.{CartesianTests, MonadTests, SerializableTests} import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary.function0Arbitrary + import org.scalacheck.{Arbitrary, Gen} import Arbitrary.arbFunction1 diff --git a/tests/src/test/scala/cats/tests/FunctionTests.scala b/tests/src/test/scala/cats/tests/FunctionTests.scala index 7daf31c445..d7ca5cf729 100644 --- a/tests/src/test/scala/cats/tests/FunctionTests.scala +++ b/tests/src/test/scala/cats/tests/FunctionTests.scala @@ -6,7 +6,7 @@ import cats.functor.Contravariant import cats.laws.discipline._ import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary._ -import algebra.laws.GroupLaws +import cats.kernel.laws.GroupLaws class FunctionTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/KleisliTests.scala b/tests/src/test/scala/cats/tests/KleisliTests.scala index 959bba9776..27350621db 100644 --- a/tests/src/test/scala/cats/tests/KleisliTests.scala +++ b/tests/src/test/scala/cats/tests/KleisliTests.scala @@ -8,7 +8,7 @@ import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq._ import org.scalacheck.Arbitrary -import algebra.laws.GroupLaws +import cats.kernel.laws.GroupLaws import cats.laws.discipline.{SemigroupKTests, MonoidKTests} class KleisliTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index ad711ea410..96dec80fbb 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -1,7 +1,7 @@ package cats package tests -import algebra.laws.{GroupLaws, OrderLaws} +import cats.kernel.laws.{GroupLaws, OrderLaws} import cats.data.{NonEmptyList, OneAnd} import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, CartesianTests, TraverseTests, ReducibleTests} diff --git a/tests/src/test/scala/cats/tests/SetTests.scala b/tests/src/test/scala/cats/tests/SetTests.scala index 571b238b0b..e4716df529 100644 --- a/tests/src/test/scala/cats/tests/SetTests.scala +++ b/tests/src/test/scala/cats/tests/SetTests.scala @@ -4,7 +4,7 @@ package tests import cats.laws.discipline.{FoldableTests, MonoidKTests, SerializableTests} class SetTests extends CatsSuite { - checkAll("Set[Int]", algebra.laws.GroupLaws[Set[Int]].monoid) + checkAll("Set[Int]", cats.kernel.laws.GroupLaws[Set[Int]].monoid) checkAll("Set[Int]", MonoidKTests[Set].monoidK[Int]) checkAll("MonoidK[Set]", SerializableTests.serializable(MonoidK[Set])) diff --git a/tests/src/test/scala/cats/tests/ValidatedTests.scala b/tests/src/test/scala/cats/tests/ValidatedTests.scala index e755b1719d..c629cbdca4 100644 --- a/tests/src/test/scala/cats/tests/ValidatedTests.scala +++ b/tests/src/test/scala/cats/tests/ValidatedTests.scala @@ -7,7 +7,7 @@ import cats.laws.discipline.{BifunctorTests, TraverseTests, ApplicativeErrorTest import org.scalacheck.Arbitrary._ import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq.tuple3Eq -import algebra.laws.{OrderLaws, GroupLaws} +import cats.kernel.laws.{OrderLaws, GroupLaws} import scala.util.Try diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index cd0cc07312..ecc3846a02 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -7,7 +7,7 @@ import cats.laws.discipline._ import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary._ -import algebra.laws.OrderLaws +import cats.kernel.laws.OrderLaws class WriterTTests extends CatsSuite { type Logged[A] = Writer[ListWrapper[Int], A] diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index 7ea4e9b343..a9f2105045 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -6,7 +6,7 @@ import cats.data.{Xor, XorT} import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq.tuple3Eq -import algebra.laws.OrderLaws +import cats.kernel.laws.OrderLaws class XorTTests extends CatsSuite { implicit val eq0 = XorT.xorTEq[List, String, String Xor Int] diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index 58d0a90de2..cffedddd37 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -6,7 +6,7 @@ import cats.data.Xor._ import cats.laws.discipline.arbitrary._ import cats.laws.discipline.{BitraverseTests, TraverseTests, MonadErrorTests, SerializableTests, CartesianTests} import cats.laws.discipline.eq.tuple3Eq -import algebra.laws.{GroupLaws, OrderLaws} +import cats.kernel.laws.{GroupLaws, OrderLaws} import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary._ From 041a06a876a307c813c7a7adc032ccde983bacbd Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 13:23:36 -0400 Subject: [PATCH 083/185] Remove the algebra dependency. After clean, validate passed. I think this branch is good to go. --- build.sbt | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.sbt b/build.sbt index 0a6820fe72..fbdd6dc3e9 100644 --- a/build.sbt +++ b/build.sbt @@ -44,8 +44,6 @@ lazy val commonSettings = Seq( ), libraryDependencies ++= Seq( "com.github.mpilquist" %%% "simulacrum" % "0.7.0", - "org.spire-math" %%% "algebra" % "0.3.1", - "org.spire-math" %%% "algebra-std" % "0.3.1", "org.typelevel" %%% "machinist" % "0.4.1", compilerPlugin("org.scalamacros" %% "paradise" % "2.1.0" cross CrossVersion.full), compilerPlugin("org.spire-math" %% "kind-projector" % "0.6.3") From 36c5a33b137a0aa12bfc718548dbb0a7dd6c7299 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Mon, 25 Apr 2016 11:04:37 -0400 Subject: [PATCH 084/185] updated to algebra 0.4.2 --- build.sbt | 8 ++++--- core/src/main/scala/cats/data/WriterT.scala | 6 +++--- core/src/main/scala/cats/std/anyval.scala | 1 + core/src/main/scala/cats/std/tuple.scala | 2 +- docs/src/main/tut/monoid.md | 21 ++++--------------- .../test/scala/cats/tests/FutureTests.scala | 1 - .../test/scala/cats/tests/FutureTests.scala | 1 - .../main/scala/cats/laws/discipline/Eq.scala | 12 ----------- .../scala/cats/tests/BitraverseTests.scala | 1 - .../test/scala/cats/tests/ComposeTests.scala | 2 +- .../test/scala/cats/tests/EitherTests.scala | 3 +-- .../src/test/scala/cats/tests/EvalTests.scala | 1 - .../cats/tests/FreeApplicativeTests.scala | 1 - .../src/test/scala/cats/tests/FreeTests.scala | 1 - tests/src/test/scala/cats/tests/IdTests.scala | 1 - .../src/test/scala/cats/tests/IorTests.scala | 1 - .../src/test/scala/cats/tests/ListTests.scala | 1 - .../src/test/scala/cats/tests/MapTests.scala | 1 - .../scala/cats/tests/MonadCombineTests.scala | 1 - .../test/scala/cats/tests/OneAndTests.scala | 1 - .../test/scala/cats/tests/OptionTTests.scala | 1 - .../test/scala/cats/tests/OptionTests.scala | 1 - .../src/test/scala/cats/tests/ProdTests.scala | 1 - .../test/scala/cats/tests/StateTTests.scala | 1 + .../test/scala/cats/tests/StreamTests.scala | 1 - .../test/scala/cats/tests/TupleTests.scala | 1 - .../scala/cats/tests/ValidatedTests.scala | 1 - .../test/scala/cats/tests/VectorTests.scala | 1 - .../test/scala/cats/tests/WriterTTests.scala | 1 - .../test/scala/cats/tests/WriterTests.scala | 1 - .../src/test/scala/cats/tests/XorTTests.scala | 3 +-- .../src/test/scala/cats/tests/XorTests.scala | 3 +-- 32 files changed, 19 insertions(+), 64 deletions(-) diff --git a/build.sbt b/build.sbt index e0f4d915ab..17266c1dc0 100644 --- a/build.sbt +++ b/build.sbt @@ -24,6 +24,8 @@ lazy val catsDoctestSettings = Seq( doctestWithDependencies := false ) ++ doctestSettings +lazy val algebraVersion = "0.4.2" + lazy val commonSettings = Seq( scalacOptions ++= commonScalacOptions, resolvers ++= Seq( @@ -33,8 +35,8 @@ lazy val commonSettings = Seq( ), libraryDependencies ++= Seq( "com.github.mpilquist" %%% "simulacrum" % "0.7.0", - "org.spire-math" %%% "algebra" % "0.3.1", - "org.spire-math" %%% "algebra-std" % "0.3.1", + "org.spire-math" %%% "algebra" % algebraVersion, + "org.spire-math" %%% "algebra-std" % algebraVersion, "org.typelevel" %%% "machinist" % "0.4.1", compilerPlugin("org.scalamacros" %% "paradise" % "2.1.0" cross CrossVersion.full), compilerPlugin("org.spire-math" %% "kind-projector" % "0.6.3") @@ -179,7 +181,7 @@ lazy val laws = crossProject.crossType(CrossType.Pure) .settings(catsSettings:_*) .settings(disciplineDependencies:_*) .settings(libraryDependencies ++= Seq( - "org.spire-math" %%% "algebra-laws" % "0.3.1", + "org.spire-math" %%% "algebra-laws" % algebraVersion, "org.typelevel" %%% "catalysts-platform" % "0.0.2")) .jsSettings(commonJsSettings:_*) .jvmSettings(commonJvmSettings:_*) diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 8d21fbf1b5..a2181c9b48 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -1,6 +1,7 @@ package cats package data +import algebra.std.tuple.tuple2Eq import cats.functor.Bifunctor final case class WriterT[F[_], L, V](run: F[(L, V)]) { @@ -50,12 +51,11 @@ final case class WriterT[F[_], L, V](run: F[(L, V)]) { object WriterT extends WriterTInstances with WriterTFunctions private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { + implicit def writerTIdMonad[L:Monoid]: Monad[WriterT[Id, L, ?]] = writerTMonadWriter[Id, L] - // The Eq[(L, V)] can be derived from an Eq[L] and Eq[V], but we are waiting - // on an algebra release that includes https://github.com/non/algebra/pull/82 - implicit def writerTIdEq[L, V](implicit E: Eq[(L, V)]): Eq[WriterT[Id, L, V]] = + implicit def writerTIdEq[L: Eq, V: Eq]: Eq[WriterT[Id, L, V]] = writerTEq[Id, L, V] implicit def writerTBifunctor[F[_]:Functor]: Bifunctor[WriterT[F, ?, ?]] = diff --git a/core/src/main/scala/cats/std/anyval.scala b/core/src/main/scala/cats/std/anyval.scala index 7964aa18e9..0d503eefec 100644 --- a/core/src/main/scala/cats/std/anyval.scala +++ b/core/src/main/scala/cats/std/anyval.scala @@ -14,6 +14,7 @@ trait AnyValInstances with DoubleInstances with BooleanInstances with UnitInstances + with TupleInstances trait IntInstances extends algebra.std.IntInstances { diff --git a/core/src/main/scala/cats/std/tuple.scala b/core/src/main/scala/cats/std/tuple.scala index a690245a81..a9e8edef35 100644 --- a/core/src/main/scala/cats/std/tuple.scala +++ b/core/src/main/scala/cats/std/tuple.scala @@ -1,7 +1,7 @@ package cats package std -trait TupleInstances extends Tuple2Instances +trait TupleInstances extends Tuple2Instances with algebra.std.TupleInstances sealed trait Tuple2Instances { implicit val tuple2Bitraverse: Bitraverse[Tuple2] = diff --git a/docs/src/main/tut/monoid.md b/docs/src/main/tut/monoid.md index 183f5c97d3..b31f6e6662 100644 --- a/docs/src/main/tut/monoid.md +++ b/docs/src/main/tut/monoid.md @@ -60,23 +60,9 @@ l.foldMap(i => i.toString) ``` To use this -with a function that produces a tuple, we can define a `Monoid` for a tuple +with a function that produces a tuple, cats also provides a `Monoid` for a tuple that will be valid for any tuple where the types it contains also have a -`Monoid` available: - -```tut:silent -implicit def tupleMonoid[A : Monoid, B : Monoid]: Monoid[(A, B)] = - new Monoid[(A, B)] { - def combine(x: (A, B), y: (A, B)): (A, B) = { - val (xa, xb) = x - val (ya, yb) = y - (Monoid[A].combine(xa, ya), Monoid[B].combine(xb, yb)) - } - def empty: (A, B) = (Monoid[A].empty, Monoid[B].empty) - } -``` - -Thus. +`Monoid` available, thus. ```tut l.foldMap(i => (i, i.toString)) // do both of the above in one pass, hurrah! @@ -90,4 +76,5 @@ trait](https://github.com/non/algebra/blob/master/core/src/main/scala/algebra/Mo which is defined in the [algebra project](https://github.com/non/algebra) on which it depends. The [`cats` package object](https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/package.scala) defines type aliases to the `Monoid` from algebra, so that you can -`import cats.Monoid`. +`import cats.Monoid`. Also the `Monoid` instance for tuple is also [implemented in algebra](https://github.com/non/algebra/blob/v0.4.2/project/Boilerplate.scala#L80-L217), +cats merely provides it through [inheritance](https://github.com/typelevel/cats/blob/v0.5.0/core/src/main/scala/cats/std/tuple.scala). diff --git a/js/src/test/scala/cats/tests/FutureTests.scala b/js/src/test/scala/cats/tests/FutureTests.scala index 0af2e907e0..b0dc5172dc 100644 --- a/js/src/test/scala/cats/tests/FutureTests.scala +++ b/js/src/test/scala/cats/tests/FutureTests.scala @@ -4,7 +4,6 @@ package tests import cats.data.Xor import cats.laws.discipline._ -import cats.laws.discipline.eq.tuple3Eq import cats.js.std.Await import cats.js.std.future.futureComonad import cats.tests.CatsSuite diff --git a/jvm/src/test/scala/cats/tests/FutureTests.scala b/jvm/src/test/scala/cats/tests/FutureTests.scala index 58a46e13dd..8fba502584 100644 --- a/jvm/src/test/scala/cats/tests/FutureTests.scala +++ b/jvm/src/test/scala/cats/tests/FutureTests.scala @@ -5,7 +5,6 @@ package tests import cats.data.Xor import cats.laws.discipline._ import cats.laws.discipline.arbitrary.evalArbitrary -import cats.laws.discipline.eq.tuple3Eq import cats.jvm.std.future.futureComonad import cats.tests.CatsSuite diff --git a/laws/src/main/scala/cats/laws/discipline/Eq.scala b/laws/src/main/scala/cats/laws/discipline/Eq.scala index 730822231d..c39879c07b 100644 --- a/laws/src/main/scala/cats/laws/discipline/Eq.scala +++ b/laws/src/main/scala/cats/laws/discipline/Eq.scala @@ -31,18 +31,6 @@ object eq { (a: A) => showInstance.show(a) } - // Temporary, see https://github.com/non/algebra/pull/82 - implicit def tuple2Eq[A, B](implicit A: Eq[A], B: Eq[B]): Eq[(A, B)] = - new Eq[(A, B)] { - def eqv(x: (A, B), y: (A, B)): Boolean = - A.eqv(x._1, y._1) && B.eqv(x._2, y._2) - } - - implicit def tuple3Eq[A, B, C](implicit EqA: Eq[A], EqB: Eq[B], EqC: Eq[C]): Eq[(A, B, C)] = - new Eq[(A, B, C)] { - def eqv(x: (A, B, C), y: (A, B, C)): Boolean = EqA.eqv(x._1, y._1) && EqB.eqv(x._2, y._2) && EqC.eqv(x._3, y._3) - } - /** * Create an approximation of Eq[Semigroup[A]] by generating values for A * and comparing the application of the two combine functions. diff --git a/tests/src/test/scala/cats/tests/BitraverseTests.scala b/tests/src/test/scala/cats/tests/BitraverseTests.scala index e981925d80..989c21822a 100644 --- a/tests/src/test/scala/cats/tests/BitraverseTests.scala +++ b/tests/src/test/scala/cats/tests/BitraverseTests.scala @@ -4,7 +4,6 @@ package tests import cats.data.Xor import cats.laws.discipline.{BitraverseTests, SerializableTests} import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq.tuple2Eq class BitraverseTest extends CatsSuite { type XorTuple2[A, B] = Xor[(A, B), (A, B)] diff --git a/tests/src/test/scala/cats/tests/ComposeTests.scala b/tests/src/test/scala/cats/tests/ComposeTests.scala index 88fcbe3718..2cccfa1eff 100644 --- a/tests/src/test/scala/cats/tests/ComposeTests.scala +++ b/tests/src/test/scala/cats/tests/ComposeTests.scala @@ -2,7 +2,7 @@ package cats package tests import cats.data.{ NonEmptyList, NonEmptyVector } -import cats.laws.discipline.{ AlternativeTests, ApplicativeTests, FoldableTests, CartesianTests, MonoidKTests, SemigroupKTests, arbitrary, eq }, arbitrary._, eq._ +import cats.laws.discipline.{ AlternativeTests, ApplicativeTests, FoldableTests, CartesianTests, MonoidKTests, SemigroupKTests, arbitrary }, arbitrary._ class ComposeTests extends CatsSuite { // we have a lot of generated lists of lists in these tests. We have to tell diff --git a/tests/src/test/scala/cats/tests/EitherTests.scala b/tests/src/test/scala/cats/tests/EitherTests.scala index a104d21cbd..f6e8f6d55b 100644 --- a/tests/src/test/scala/cats/tests/EitherTests.scala +++ b/tests/src/test/scala/cats/tests/EitherTests.scala @@ -1,9 +1,8 @@ package cats package tests -import cats.laws.discipline.{BitraverseTests, TraverseTests, MonadTests, SerializableTests, CartesianTests} -import cats.laws.discipline.eq._ import algebra.laws.OrderLaws +import cats.laws.discipline.{BitraverseTests, TraverseTests, MonadTests, SerializableTests, CartesianTests} class EitherTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/EvalTests.scala b/tests/src/test/scala/cats/tests/EvalTests.scala index 46c490898d..d1388f8c99 100644 --- a/tests/src/test/scala/cats/tests/EvalTests.scala +++ b/tests/src/test/scala/cats/tests/EvalTests.scala @@ -5,7 +5,6 @@ import scala.math.min import cats.laws.ComonadLaws import cats.laws.discipline.{CartesianTests, BimonadTests, SerializableTests} import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq._ import algebra.laws.{GroupLaws, OrderLaws} class EvalTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/FreeApplicativeTests.scala b/tests/src/test/scala/cats/tests/FreeApplicativeTests.scala index c8f6dd8861..4db958c593 100644 --- a/tests/src/test/scala/cats/tests/FreeApplicativeTests.scala +++ b/tests/src/test/scala/cats/tests/FreeApplicativeTests.scala @@ -4,7 +4,6 @@ package tests import cats.arrow.NaturalTransformation import cats.free.FreeApplicative import cats.laws.discipline.{CartesianTests, ApplicativeTests, SerializableTests} -import cats.laws.discipline.eq.{tuple3Eq, tuple2Eq} import cats.data.State import org.scalacheck.{Arbitrary, Gen} diff --git a/tests/src/test/scala/cats/tests/FreeTests.scala b/tests/src/test/scala/cats/tests/FreeTests.scala index 56fe308ac2..c03f86482c 100644 --- a/tests/src/test/scala/cats/tests/FreeTests.scala +++ b/tests/src/test/scala/cats/tests/FreeTests.scala @@ -4,7 +4,6 @@ package tests import cats.arrow.NaturalTransformation import cats.free.{Free, Trampoline} import cats.laws.discipline.{CartesianTests, MonadTests, SerializableTests} -import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary.function0Arbitrary import org.scalacheck.{Arbitrary, Gen} import Arbitrary.arbFunction1 diff --git a/tests/src/test/scala/cats/tests/IdTests.scala b/tests/src/test/scala/cats/tests/IdTests.scala index d811e9c3ed..115ccc875e 100644 --- a/tests/src/test/scala/cats/tests/IdTests.scala +++ b/tests/src/test/scala/cats/tests/IdTests.scala @@ -2,7 +2,6 @@ package cats package tests import cats.laws.discipline._ -import cats.laws.discipline.eq.tuple3Eq class IdTests extends CatsSuite { implicit val iso = CartesianTests.Isomorphisms.invariant[Id] diff --git a/tests/src/test/scala/cats/tests/IorTests.scala b/tests/src/test/scala/cats/tests/IorTests.scala index 27c7779516..eca8a7c9c9 100644 --- a/tests/src/test/scala/cats/tests/IorTests.scala +++ b/tests/src/test/scala/cats/tests/IorTests.scala @@ -4,7 +4,6 @@ package tests import cats.data.{Xor, Ior} import cats.laws.discipline.{BifunctorTests, TraverseTests, MonadTests, SerializableTests, CartesianTests} import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq._ import org.scalacheck.Arbitrary._ class IorTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/ListTests.scala b/tests/src/test/scala/cats/tests/ListTests.scala index 98517df453..fc92265ce8 100644 --- a/tests/src/test/scala/cats/tests/ListTests.scala +++ b/tests/src/test/scala/cats/tests/ListTests.scala @@ -4,7 +4,6 @@ package tests import cats.data.NonEmptyList import cats.laws.discipline.{TraverseTests, CoflatMapTests, MonadCombineTests, SerializableTests, CartesianTests} import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq._ class ListTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/MapTests.scala b/tests/src/test/scala/cats/tests/MapTests.scala index ffa619729a..fb33354c64 100644 --- a/tests/src/test/scala/cats/tests/MapTests.scala +++ b/tests/src/test/scala/cats/tests/MapTests.scala @@ -2,7 +2,6 @@ package cats package tests import cats.laws.discipline.{TraverseTests, FlatMapTests, SerializableTests, CartesianTests} -import cats.laws.discipline.eq._ class MapTests extends CatsSuite { implicit val iso = CartesianTests.Isomorphisms.invariant[Map[Int, ?]] diff --git a/tests/src/test/scala/cats/tests/MonadCombineTests.scala b/tests/src/test/scala/cats/tests/MonadCombineTests.scala index adf8f7df73..32b3577d77 100644 --- a/tests/src/test/scala/cats/tests/MonadCombineTests.scala +++ b/tests/src/test/scala/cats/tests/MonadCombineTests.scala @@ -3,7 +3,6 @@ package tests import cats.data.Xor import cats.laws.discipline.arbitrary.xorArbitrary -import cats.laws.discipline.eq.tuple2Eq class MonadCombineTest extends CatsSuite { test("separate") { diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index ad711ea410..84d3cfbbf3 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -6,7 +6,6 @@ import algebra.laws.{GroupLaws, OrderLaws} import cats.data.{NonEmptyList, OneAnd} import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, CartesianTests, TraverseTests, ReducibleTests} import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq._ class OneAndTests extends CatsSuite { // Lots of collections here.. telling ScalaCheck to calm down a bit diff --git a/tests/src/test/scala/cats/tests/OptionTTests.scala b/tests/src/test/scala/cats/tests/OptionTTests.scala index cfe306c921..0873a8cff9 100644 --- a/tests/src/test/scala/cats/tests/OptionTTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTTests.scala @@ -4,7 +4,6 @@ import cats.{Id, Monad, Cartesian, Show} import cats.data.{OptionT, Xor} import cats.laws.discipline.{FunctorTests, SerializableTests, CartesianTests, MonadTests} import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq._ class OptionTTests extends CatsSuite { diff --git a/tests/src/test/scala/cats/tests/OptionTests.scala b/tests/src/test/scala/cats/tests/OptionTests.scala index 8e9d58c89c..717eca34b1 100644 --- a/tests/src/test/scala/cats/tests/OptionTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTests.scala @@ -3,7 +3,6 @@ package tests import cats.laws.{ApplicativeLaws, CoflatMapLaws, FlatMapLaws, MonadLaws} import cats.laws.discipline.{TraverseTests, CoflatMapTests, MonadCombineTests, SerializableTests, CartesianTests} -import cats.laws.discipline.eq._ class OptionTests extends CatsSuite { checkAll("Option[Int]", CartesianTests[Option].cartesian[Int, Int, Int]) diff --git a/tests/src/test/scala/cats/tests/ProdTests.scala b/tests/src/test/scala/cats/tests/ProdTests.scala index c0c10077f5..0ab63943d6 100644 --- a/tests/src/test/scala/cats/tests/ProdTests.scala +++ b/tests/src/test/scala/cats/tests/ProdTests.scala @@ -4,7 +4,6 @@ package tests import cats.data.Prod import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq._ class ProdTests extends CatsSuite { implicit val iso = CartesianTests.Isomorphisms.invariant[Prod[Option, List, ?]] diff --git a/tests/src/test/scala/cats/tests/StateTTests.scala b/tests/src/test/scala/cats/tests/StateTTests.scala index 9e0b8a447e..ec61cf725f 100644 --- a/tests/src/test/scala/cats/tests/StateTTests.scala +++ b/tests/src/test/scala/cats/tests/StateTTests.scala @@ -1,6 +1,7 @@ package cats package tests +import algebra.std.tuple.tuple2Eq import cats.laws.discipline.{CartesianTests, MonadStateTests, SerializableTests} import cats.data.{State, StateT} import cats.laws.discipline.eq._ diff --git a/tests/src/test/scala/cats/tests/StreamTests.scala b/tests/src/test/scala/cats/tests/StreamTests.scala index 82df422329..3e9de86445 100644 --- a/tests/src/test/scala/cats/tests/StreamTests.scala +++ b/tests/src/test/scala/cats/tests/StreamTests.scala @@ -2,7 +2,6 @@ package cats package tests import cats.laws.discipline.{CoflatMapTests, MonadCombineTests, SerializableTests, TraverseTests, CartesianTests} -import cats.laws.discipline.eq.tuple3Eq class StreamTests extends CatsSuite { checkAll("Stream[Int]", CartesianTests[Stream].cartesian[Int, Int, Int]) diff --git a/tests/src/test/scala/cats/tests/TupleTests.scala b/tests/src/test/scala/cats/tests/TupleTests.scala index dae6c08429..fdde8f5545 100644 --- a/tests/src/test/scala/cats/tests/TupleTests.scala +++ b/tests/src/test/scala/cats/tests/TupleTests.scala @@ -2,7 +2,6 @@ package cats package tests import cats.laws.discipline.{BitraverseTests, SerializableTests} -import cats.laws.discipline.eq.tuple2Eq class TupleTests extends CatsSuite { checkAll("Tuple2", BitraverseTests[Tuple2].bitraverse[Option, Int, Int, Int, String, String, String]) diff --git a/tests/src/test/scala/cats/tests/ValidatedTests.scala b/tests/src/test/scala/cats/tests/ValidatedTests.scala index e755b1719d..6a3c05b8b2 100644 --- a/tests/src/test/scala/cats/tests/ValidatedTests.scala +++ b/tests/src/test/scala/cats/tests/ValidatedTests.scala @@ -6,7 +6,6 @@ import cats.data.Validated.{Valid, Invalid} import cats.laws.discipline.{BifunctorTests, TraverseTests, ApplicativeErrorTests, SerializableTests, CartesianTests} import org.scalacheck.Arbitrary._ import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq.tuple3Eq import algebra.laws.{OrderLaws, GroupLaws} import scala.util.Try diff --git a/tests/src/test/scala/cats/tests/VectorTests.scala b/tests/src/test/scala/cats/tests/VectorTests.scala index 8444bb1bb0..cb14ee0313 100644 --- a/tests/src/test/scala/cats/tests/VectorTests.scala +++ b/tests/src/test/scala/cats/tests/VectorTests.scala @@ -2,7 +2,6 @@ package cats package tests import cats.laws.discipline.{MonadCombineTests, CoflatMapTests, SerializableTests, TraverseTests, CartesianTests} -import cats.laws.discipline.eq.tuple3Eq class VectorTests extends CatsSuite { checkAll("Vector[Int]", CartesianTests[Vector].cartesian[Int, Int, Int]) diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index cd0cc07312..9cfae56628 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -4,7 +4,6 @@ package tests import cats.data.{Writer, WriterT} import cats.functor.Bifunctor import cats.laws.discipline._ -import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary._ import algebra.laws.OrderLaws diff --git a/tests/src/test/scala/cats/tests/WriterTests.scala b/tests/src/test/scala/cats/tests/WriterTests.scala index 6b93c1ef72..80cd56aa05 100644 --- a/tests/src/test/scala/cats/tests/WriterTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTests.scala @@ -2,7 +2,6 @@ package cats package tests import cats.data.Writer -import cats.laws.discipline.eq._ class WriterTests extends CatsSuite { test("pure syntax creates a writer with an empty log"){ diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index 7ea4e9b343..d4d2fc4c5b 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -1,12 +1,11 @@ package cats package tests +import algebra.laws.OrderLaws import cats.functor.Bifunctor import cats.data.{Xor, XorT} import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq.tuple3Eq -import algebra.laws.OrderLaws class XorTTests extends CatsSuite { implicit val eq0 = XorT.xorTEq[List, String, String Xor Int] diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index 58d0a90de2..f66704ef5f 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -1,12 +1,11 @@ package cats package tests +import algebra.laws.{GroupLaws, OrderLaws} import cats.data.{NonEmptyList, Xor, XorT} import cats.data.Xor._ import cats.laws.discipline.arbitrary._ import cats.laws.discipline.{BitraverseTests, TraverseTests, MonadErrorTests, SerializableTests, CartesianTests} -import cats.laws.discipline.eq.tuple3Eq -import algebra.laws.{GroupLaws, OrderLaws} import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary._ From f8e3910b9a4d34e867babfaafc027a53279e6c91 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 15:00:55 -0400 Subject: [PATCH 085/185] Work around 2.10 bug with Unit specialization. --- build.sbt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index fbdd6dc3e9..6189512395 100644 --- a/build.sbt +++ b/build.sbt @@ -25,12 +25,11 @@ lazy val catsDoctestSettings = Seq( ) ++ doctestSettings lazy val kernelSettings = Seq( - scalacOptions ++= commonScalacOptions, + scalacOptions ++= commonScalacOptions.filter(_ != "-Ywarn-value-discard"), resolvers ++= Seq( "bintray/non" at "http://dl.bintray.com/non/maven", Resolver.sonatypeRepo("releases"), - Resolver.sonatypeRepo("snapshots") - ), + Resolver.sonatypeRepo("snapshots")), parallelExecution in Test := false, scalacOptions in (Compile, doc) := (scalacOptions in (Compile, doc)).value.filter(_ != "-Xfatal-warnings") ) ++ warnUnusedImport From e6bd8fec9a6118c94058a7756a1d5c319dff86f5 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 15:33:40 -0400 Subject: [PATCH 086/185] Respond to Oscar's review comments. --- core/src/main/scala/cats/std/anyval.scala | 85 ++----------------- .../scala/cats/kernel/laws/GroupLaws.scala | 49 ----------- .../main/scala/cats/kernel/laws/package.scala | 2 +- kernel/src/main/scala/cats/kernel/Group.scala | 15 +++- kernel/src/main/scala/cats/kernel/Order.scala | 23 ++--- .../main/scala/cats/kernel/std/bigInt.scala | 9 ++ .../main/scala/cats/kernel/std/boolean.scala | 2 +- .../src/main/scala/cats/kernel/std/byte.scala | 8 ++ .../main/scala/cats/kernel/std/double.scala | 8 ++ .../main/scala/cats/kernel/std/float.scala | 13 ++- .../src/main/scala/cats/kernel/std/int.scala | 8 ++ .../src/main/scala/cats/kernel/std/long.scala | 10 ++- .../src/main/scala/cats/kernel/std/set.scala | 6 +- .../main/scala/cats/kernel/std/short.scala | 8 ++ .../src/main/scala/cats/kernel/std/unit.scala | 11 ++- 15 files changed, 111 insertions(+), 146 deletions(-) diff --git a/core/src/main/scala/cats/std/anyval.scala b/core/src/main/scala/cats/std/anyval.scala index 41110f5377..708e5ee021 100644 --- a/core/src/main/scala/cats/std/anyval.scala +++ b/core/src/main/scala/cats/std/anyval.scala @@ -1,8 +1,6 @@ package cats package std -import cats.kernel.CommutativeGroup - trait AnyValInstances extends IntInstances with ByteInstances @@ -15,102 +13,37 @@ trait AnyValInstances with UnitInstances trait IntInstances extends cats.kernel.std.IntInstances { - - implicit val intShow: Show[Int] = - Show.fromToString[Int] - - implicit val intGroup: CommutativeGroup[Int] = - new CommutativeGroup[Int] { - def combine(x: Int, y: Int): Int = x + y - def empty: Int = 0 - def inverse(x: Int): Int = -x - override def remove(x: Int, y: Int): Int = x - y - } + implicit val intShow: Show[Int] = Show.fromToString[Int] } trait ByteInstances extends cats.kernel.std.ByteInstances { - - implicit val byteShow: Show[Byte] = - Show.fromToString[Byte] - - implicit val byteGroup: CommutativeGroup[Byte] = - new CommutativeGroup[Byte] { - def combine(x: Byte, y: Byte): Byte = (x + y).toByte - def empty: Byte = 0 - def inverse(x: Byte): Byte = (-x).toByte - override def remove(x: Byte, y: Byte): Byte = (x - y).toByte - } + implicit val byteShow: Show[Byte] = Show.fromToString[Byte] } trait CharInstances extends cats.kernel.std.CharInstances { - implicit val charShow: Show[Char] = - Show.fromToString[Char] + implicit val charShow: Show[Char] = Show.fromToString[Char] } trait ShortInstances extends cats.kernel.std.ShortInstances { - - implicit val shortShow: Show[Short] = - Show.fromToString[Short] - - implicit val shortGroup: CommutativeGroup[Short] = - new CommutativeGroup[Short] { - def combine(x: Short, y: Short): Short = (x + y).toShort - def empty: Short = 0 - def inverse(x: Short): Short = (-x).toShort - override def remove(x: Short, y: Short): Short = (x - y).toShort - } + implicit val shortShow: Show[Short] = Show.fromToString[Short] } trait LongInstances extends cats.kernel.std.LongInstances { - - implicit val longShow: Show[Long] = - Show.fromToString[Long] - - implicit val longGroup: CommutativeGroup[Long] = - new CommutativeGroup[Long] { - def combine(x: Long, y: Long): Long = x + y - def empty: Long = 0L - def inverse(x: Long): Long = -x - override def remove(x: Long, y: Long): Long = x - y - } + implicit val longShow: Show[Long] = Show.fromToString[Long] } trait FloatInstances extends cats.kernel.std.FloatInstances { - - implicit val floatShow: Show[Float] = - Show.fromToString[Float] - - implicit val floatGroup: CommutativeGroup[Float] = - new CommutativeGroup[Float] { - def combine(x: Float, y: Float): Float = x + y - def empty: Float = 0F - def inverse(x: Float): Float = -x - override def remove(x: Float, y: Float): Float = x - y - } + implicit val floatShow: Show[Float] = Show.fromToString[Float] } trait DoubleInstances extends cats.kernel.std.DoubleInstances { - - implicit val doubleShow: Show[Double] = - Show.fromToString[Double] - - implicit val doubleGroup: CommutativeGroup[Double] = - new CommutativeGroup[Double] { - def combine(x: Double, y: Double): Double = x + y - def empty: Double = 0D - def inverse(x: Double): Double = -x - override def remove(x: Double, y: Double): Double = x - y - } + implicit val doubleShow: Show[Double] = Show.fromToString[Double] } trait BooleanInstances extends cats.kernel.std.BooleanInstances { - - implicit val booleanShow: Show[Boolean] = - Show.fromToString[Boolean] + implicit val booleanShow: Show[Boolean] = Show.fromToString[Boolean] } trait UnitInstances extends cats.kernel.std.UnitInstances { - - implicit val unitShow: Show[Unit] = - Show.fromToString[Unit] + implicit val unitShow: Show[Unit] = Show.fromToString[Unit] } diff --git a/kernel-laws/src/main/scala/cats/kernel/laws/GroupLaws.scala b/kernel-laws/src/main/scala/cats/kernel/laws/GroupLaws.scala index f12ed7d2e8..281d853f50 100644 --- a/kernel-laws/src/main/scala/cats/kernel/laws/GroupLaws.scala +++ b/kernel-laws/src/main/scala/cats/kernel/laws/GroupLaws.scala @@ -81,45 +81,6 @@ trait GroupLaws[A] extends Laws { parents = List(group, commutativeMonoid) ) - // // additive groups - // - // def additiveSemigroup(implicit A: AdditiveSemigroup[A]) = new AdditiveProperties( - // base = semigroup(A.additive), - // parents = Nil, - // Rules.serializable(A), - // Rules.repeat1("sumN")(A.sumN), - // Rules.repeat2("sumN", "+")(A.sumN)(A.plus) - // ) - // - // def additiveCommutativeSemigroup(implicit A: AdditiveCommutativeSemigroup[A]) = new AdditiveProperties( - // base = commutativeSemigroup(A.additive), - // parents = List(additiveSemigroup) - // ) - // - // def additiveMonoid(implicit A: AdditiveMonoid[A]) = new AdditiveProperties( - // base = monoid(A.additive), - // parents = List(additiveSemigroup), - // Rules.repeat0("sumN", "zero", A.zero)(A.sumN), - // Rules.collect0("sum", "zero", A.zero)(A.sum) - // ) - // - // def additiveCommutativeMonoid(implicit A: AdditiveCommutativeMonoid[A]) = new AdditiveProperties( - // base = commutativeMonoid(A.additive), - // parents = List(additiveMonoid) - // ) - // - // def additiveGroup(implicit A: AdditiveGroup[A]) = new AdditiveProperties( - // base = group(A.additive), - // parents = List(additiveMonoid), - // Rules.consistentInverse("subtract")(A.minus)(A.plus)(A.negate) - // ) - // - // def additiveCommutativeGroup(implicit A: AdditiveCommutativeGroup[A]) = new AdditiveProperties( - // base = commutativeGroup(A.additive), - // parents = List(additiveGroup) - // ) - - // property classes class GroupProperties( @@ -129,14 +90,4 @@ trait GroupLaws[A] extends Laws { ) extends RuleSet { val bases = Nil } - - // class AdditiveProperties( - // val base: GroupProperties, - // val parents: Seq[AdditiveProperties], - // val props: (String, Prop)* - // ) extends RuleSet { - // val name = base.name - // val bases = List("base" -> base) - // } - } diff --git a/kernel-laws/src/main/scala/cats/kernel/laws/package.scala b/kernel-laws/src/main/scala/cats/kernel/laws/package.scala index 4e576499df..f4c7ed5ca0 100644 --- a/kernel-laws/src/main/scala/cats/kernel/laws/package.scala +++ b/kernel-laws/src/main/scala/cats/kernel/laws/package.scala @@ -8,7 +8,7 @@ import Prop.{False, Proof, Result} package object laws { - implicit def PredicateFromMonoid[A](implicit ev: Eq[A], A: Monoid[A]): Predicate[A] = + implicit def nonEmptyPredicate[A](implicit ev: Eq[A], A: Monoid[A]): Predicate[A] = new Predicate[A] { def apply(a: A) = ev.neqv(a, A.empty) } diff --git a/kernel/src/main/scala/cats/kernel/Group.scala b/kernel/src/main/scala/cats/kernel/Group.scala index 2a3412dbbd..11776f69bd 100644 --- a/kernel/src/main/scala/cats/kernel/Group.scala +++ b/kernel/src/main/scala/cats/kernel/Group.scala @@ -25,11 +25,24 @@ trait Group[@sp(Int, Long, Float, Double) A] extends Any with Monoid[A] { * Return `a` appended to itself `n` times. If `n` is negative, then * this returns `inverse(a)` appended to itself `n` times. */ - override def combineN(a: A, n: Int): A = + override def combineN(a: A, n: Int): A = { + // This method is a bit tricky. Normally, to sum x a negative + // number of times (n), we can sum (-x) a positive number of times + // (-n). The issue here is that Int.MinValue cannot be negated; in + // other words (-MinValue) == MinValue. + // + // To work around that, we rely on the fact that we can divide n + // by 2 if we sum 2a (i.e. combine(a, a)) instead. Here is the + // transformation we use: + // + // combineN(x, -2147483648) + // combineN(combine(x, x), -1073741824) + // combineN(inverse(combine(x, x)), 1073741824) if (n > 0) repeatedCombineN(a, n) else if (n == 0) empty else if (n == Int.MinValue) combineN(inverse(combine(a, a)), 1073741824) else repeatedCombineN(inverse(a), -n) + } } trait GroupFunctions[G[T] <: Group[T]] extends MonoidFunctions[Group] { diff --git a/kernel/src/main/scala/cats/kernel/Order.scala b/kernel/src/main/scala/cats/kernel/Order.scala index 7381684b5c..c3e5a102b0 100644 --- a/kernel/src/main/scala/cats/kernel/Order.scala +++ b/kernel/src/main/scala/cats/kernel/Order.scala @@ -177,13 +177,14 @@ object Order extends OrderFunctions { /** * An `Order` instance that considers all `A` instances to be equal. */ - def allEqual[A]: Order[A] = new Order[A] { - def compare(x: A, y: A): Int = 0 - } + def allEqual[A]: Order[A] = + new Order[A] { + def compare(x: A, y: A): Int = 0 + } /** - * A `Monoid[Order[A]]` can be generated for all `A` with the following + * A `Band[Order[A]]` can be generated for all `A` with the following * properties: * * `empty` returns a trivial `Order[A]` which considers all `A` instances to @@ -194,16 +195,16 @@ object Order extends OrderFunctions { * * @see [[Order.whenEqual]] */ - def whenEqualMonoid[A]: Monoid[Order[A]] = - new Monoid[Order[A]] { + def whenEqualMonoid[A]: Band[Order[A]] = + new Band[Order[A]] { val empty: Order[A] = allEqual[A] - def combine(x: Order[A], y: Order[A]): Order[A] = x whenEqual y } - def fromOrdering[A](implicit ev: Ordering[A]): Order[A] = new Order[A] { - def compare(x: A, y: A): Int = ev.compare(x, y) + def fromOrdering[A](implicit ev: Ordering[A]): Order[A] = + new Order[A] { + def compare(x: A, y: A): Int = ev.compare(x, y) - override def toOrdering: Ordering[A] = ev - } + override def toOrdering: Ordering[A] = ev + } } diff --git a/kernel/src/main/scala/cats/kernel/std/bigInt.scala b/kernel/src/main/scala/cats/kernel/std/bigInt.scala index 13afc51058..f02552af20 100644 --- a/kernel/src/main/scala/cats/kernel/std/bigInt.scala +++ b/kernel/src/main/scala/cats/kernel/std/bigInt.scala @@ -6,6 +6,15 @@ package object bigInt extends BigIntInstances trait BigIntInstances { implicit val bigIntOrder: Order[BigInt] = new BigIntOrder + implicit val bigIntGroup: CommutativeGroup[BigInt] = + new BigIntGroup +} + +class BigIntGroup extends CommutativeGroup[BigInt] { + val empty: BigInt = BigInt(0) + def combine(x: BigInt, y: BigInt): BigInt = x + y + def inverse(x: BigInt): BigInt = -x + override def remove(x: BigInt, y: BigInt): BigInt = x - y } class BigIntOrder extends Order[BigInt] { diff --git a/kernel/src/main/scala/cats/kernel/std/boolean.scala b/kernel/src/main/scala/cats/kernel/std/boolean.scala index 8f83996118..6d3214efc5 100644 --- a/kernel/src/main/scala/cats/kernel/std/boolean.scala +++ b/kernel/src/main/scala/cats/kernel/std/boolean.scala @@ -4,7 +4,7 @@ package std package object boolean extends BooleanInstances trait BooleanInstances { - implicit val booleanOrder: BooleanOrder = + implicit val booleanOrder: Order[Boolean] = new BooleanOrder } diff --git a/kernel/src/main/scala/cats/kernel/std/byte.scala b/kernel/src/main/scala/cats/kernel/std/byte.scala index 0b1a948ff8..d635597c1f 100644 --- a/kernel/src/main/scala/cats/kernel/std/byte.scala +++ b/kernel/src/main/scala/cats/kernel/std/byte.scala @@ -5,6 +5,14 @@ package object byte extends ByteInstances trait ByteInstances { implicit val byteOrder: Order[Byte] = new ByteOrder + implicit val byteGroup: CommutativeGroup[Byte] = new ByteGroup +} + +class ByteGroup extends CommutativeGroup[Byte] { + def combine(x: Byte, y: Byte): Byte = (x + y).toByte + def empty: Byte = 0 + def inverse(x: Byte): Byte = (-x).toByte + override def remove(x: Byte, y: Byte): Byte = (x - y).toByte } class ByteOrder extends Order[Byte] { diff --git a/kernel/src/main/scala/cats/kernel/std/double.scala b/kernel/src/main/scala/cats/kernel/std/double.scala index 32945635c0..215fa96564 100644 --- a/kernel/src/main/scala/cats/kernel/std/double.scala +++ b/kernel/src/main/scala/cats/kernel/std/double.scala @@ -5,6 +5,14 @@ import java.lang.Math trait DoubleInstances { implicit val doubleOrder: Order[Double] = new DoubleOrder + implicit val doubleGroup: CommutativeGroup[Double] = new DoubleGroup +} + +class DoubleGroup extends CommutativeGroup[Double] { + def combine(x: Double, y: Double): Double = x + y + def empty: Double = 0D + def inverse(x: Double): Double = -x + override def remove(x: Double, y: Double): Double = x - y } class DoubleOrder extends Order[Double] { diff --git a/kernel/src/main/scala/cats/kernel/std/float.scala b/kernel/src/main/scala/cats/kernel/std/float.scala index f50e96f1fc..81c72ed5d5 100644 --- a/kernel/src/main/scala/cats/kernel/std/float.scala +++ b/kernel/src/main/scala/cats/kernel/std/float.scala @@ -2,7 +2,18 @@ package cats.kernel package std trait FloatInstances { - implicit val floatOrder = new FloatOrder + implicit val floatOrder: Order[Float] = new FloatOrder + implicit val floatGroup: CommutativeGroup[Float] = new FloatGroup +} + +/** + * This is only approximately associative. + */ +class FloatGroup extends CommutativeGroup[Float] { + def combine(x: Float, y: Float): Float = x + y + def empty: Float = 0F + def inverse(x: Float): Float = -x + override def remove(x: Float, y: Float): Float = x - y } /** diff --git a/kernel/src/main/scala/cats/kernel/std/int.scala b/kernel/src/main/scala/cats/kernel/std/int.scala index 59fb8474ff..7224bb059e 100644 --- a/kernel/src/main/scala/cats/kernel/std/int.scala +++ b/kernel/src/main/scala/cats/kernel/std/int.scala @@ -5,6 +5,14 @@ package object int extends IntInstances trait IntInstances { implicit val intOrder: Order[Int] = new IntOrder + implicit val intGroup: CommutativeGroup[Int] = new IntGroup +} + +class IntGroup extends CommutativeGroup[Int] { + def combine(x: Int, y: Int): Int = x + y + def empty: Int = 0 + def inverse(x: Int): Int = -x + override def remove(x: Int, y: Int): Int = x - y } class IntOrder extends Order[Int] { diff --git a/kernel/src/main/scala/cats/kernel/std/long.scala b/kernel/src/main/scala/cats/kernel/std/long.scala index 8d978062cc..21552b6a6f 100644 --- a/kernel/src/main/scala/cats/kernel/std/long.scala +++ b/kernel/src/main/scala/cats/kernel/std/long.scala @@ -4,7 +4,15 @@ package std package object long extends LongInstances trait LongInstances { - implicit val longOrder = new LongOrder + implicit val longOrder: Order[Long] = new LongOrder + implicit val longGroup: CommutativeGroup[Long] = new LongGroup +} + +class LongGroup extends CommutativeGroup[Long] { + def combine(x: Long, y: Long): Long = x + y + def empty: Long = 0L + def inverse(x: Long): Long = -x + override def remove(x: Long, y: Long): Long = x - y } class LongOrder extends Order[Long] { diff --git a/kernel/src/main/scala/cats/kernel/std/set.scala b/kernel/src/main/scala/cats/kernel/std/set.scala index 844fab2fe5..6f91f97107 100644 --- a/kernel/src/main/scala/cats/kernel/std/set.scala +++ b/kernel/src/main/scala/cats/kernel/std/set.scala @@ -7,8 +7,8 @@ trait SetInstances { implicit def setPartialOrder[A]: PartialOrder[Set[A]] = new SetPartialOrder[A] - implicit def setMonoid[A]: Monoid[Set[A]] = - new SetMonoid[A] + implicit def setSemilattice[A]: BoundedSemilattice[Set[A]] = + new SetSemilattice[A] } class SetPartialOrder[A] extends PartialOrder[Set[A]] { @@ -22,7 +22,7 @@ class SetPartialOrder[A] extends PartialOrder[Set[A]] { x == y } -class SetMonoid[A] extends Monoid[Set[A]] { +class SetSemilattice[A] extends BoundedSemilattice[Set[A]] { def empty: Set[A] = Set.empty def combine(x: Set[A], y: Set[A]): Set[A] = x | y } diff --git a/kernel/src/main/scala/cats/kernel/std/short.scala b/kernel/src/main/scala/cats/kernel/std/short.scala index ba3582205c..531dfae07d 100644 --- a/kernel/src/main/scala/cats/kernel/std/short.scala +++ b/kernel/src/main/scala/cats/kernel/std/short.scala @@ -5,6 +5,14 @@ package object short extends ShortInstances trait ShortInstances { implicit val shortOrder: Order[Short] = new ShortOrder + implicit val shortGroup: CommutativeGroup[Short] = new ShortGroup +} + +class ShortGroup extends CommutativeGroup[Short] { + def combine(x: Short, y: Short): Short = (x + y).toShort + def empty: Short = 0 + def inverse(x: Short): Short = (-x).toShort + override def remove(x: Short, y: Short): Short = (x - y).toShort } class ShortOrder extends Order[Short] { diff --git a/kernel/src/main/scala/cats/kernel/std/unit.scala b/kernel/src/main/scala/cats/kernel/std/unit.scala index 8068b246de..fd0b8b8b1a 100644 --- a/kernel/src/main/scala/cats/kernel/std/unit.scala +++ b/kernel/src/main/scala/cats/kernel/std/unit.scala @@ -4,11 +4,14 @@ package std package object unit extends UnitInstances trait UnitInstances { - implicit val unitAlgebra: Order[Unit] with BoundedSemilattice[Unit] = + implicit val unitOrder: Order[Unit] = + new UnitOrder + + implicit val unitAlgebra: BoundedSemilattice[Unit] with CommutativeGroup[Unit] = new UnitAlgebra } -class UnitAlgebra extends Order[Unit] with BoundedSemilattice[Unit] { +class UnitOrder extends Order[Unit] { def compare(x: Unit, y: Unit): Int = 0 override def eqv(x: Unit, y: Unit): Boolean = true @@ -20,9 +23,13 @@ class UnitAlgebra extends Order[Unit] with BoundedSemilattice[Unit] { override def min(x: Unit, y: Unit): Unit = () override def max(x: Unit, y: Unit): Unit = () +} +class UnitAlgebra extends BoundedSemilattice[Unit] with CommutativeGroup[Unit] { def empty: Unit = () def combine(x: Unit, y: Unit): Unit = () + override def remove(x: Unit, y: Unit): Unit = () + def inverse(x: Unit): Unit = () override protected[this] def repeatedCombineN(a: Unit, n: Int): Unit = () override def combineAllOption(as: TraversableOnce[Unit]): Option[Unit] = if (as.isEmpty) None else Some(()) From 4b8b7895429f9af30c58dd87d08311c0f542a434 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 16:12:27 -0400 Subject: [PATCH 087/185] Remove totally-commented file. --- .../main/scala/cats/kernel/laws/CheckSupport.scala | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 kernel-laws/src/main/scala/cats/kernel/laws/CheckSupport.scala diff --git a/kernel-laws/src/main/scala/cats/kernel/laws/CheckSupport.scala b/kernel-laws/src/main/scala/cats/kernel/laws/CheckSupport.scala deleted file mode 100644 index ad80b28a19..0000000000 --- a/kernel-laws/src/main/scala/cats/kernel/laws/CheckSupport.scala +++ /dev/null @@ -1,12 +0,0 @@ -// package cats.kernel.laws -// -// /** -// * This object contains Arbitrary instances for types defined in -// * cats.kernel.std, as well as anything else we'd like to import to assist -// * in running ScalaCheck tests. -// * -// * (Since cats.kernel-std has no dependencies, its types can't define -// * Arbitrary instances in companions.) -// */ -// object CheckSupport { -// } From ada6b75dfb01d118abb2daacb1ae8dede43c6d98 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 17:30:19 -0400 Subject: [PATCH 088/185] Remove bintray resolver from cats-kernel's SBT config. --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 6189512395..7bd28f36ce 100644 --- a/build.sbt +++ b/build.sbt @@ -25,9 +25,9 @@ lazy val catsDoctestSettings = Seq( ) ++ doctestSettings lazy val kernelSettings = Seq( + // don't warn on value discarding because it's broken on 2.10 with @sp(Unit) scalacOptions ++= commonScalacOptions.filter(_ != "-Ywarn-value-discard"), resolvers ++= Seq( - "bintray/non" at "http://dl.bintray.com/non/maven", Resolver.sonatypeRepo("releases"), Resolver.sonatypeRepo("snapshots")), parallelExecution in Test := false, From 70dd645817c9a1301052a3deb7597dc25d7c607a Mon Sep 17 00:00:00 2001 From: Channing Walton Date: Mon, 25 Apr 2016 22:46:15 +0100 Subject: [PATCH 089/185] =?UTF-8?q?correct=20the=20url=20to=20Data=20types?= =?UTF-8?q?=20=C3=A0=20la=20carte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/src/main/scala/cats/free/Inject.scala | 2 +- docs/src/main/tut/freemonad.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/free/Inject.scala b/core/src/main/scala/cats/free/Inject.scala index 486ab04688..06e7f24dcb 100644 --- a/core/src/main/scala/cats/free/Inject.scala +++ b/core/src/main/scala/cats/free/Inject.scala @@ -7,7 +7,7 @@ import cats.data.Coproduct /** * Inject type class as described in "Data types a la carte" (Swierstra 2008). * - * @see [[http://www.staff.science.uu.nl/~swier004/Publications/DataTypesALaCarte.pdf]] + * @see [[http://www.staff.science.uu.nl/~swier004/publications/2008-jfp.pdf]] */ sealed abstract class Inject[F[_], G[_]] { def inj[A](fa: F[A]): G[A] diff --git a/docs/src/main/tut/freemonad.md b/docs/src/main/tut/freemonad.md index dbd2d1e447..3b63e9e882 100644 --- a/docs/src/main/tut/freemonad.md +++ b/docs/src/main/tut/freemonad.md @@ -292,7 +292,7 @@ val result: (Map[String, Any], Option[Int]) = program.foldMap(pureCompiler).run( ## Composing Free monads ADTs. Real world applications often time combine different algebras. -The `Inject` type class described by Swierstra in [Data types à la carte](http://www.staff.science.uu.nl/~swier004/Publications/DataTypesALaCarte.pdf) +The `Inject` type class described by Swierstra in [Data types à la carte](http://www.staff.science.uu.nl/~swier004/publications/2008-jfp.pdf) lets us compose different algebras in the context of `Free`. Let's see a trivial example of unrelated ADT's getting composed as a `Coproduct` that can form a more complex program. From de8a2fc289940eb60ec569b9c7555b5b4998198e Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 18:34:39 -0400 Subject: [PATCH 090/185] Improve testing, remove lawless instance. This commit fixes some problems which better testing exposed. The biggest was that when we moved from the "sparse vector" interpretation of maps to the "strict" interpretation some of our instances became unlawful. We can't define a group directly on maps because we can't enforce the equivalence of "absent key" and "present key with empty value". --- .../scala/cats/kernel/laws/LawTests.scala | 42 +++++++++++-------- kernel/src/main/scala/cats/kernel/Order.scala | 4 +- .../src/main/scala/cats/kernel/std/Util.scala | 15 +------ .../src/main/scala/cats/kernel/std/all.scala | 6 ++- .../src/main/scala/cats/kernel/std/map.scala | 17 +------- 5 files changed, 34 insertions(+), 50 deletions(-) diff --git a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala index 9064c9b1ef..03ca448319 100644 --- a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala +++ b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala @@ -25,19 +25,19 @@ class LawTests extends FunSuite with Discipline { implicit def orderLaws[A: Eq: Arbitrary] = OrderLaws[A] implicit def groupLaws[A: Eq: Arbitrary] = GroupLaws[A] + laws[OrderLaws, Map[String, HasEq[Int]]].check(_.eqv) laws[OrderLaws, List[HasEq[Int]]].check(_.eqv) - laws[OrderLaws, List[HasEq[String]]].check(_.eqv) laws[OrderLaws, Option[HasEq[Int]]].check(_.eqv) - laws[OrderLaws, Option[HasEq[String]]].check(_.eqv) - laws[OrderLaws, Map[Char, Int]].check(_.eqv) - laws[OrderLaws, Map[Int, BigInt]].check(_.eqv) + laws[OrderLaws, Array[HasEq[Int]]].check(_.eqv) + laws[OrderLaws, Vector[HasEq[Int]]].check(_.eqv) + laws[OrderLaws, Stream[HasEq[Int]]].check(_.eqv) + laws[OrderLaws, Set[Int]].check(_.partialOrder) laws[OrderLaws, Option[HasPartialOrder[Int]]].check(_.partialOrder) - laws[OrderLaws, Option[HasPartialOrder[String]]].check(_.partialOrder) laws[OrderLaws, List[HasPartialOrder[Int]]].check(_.partialOrder) - laws[OrderLaws, List[HasPartialOrder[String]]].check(_.partialOrder) - laws[OrderLaws, Set[Int]].check(_.partialOrder) - laws[OrderLaws, Array[Int]].check(_.partialOrder) + laws[OrderLaws, Array[HasPartialOrder[Int]]].check(_.partialOrder) + laws[OrderLaws, Vector[HasPartialOrder[Int]]].check(_.partialOrder) + laws[OrderLaws, Stream[HasPartialOrder[Int]]].check(_.partialOrder) laws[OrderLaws, Unit].check(_.order) laws[OrderLaws, Boolean].check(_.order) @@ -51,26 +51,33 @@ class LawTests extends FunSuite with Discipline { laws[OrderLaws, Option[String]].check(_.order) laws[OrderLaws, List[String]].check(_.order) laws[OrderLaws, Array[Int]].check(_.order) + laws[OrderLaws, Vector[Int]].check(_.order) + laws[OrderLaws, Stream[Int]].check(_.order) laws[OrderLaws, Int]("fromOrdering").check(_.order(Order.fromOrdering[Int])) - laws[GroupLaws, Int].check(_.monoid) laws[GroupLaws, String].check(_.monoid) laws[GroupLaws, Option[Int]].check(_.monoid) laws[GroupLaws, Option[String]].check(_.monoid) laws[GroupLaws, List[Int]].check(_.monoid) + laws[GroupLaws, Vector[Int]].check(_.monoid) + laws[GroupLaws, Stream[Int]].check(_.monoid) laws[GroupLaws, List[String]].check(_.monoid) + laws[GroupLaws, Map[String, Int]].check(_.monoid) + + laws[GroupLaws, Unit].check(_.commutativeGroup) + laws[GroupLaws, Byte].check(_.commutativeGroup) + laws[GroupLaws, Short].check(_.commutativeGroup) + laws[GroupLaws, Int].check(_.commutativeGroup) + laws[GroupLaws, Long].check(_.commutativeGroup) + //laws[GroupLaws, Float].check(_.commutativeGroup) // approximately associative + //laws[GroupLaws, Double].check(_.commutativeGroup) // approximately associative + laws[GroupLaws, BigInt].check(_.commutativeGroup) laws[GroupLaws, (Int, Int)].check(_.band) + laws[GroupLaws, Unit].check(_.boundedSemilattice) // esoteric machinery follows... - implicit lazy val intGroup: Group[Int] = - new Group[Int] { - def empty: Int = 0 - def combine(x: Int, y: Int): Int = x + y - def inverse(x: Int): Int = -x - } - implicit lazy val band: Band[(Int, Int)] = new Band[(Int, Int)] { def combine(a: (Int, Int), b: (Int, Int)) = (a._1, b._2) @@ -109,8 +116,9 @@ class LawTests extends FunSuite with Discipline { .forall { case (x, y) => a.eqv(x, y) == b.eqv(x, y) } } - implicit val monoidOrderN: Monoid[Order[N]] = Order.whenEqualMonoid[N] + implicit val monoidOrderN = Order.whenEqualMonoid[N] laws[GroupLaws, Order[N]].check(_.monoid) + laws[GroupLaws, Order[N]].check(_.band) { implicit val bsEqN: BoundedSemilattice[Eq[N]] = Eq.allEqualBoundedSemilattice[N] diff --git a/kernel/src/main/scala/cats/kernel/Order.scala b/kernel/src/main/scala/cats/kernel/Order.scala index c3e5a102b0..743920eac9 100644 --- a/kernel/src/main/scala/cats/kernel/Order.scala +++ b/kernel/src/main/scala/cats/kernel/Order.scala @@ -195,8 +195,8 @@ object Order extends OrderFunctions { * * @see [[Order.whenEqual]] */ - def whenEqualMonoid[A]: Band[Order[A]] = - new Band[Order[A]] { + def whenEqualMonoid[A]: Monoid[Order[A]] with Band[Order[A]] = + new Monoid[Order[A]] with Band[Order[A]] { val empty: Order[A] = allEqual[A] def combine(x: Order[A], y: Order[A]): Order[A] = x whenEqual y } diff --git a/kernel/src/main/scala/cats/kernel/std/Util.scala b/kernel/src/main/scala/cats/kernel/std/Util.scala index 5b0859e0da..6974b1b10a 100644 --- a/kernel/src/main/scala/cats/kernel/std/Util.scala +++ b/kernel/src/main/scala/cats/kernel/std/Util.scala @@ -37,19 +37,6 @@ object StaticMethods { wrapMutableMap(m) } - def subtractMap[K, V](x: Map[K, V], y: Map[K, V])(subtract: (V, V) => V)(negate: V => V): Map[K, V] = { - // even if x is smaller, we'd need to call map/foreach on y to - // negate all its values, so this is just as fast or faster. - val m = initMutableMap(x) - y.foreach { case (k, v2) => - m(k) = m.get(k) match { - case Some(v1) => subtract(v1, v2) - case None => negate(v2) - } - } - wrapMutableMap(m) - } - def iteratorCompare[A](xs: Iterator[A], ys: Iterator[A])(implicit ev: Order[A]): Int = { while (true) { if (xs.hasNext) { @@ -90,7 +77,7 @@ object StaticMethods { while (true) { if (xs.hasNext) { if (ys.hasNext) { - if (ev.eqv(xs.next, ys.next)) return true + if (ev.neqv(xs.next, ys.next)) return false } else { return false } diff --git a/kernel/src/main/scala/cats/kernel/std/all.scala b/kernel/src/main/scala/cats/kernel/std/all.scala index 233e7d6c9c..fca7d96228 100644 --- a/kernel/src/main/scala/cats/kernel/std/all.scala +++ b/kernel/src/main/scala/cats/kernel/std/all.scala @@ -4,7 +4,8 @@ package std package object all extends AllInstances trait AllInstances - extends BigIntInstances + extends ArrayInstances + with BigIntInstances with BooleanInstances with ByteInstances with CharInstances @@ -17,7 +18,8 @@ trait AllInstances with OptionInstances with SetInstances with ShortInstances + with StreamInstances with StringInstances with TupleInstances with UnitInstances - with ArrayInstances + with VectorInstances diff --git a/kernel/src/main/scala/cats/kernel/std/map.scala b/kernel/src/main/scala/cats/kernel/std/map.scala index 912b7bab48..4174ed20a9 100644 --- a/kernel/src/main/scala/cats/kernel/std/map.scala +++ b/kernel/src/main/scala/cats/kernel/std/map.scala @@ -1,16 +1,11 @@ package cats.kernel package std -import cats.kernel.std.util.StaticMethods.{ addMap, subtractMap } +import cats.kernel.std.util.StaticMethods.addMap package object map extends MapInstances -trait MapInstances extends MapInstances0 { - implicit def mapGroup[K, V: Group]: MapGroup[K, V] = - new MapGroup[K, V] -} - -trait MapInstances0 { +trait MapInstances { implicit def mapEq[K, V: Eq]: Eq[Map[K, V]] = new MapEq[K, V] implicit def mapMonoid[K, V: Semigroup]: MapMonoid[K, V] = @@ -33,11 +28,3 @@ class MapMonoid[K, V](implicit V: Semigroup[V]) extends Monoid[Map[K, V]] { def combine(x: Map[K, V], y: Map[K, V]): Map[K, V] = addMap(x, y)(V.combine) } - -class MapGroup[K, V](implicit V: Group[V]) extends MapMonoid[K, V] with Group[Map[K, V]] { - def inverse(x: Map[K, V]): Map[K, V] = - x.map { case (k, v) => (k, V.inverse(v)) } - - override def remove(x: Map[K, V], y: Map[K, V]): Map[K, V] = - subtractMap(x, y)(V.remove)(V.inverse) -} From fc0acf2f048eaee0a8be1512c6fb7bb28ca6559f Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Mon, 25 Apr 2016 23:56:40 -0400 Subject: [PATCH 091/185] Optimize eqv/partialCompare/compare using eq where relevant. This is an optimization that we've known about for awhile and that Oscar suggested should be added to some of the instances for Eq, PartialOrder, and Order (those where the cost of doing the check can be linear). --- kernel/src/main/scala/cats/kernel/std/array.scala | 3 +++ kernel/src/main/scala/cats/kernel/std/bigInt.scala | 4 ++-- kernel/src/main/scala/cats/kernel/std/list.scala | 8 ++++---- kernel/src/main/scala/cats/kernel/std/map.scala | 3 ++- kernel/src/main/scala/cats/kernel/std/set.scala | 3 ++- kernel/src/main/scala/cats/kernel/std/stream.scala | 9 ++++++--- kernel/src/main/scala/cats/kernel/std/string.scala | 5 ++++- kernel/src/main/scala/cats/kernel/std/vector.scala | 9 ++++++--- 8 files changed, 29 insertions(+), 15 deletions(-) diff --git a/kernel/src/main/scala/cats/kernel/std/array.scala b/kernel/src/main/scala/cats/kernel/std/array.scala index 1cad7e6185..dc10038d70 100644 --- a/kernel/src/main/scala/cats/kernel/std/array.scala +++ b/kernel/src/main/scala/cats/kernel/std/array.scala @@ -22,6 +22,7 @@ object ArraySupport { else 0 def eqv[@sp A: Eq](x: Array[A], y: Array[A]): Boolean = { + if (x eq y) return true var i = 0 if (x.length != y.length) return false while (i < x.length && i < y.length && Eq.eqv(x(i), y(i))) i += 1 @@ -29,6 +30,7 @@ object ArraySupport { } def compare[@sp A: Order](x: Array[A], y: Array[A]): Int = { + if (x eq y) return 0 var i = 0 while (i < x.length && i < y.length) { val cmp = Order.compare(x(i), y(i)) @@ -39,6 +41,7 @@ object ArraySupport { } def partialCompare[@sp A: PartialOrder](x: Array[A], y: Array[A]): Double = { + if (x eq y) return 0.0 var i = 0 while (i < x.length && i < y.length) { val cmp = PartialOrder.partialCompare(x(i), y(i)) diff --git a/kernel/src/main/scala/cats/kernel/std/bigInt.scala b/kernel/src/main/scala/cats/kernel/std/bigInt.scala index f02552af20..36094a3512 100644 --- a/kernel/src/main/scala/cats/kernel/std/bigInt.scala +++ b/kernel/src/main/scala/cats/kernel/std/bigInt.scala @@ -21,8 +21,8 @@ class BigIntOrder extends Order[BigInt] { def compare(x: BigInt, y: BigInt): Int = x compare y - override def eqv(x:BigInt, y:BigInt): Boolean = x == y - override def neqv(x:BigInt, y:BigInt): Boolean = x != y + override def eqv(x: BigInt, y: BigInt): Boolean = x == y + override def neqv(x: BigInt, y: BigInt): Boolean = x != y override def gt(x: BigInt, y: BigInt): Boolean = x > y override def gteqv(x: BigInt, y: BigInt): Boolean = x >= y override def lt(x: BigInt, y: BigInt): Boolean = x < y diff --git a/kernel/src/main/scala/cats/kernel/std/list.scala b/kernel/src/main/scala/cats/kernel/std/list.scala index ee717ac78e..3baeb84b58 100644 --- a/kernel/src/main/scala/cats/kernel/std/list.scala +++ b/kernel/src/main/scala/cats/kernel/std/list.scala @@ -33,7 +33,7 @@ class ListOrder[A](implicit ev: Order[A]) extends Order[List[A]] { if (n != 0) n else loop(xs, ys) } } - loop(xs, ys) + if (xs eq ys) 0 else loop(xs, ys) } } @@ -51,12 +51,12 @@ class ListPartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[Lis if (n != 0.0) n else loop(xs, ys) } } - loop(xs, ys) + if (xs eq ys) 0.0 else loop(xs, ys) } } class ListEq[A](implicit ev: Eq[A]) extends Eq[List[A]] { - def eqv(x: List[A], y: List[A]): Boolean = { + def eqv(xs: List[A], ys: List[A]): Boolean = { def loop(xs: List[A], ys: List[A]): Boolean = xs match { case Nil => @@ -69,7 +69,7 @@ class ListEq[A](implicit ev: Eq[A]) extends Eq[List[A]] { false } } - loop(x, y) + (xs eq ys) || loop(xs, ys) } } diff --git a/kernel/src/main/scala/cats/kernel/std/map.scala b/kernel/src/main/scala/cats/kernel/std/map.scala index 4174ed20a9..61c8f65625 100644 --- a/kernel/src/main/scala/cats/kernel/std/map.scala +++ b/kernel/src/main/scala/cats/kernel/std/map.scala @@ -14,7 +14,8 @@ trait MapInstances { class MapEq[K, V](implicit V: Eq[V]) extends Eq[Map[K, V]] { def eqv(x: Map[K, V], y: Map[K, V]): Boolean = - x.size == y.size && x.forall { case (k, v1) => + if (x eq y) true + else x.size == y.size && x.forall { case (k, v1) => y.get(k) match { case Some(v2) => V.eqv(v1, v2) case None => false diff --git a/kernel/src/main/scala/cats/kernel/std/set.scala b/kernel/src/main/scala/cats/kernel/std/set.scala index 6f91f97107..ae84e1d09d 100644 --- a/kernel/src/main/scala/cats/kernel/std/set.scala +++ b/kernel/src/main/scala/cats/kernel/std/set.scala @@ -13,7 +13,8 @@ trait SetInstances { class SetPartialOrder[A] extends PartialOrder[Set[A]] { def partialCompare(x: Set[A], y: Set[A]): Double = - if (x.size < y.size) if (x.subsetOf(y)) -1.0 else Double.NaN + if (x eq y) 0.0 + else if (x.size < y.size) if (x.subsetOf(y)) -1.0 else Double.NaN else if (y.size < x.size) -partialCompare(y, x) else if (x == y) 0.0 else Double.NaN diff --git a/kernel/src/main/scala/cats/kernel/std/stream.scala b/kernel/src/main/scala/cats/kernel/std/stream.scala index 899cc58d72..f924c786d3 100644 --- a/kernel/src/main/scala/cats/kernel/std/stream.scala +++ b/kernel/src/main/scala/cats/kernel/std/stream.scala @@ -20,17 +20,20 @@ trait StreamInstances2 { class StreamOrder[A](implicit ev: Order[A]) extends Order[Stream[A]] { def compare(xs: Stream[A], ys: Stream[A]): Int = - StaticMethods.iteratorCompare(xs.iterator, ys.iterator) + if (xs eq ys) 0 + else StaticMethods.iteratorCompare(xs.iterator, ys.iterator) } class StreamPartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[Stream[A]] { def partialCompare(xs: Stream[A], ys: Stream[A]): Double = - StaticMethods.iteratorPartialCompare(xs.iterator, ys.iterator) + if (xs eq ys) 0.0 + else StaticMethods.iteratorPartialCompare(xs.iterator, ys.iterator) } class StreamEq[A](implicit ev: Eq[A]) extends Eq[Stream[A]] { def eqv(xs: Stream[A], ys: Stream[A]): Boolean = - StaticMethods.iteratorEq(xs.iterator, ys.iterator) + if (xs eq ys) true + else StaticMethods.iteratorEq(xs.iterator, ys.iterator) } class StreamMonoid[A] extends Monoid[Stream[A]] { diff --git a/kernel/src/main/scala/cats/kernel/std/string.scala b/kernel/src/main/scala/cats/kernel/std/string.scala index 52ac8de129..b7394a3086 100644 --- a/kernel/src/main/scala/cats/kernel/std/string.scala +++ b/kernel/src/main/scala/cats/kernel/std/string.scala @@ -9,7 +9,10 @@ trait StringInstances { } class StringOrder extends Order[String] { - def compare(x: String, y: String): Int = x compare y + override def eqv(x: String, y: String): Boolean = + x == y + def compare(x: String, y: String): Int = + if (x eq y) 0 else x compareTo y } class StringMonoid extends Monoid[String] { diff --git a/kernel/src/main/scala/cats/kernel/std/vector.scala b/kernel/src/main/scala/cats/kernel/std/vector.scala index 50c13c5174..3bb99381a8 100644 --- a/kernel/src/main/scala/cats/kernel/std/vector.scala +++ b/kernel/src/main/scala/cats/kernel/std/vector.scala @@ -20,17 +20,20 @@ trait VectorInstances2 { class VectorOrder[A](implicit ev: Order[A]) extends Order[Vector[A]] { def compare(xs: Vector[A], ys: Vector[A]): Int = - StaticMethods.iteratorCompare(xs.iterator, ys.iterator) + if (xs eq ys) 0 + else StaticMethods.iteratorCompare(xs.iterator, ys.iterator) } class VectorPartialOrder[A](implicit ev: PartialOrder[A]) extends PartialOrder[Vector[A]] { def partialCompare(xs: Vector[A], ys: Vector[A]): Double = - StaticMethods.iteratorPartialCompare(xs.iterator, ys.iterator) + if (xs eq ys) 0.0 + else StaticMethods.iteratorPartialCompare(xs.iterator, ys.iterator) } class VectorEq[A](implicit ev: Eq[A]) extends Eq[Vector[A]] { def eqv(xs: Vector[A], ys: Vector[A]): Boolean = - StaticMethods.iteratorEq(xs.iterator, ys.iterator) + if (xs eq ys) true + else StaticMethods.iteratorEq(xs.iterator, ys.iterator) } class VectorMonoid[A] extends Monoid[Vector[A]] { From e66d23894ed022716ea8db2300541c44feb66a4b Mon Sep 17 00:00:00 2001 From: aaron levin Date: Tue, 26 Apr 2016 09:25:00 +0200 Subject: [PATCH 092/185] XorT: remove MonoidK inst. + new SemigroupK inst. We remove the MonoidK instance as it depended on an not-well defined SemigroupK instance. The new SemigroupK instance places no constraints on `L`, and has a "first Right or Last Left" behaviour for combine. This has a closer semantic meaning to Xor(T)'s "success or failure" interpretation. It didn't make sense previously that the empty value for XortT's MonoidK instance represented failure (`XortT.left(F.pure(L.empty))(F)`). Why should every empty value signal failure? --- core/src/main/scala/cats/data/XorT.scala | 34 ++++--------------- .../src/test/scala/cats/tests/XorTTests.scala | 2 -- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index 4e0331f24a..97b4cae357 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -215,16 +215,6 @@ private[data] abstract class XorTInstances1 extends XorTInstances2 { } */ - /* TODO delete this when MonadCombine instance is re-enabled */ - implicit def xorTMonoidK[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonoidK[XorT[F, L, ?]] = { - implicit val F0 = F - implicit val L0 = L - new MonoidK[XorT[F, L, ?]] with XorTSemigroupK[F, L] { - implicit val F = F0; implicit val L = L0 - def empty[A]: XorT[F, L, A] = XorT.left(F.pure(L.empty))(F) - } - } - implicit def xorTFoldable[F[_], L](implicit F: Foldable[F]): Foldable[XorT[F, L, ?]] = new XorTFoldable[F, L] { val F0: Foldable[F] = F @@ -242,10 +232,13 @@ private[data] abstract class XorTInstances2 extends XorTInstances3 { new XorTMonadError[F, L] { implicit val F = F0 } } - implicit def xorTSemigroupK[F[_], L](implicit F: Monad[F], L: Semigroup[L]): SemigroupK[XorT[F, L, ?]] = { - implicit val F0 = F - implicit val L0 = L - new XorTSemigroupK[F, L] { implicit val F = F0; implicit val L = L0 } + implicit def xorTSemigroupK[F[_], L](implicit F: Monad[F]): SemigroupK[XorT[F, L, ?]] = + new SemigroupK[XorT[F,L,?]] { + def combineK[A](x: XorT[F,L,A], y: XorT[F, L, A]): XorT[F, L, A] = + XorT(F.flatMap(x.value) { + case l @ Xor.Left(_) => y.value + case r @ Xor.Right(_) => F.pure(r) + }) } implicit def xorTEq[F[_], L, R](implicit F: Eq[F[L Xor R]]): Eq[XorT[F, L, R]] = @@ -288,19 +281,6 @@ private[data] trait XorTMonadError[F[_], L] extends MonadError[XorT[F, L, ?], L] fla.recoverWith(pf) } -private[data] trait XorTSemigroupK[F[_], L] extends SemigroupK[XorT[F, L, ?]] { - implicit val F: Monad[F] - implicit val L: Semigroup[L] - def combineK[A](x: XorT[F, L, A], y: XorT[F, L, A]): XorT[F, L, A] = - XorT(F.flatMap(x.value) { - case Xor.Left(l1) => F.map(y.value) { - case Xor.Left(l2) => Xor.Left(L.combine(l1, l2)) - case r @ Xor.Right(_) => r - } - case r @ Xor.Right(_) => F.pure[L Xor A](r) - }) -} - private[data] trait XorTMonadFilter[F[_], L] extends MonadFilter[XorT[F, L, ?]] with XorTMonadError[F, L] { implicit val F: Monad[F] implicit val L: Monoid[L] diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index 7ea4e9b343..44b1cb2bf5 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -14,8 +14,6 @@ class XorTTests extends CatsSuite { implicit val iso = CartesianTests.Isomorphisms.invariant[XorT[List, String, ?]] checkAll("XorT[List, String, Int]", MonadErrorTests[XorT[List, String, ?], String].monadError[Int, Int, Int]) checkAll("MonadError[XorT[List, ?, ?]]", SerializableTests.serializable(MonadError[XorT[List, String, ?], String])) - checkAll("XorT[List, String, Int]", MonoidKTests[XorT[List, String, ?]].monoidK[Int]) - checkAll("MonoidK[XorT[List, String, ?]]", SerializableTests.serializable(MonoidK[XorT[List, String, ?]])) checkAll("XorT[List, ?, ?]", BifunctorTests[XorT[List, ?, ?]].bifunctor[Int, Int, Int, String, String, String]) checkAll("Bifunctor[XorT[List, ?, ?]]", SerializableTests.serializable(Bifunctor[XorT[List, ?, ?]])) checkAll("XorT[List, Int, ?]", TraverseTests[XorT[List, Int, ?]].traverse[Int, Int, Int, Int, Option, Option]) From 0bae0caa9472248eef18689b25ee89daf593058d Mon Sep 17 00:00:00 2001 From: aaron levin Date: Tue, 26 Apr 2016 09:28:40 +0200 Subject: [PATCH 093/185] Xor: SemigroupK w/ first-right,last-left semantics Update Xor's SemigroupK instance to be aligned with XorT's, with first-right, last-left semnatics. --- core/src/main/scala/cats/data/Xor.scala | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index c43a0d9cdf..16c64ce214 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -168,14 +168,11 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { def combine(x: A Xor B, y: A Xor B): A Xor B = x combine y } - implicit def xorSemigroupK[L](implicit ev: Semigroup[L]): SemigroupK[Xor[L,?]] = + implicit def xorSemigroupK[L]: SemigroupK[Xor[L,?]] = new SemigroupK[Xor[L,?]] { def combineK[A](x: Xor[L,A], y: Xor[L,A]): Xor[L,A] = x match { - case Xor.Left(lx) => y match { - case Xor.Left(ly) => Xor.Left(ev.combine(lx,ly)) - case r @ Xor.Right(_) => r - } - case _ => x + case Xor.Left(_) => y + case Xor.Right(_) => x } } From 86363437cc150c02fbb2289f233c3f79c46b5a2f Mon Sep 17 00:00:00 2001 From: aaron levin Date: Tue, 26 Apr 2016 13:24:05 +0200 Subject: [PATCH 094/185] Remove implct Semigroup from Xor{T} SemigroupK tests --- tests/src/test/scala/cats/tests/XorTTests.scala | 1 - tests/src/test/scala/cats/tests/XorTests.scala | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index 44b1cb2bf5..f8a736e7a6 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -46,7 +46,6 @@ class XorTTests extends CatsSuite { } { - implicit val L = ListWrapper.semigroup[String] checkAll("XorT[Option, ListWrapper[String], ?]", SemigroupKTests[XorT[Option, ListWrapper[String], ?]].semigroupK[Int]) checkAll("SemigroupK[XorT[Option, ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[XorT[Option, ListWrapper[String], ?]])) } diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index 39ca90a79d..12899f257a 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -48,7 +48,6 @@ class XorTests extends CatsSuite { } { - implicit val L = ListWrapper.semigroup[String] checkAll("Xor[ListWrapper[String], ?]", SemigroupKTests[Xor[ListWrapper[String], ?]].semigroupK[Int]) checkAll("SemigroupK[Xor[ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[Xor[ListWrapper[String], ?]])) } From db83772ef4a5df9e6a96893192ffa95085441fbb Mon Sep 17 00:00:00 2001 From: aaron levin Date: Tue, 26 Apr 2016 13:27:32 +0200 Subject: [PATCH 095/185] Move Xor{T} SemigroupK tests into broader scope --- tests/src/test/scala/cats/tests/XorTTests.scala | 7 ++----- tests/src/test/scala/cats/tests/XorTests.scala | 8 +++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index f8a736e7a6..4e5ed83151 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -20,6 +20,8 @@ class XorTTests extends CatsSuite { checkAll("Traverse[XorT[List, Int, ?]]", SerializableTests.serializable(Traverse[XorT[List, Int, ?]])) checkAll("XorT[List, String, Int]", OrderLaws[XorT[List, String, Int]].order) checkAll("Order[XorT[List, String, Int]]", SerializableTests.serializable(Order[XorT[List, String, Int]])) + checkAll("XorT[Option, ListWrapper[String], ?]", SemigroupKTests[XorT[Option, ListWrapper[String], ?]].semigroupK[Int]) + checkAll("SemigroupK[XorT[Option, ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[XorT[Option, ListWrapper[String], ?]])) { implicit val F = ListWrapper.foldable @@ -45,11 +47,6 @@ class XorTTests extends CatsSuite { checkAll("Eq[XorT[ListWrapper, String, Int]]", SerializableTests.serializable(Eq[XorT[ListWrapper, String, Int]])) } - { - checkAll("XorT[Option, ListWrapper[String], ?]", SemigroupKTests[XorT[Option, ListWrapper[String], ?]].semigroupK[Int]) - checkAll("SemigroupK[XorT[Option, ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[XorT[Option, ListWrapper[String], ?]])) - } - // make sure that the Monad and Traverse instances don't result in ambiguous // Functor instances Functor[XorT[List, Int, ?]] diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index 12899f257a..d05830233c 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -33,6 +33,9 @@ class XorTests extends CatsSuite { checkAll("Xor[Int, String]", OrderLaws[String Xor Int].order) + checkAll("Xor[ListWrapper[String], ?]", SemigroupKTests[Xor[ListWrapper[String], ?]].semigroupK[Int]) + checkAll("SemigroupK[Xor[ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[Xor[ListWrapper[String], ?]])) + { implicit val S = ListWrapper.partialOrder[String] implicit val I = ListWrapper.partialOrder[Int] @@ -47,11 +50,6 @@ class XorTests extends CatsSuite { checkAll("Eq[ListWrapper[String] Xor ListWrapper[Int]]", SerializableTests.serializable(Eq[ListWrapper[String] Xor ListWrapper[Int]])) } - { - checkAll("Xor[ListWrapper[String], ?]", SemigroupKTests[Xor[ListWrapper[String], ?]].semigroupK[Int]) - checkAll("SemigroupK[Xor[ListWrapper[String], ?]]", SerializableTests.serializable(SemigroupK[Xor[ListWrapper[String], ?]])) - } - implicit val arbitraryXor: Arbitrary[Xor[Int, String]] = Arbitrary { for { left <- arbitrary[Boolean] From 9b588acd99c17d0de91142232358f3e273dee114 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Tue, 26 Apr 2016 09:59:16 -0400 Subject: [PATCH 096/185] updated release notes --- CHANGES.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index e206a0b093..01756dee9c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -28,6 +28,8 @@ And additions: * [#947](https://github.com/typelevel/cats/pull/947): Syntax for `ApplicativeError` * [#971](https://github.com/typelevel/cats/pull/971): Add `toValidatedNel` to `Xor` * [#973](https://github.com/typelevel/cats/pull/973): Add `flatMapF` for `StateT` +* [#985](https://github.com/typelevel/cats/pull/985): Add object `reducible` for reducible syntax +* [#994](https://github.com/typelevel/cats/pull/994): updated to latest algebra (brought in all the new goodies) And bug fixes: @@ -66,7 +68,8 @@ And some other improvements to the documentation, tutorials, laws and tests, inc * [#964](https://github.com/typelevel/cats/pull/964): Clarify stabilty guarantees; drop 'proof of concept' and 'experimental' * [#972](https://github.com/typelevel/cats/pull/972): Fix swapped f and g in `invariant` docs * [#979](https://github.com/typelevel/cats/pull/979): Fix outdated import for `cats.syntax.apply._` - +* [#995](https://github.com/typelevel/cats/pull/995): Move coverage away from bash +* [#1002](https://github.com/typelevel/cats/pull/1002): correct the url to Data types à la carte As always thanks to everyone who filed issues, participated in the Cats Gitter channel, submitted code, or helped review pull requests. From 5f408e13e076e105e1ba08acbced4cb0678dfc89 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Tue, 26 Apr 2016 10:59:17 -0400 Subject: [PATCH 097/185] Address some of Cody's review comments. This does the following: 1. Fixes a lot of return types. 2. Removes cats.kernel.std.array 3. Removes the unused cats.kernel.Priority 4. Improves some comments 5. Makes a small efficiency improvement for Monoid[Map[K, V]] --- .../scala/cats/kernel/laws/LawTests.scala | 3 - .../scala/cats/kernel/CommutativeGroup.scala | 4 +- kernel/src/main/scala/cats/kernel/Order.scala | 5 +- .../src/main/scala/cats/kernel/Priority.scala | 65 ----------------- .../src/main/scala/cats/kernel/std/Util.scala | 10 +-- .../src/main/scala/cats/kernel/std/all.scala | 3 +- .../main/scala/cats/kernel/std/array.scala | 73 ------------------- .../src/main/scala/cats/kernel/std/list.scala | 12 ++- .../src/main/scala/cats/kernel/std/map.scala | 10 ++- .../main/scala/cats/kernel/std/option.scala | 12 ++- .../main/scala/cats/kernel/std/stream.scala | 12 ++- .../main/scala/cats/kernel/std/string.scala | 2 +- .../main/scala/cats/kernel/std/vector.scala | 12 ++- 13 files changed, 51 insertions(+), 172 deletions(-) delete mode 100644 kernel/src/main/scala/cats/kernel/Priority.scala delete mode 100644 kernel/src/main/scala/cats/kernel/std/array.scala diff --git a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala index 03ca448319..d504f99114 100644 --- a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala +++ b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala @@ -28,14 +28,12 @@ class LawTests extends FunSuite with Discipline { laws[OrderLaws, Map[String, HasEq[Int]]].check(_.eqv) laws[OrderLaws, List[HasEq[Int]]].check(_.eqv) laws[OrderLaws, Option[HasEq[Int]]].check(_.eqv) - laws[OrderLaws, Array[HasEq[Int]]].check(_.eqv) laws[OrderLaws, Vector[HasEq[Int]]].check(_.eqv) laws[OrderLaws, Stream[HasEq[Int]]].check(_.eqv) laws[OrderLaws, Set[Int]].check(_.partialOrder) laws[OrderLaws, Option[HasPartialOrder[Int]]].check(_.partialOrder) laws[OrderLaws, List[HasPartialOrder[Int]]].check(_.partialOrder) - laws[OrderLaws, Array[HasPartialOrder[Int]]].check(_.partialOrder) laws[OrderLaws, Vector[HasPartialOrder[Int]]].check(_.partialOrder) laws[OrderLaws, Stream[HasPartialOrder[Int]]].check(_.partialOrder) @@ -50,7 +48,6 @@ class LawTests extends FunSuite with Discipline { laws[OrderLaws, List[Int]].check(_.order) laws[OrderLaws, Option[String]].check(_.order) laws[OrderLaws, List[String]].check(_.order) - laws[OrderLaws, Array[Int]].check(_.order) laws[OrderLaws, Vector[Int]].check(_.order) laws[OrderLaws, Stream[Int]].check(_.order) laws[OrderLaws, Int]("fromOrdering").check(_.order(Order.fromOrdering[Int])) diff --git a/kernel/src/main/scala/cats/kernel/CommutativeGroup.scala b/kernel/src/main/scala/cats/kernel/CommutativeGroup.scala index 6072cecd96..80a5c6fa8c 100644 --- a/kernel/src/main/scala/cats/kernel/CommutativeGroup.scala +++ b/kernel/src/main/scala/cats/kernel/CommutativeGroup.scala @@ -3,7 +3,8 @@ package cats.kernel import scala.{ specialized => sp } /** - * An abelian group is a group whose operation is commutative. + * An commutative group (also known as an abelian group) is a group + * whose combine operation is commutative. */ trait CommutativeGroup[@sp(Int, Long, Float, Double) A] extends Any with Group[A] with CommutativeMonoid[A] @@ -14,4 +15,3 @@ object CommutativeGroup extends GroupFunctions[CommutativeGroup] { */ @inline final def apply[A](implicit ev: CommutativeGroup[A]): CommutativeGroup[A] = ev } - diff --git a/kernel/src/main/scala/cats/kernel/Order.scala b/kernel/src/main/scala/cats/kernel/Order.scala index 743920eac9..53fcf6a931 100644 --- a/kernel/src/main/scala/cats/kernel/Order.scala +++ b/kernel/src/main/scala/cats/kernel/Order.scala @@ -184,7 +184,7 @@ object Order extends OrderFunctions { /** - * A `Band[Order[A]]` can be generated for all `A` with the following + * A `Monoid[Order[A]]` can be generated for all `A` with the following * properties: * * `empty` returns a trivial `Order[A]` which considers all `A` instances to @@ -193,6 +193,9 @@ object Order extends OrderFunctions { * `combine(x: Order[A], y: Order[A])` creates an `Order[A]` that first * orders by `x` and then (if two elements are equal) falls back to `y`. * + * This monoid is also a `Band[Order[A]]` since its combine + * operations is idempotent. + * * @see [[Order.whenEqual]] */ def whenEqualMonoid[A]: Monoid[Order[A]] with Band[Order[A]] = diff --git a/kernel/src/main/scala/cats/kernel/Priority.scala b/kernel/src/main/scala/cats/kernel/Priority.scala deleted file mode 100644 index a9021c5c83..0000000000 --- a/kernel/src/main/scala/cats/kernel/Priority.scala +++ /dev/null @@ -1,65 +0,0 @@ -package cats.kernel - -/** - * Priority is a type class for prioritized implicit search. - * - * This type class will attempt to provide an implicit instance of `P` - * (the preferred type). If that type is not available it will - * fallback to `F` (the fallback type). If neither type is available - * then a `Priority[P, F]` instance will not be available. - * - * This type can be useful for problems where multiple algorithms can - * be used, depending on the type classes available. - */ -sealed trait Priority[+P, +F] { - - import Priority.{Preferred, Fallback} - - def fold[B](f1: P => B)(f2: F => B): B = - this match { - case Preferred(x) => f1(x) - case Fallback(y) => f2(y) - } - - def join[U >: P with F]: U = - fold(_.asInstanceOf[U])(_.asInstanceOf[U]) - - def bimap[P2, F2](f1: P => P2)(f2: F => F2): Priority[P2, F2] = - this match { - case Preferred(x) => Preferred(f1(x)) - case Fallback(y) => Fallback(f2(y)) - } - - def toEither: Either[P, F] = - fold[Either[P, F]](p => Left(p))(f => Right(f)) - - def isPreferred: Boolean = - fold(_ => true)(_ => false) - - def isFallback: Boolean = - fold(_ => false)(_ => true) - - def getPreferred: Option[P] = - fold[Option[P]](p => Some(p))(_ => None) - - def getFallback: Option[F] = - fold[Option[F]](_ => None)(f => Some(f)) -} - -object Priority extends FindPreferred { - - case class Preferred[P](get: P) extends Priority[P, Nothing] - case class Fallback[F](get: F) extends Priority[Nothing, F] - - def apply[P, F](implicit ev: Priority[P, F]): Priority[P, F] = ev -} - -private[kernel] trait FindPreferred extends FindFallback { - implicit def preferred[P](implicit ev: P): Priority[P, Nothing] = - Priority.Preferred(ev) -} - -private[kernel] trait FindFallback { - implicit def fallback[F](implicit ev: F): Priority[Nothing, F] = - Priority.Fallback(ev) -} diff --git a/kernel/src/main/scala/cats/kernel/std/Util.scala b/kernel/src/main/scala/cats/kernel/std/Util.scala index 6974b1b10a..50847f10f7 100644 --- a/kernel/src/main/scala/cats/kernel/std/Util.scala +++ b/kernel/src/main/scala/cats/kernel/std/Util.scala @@ -22,15 +22,13 @@ object StaticMethods { def -(key: K): Map[K, V] = m.toMap - key } - def addMap[K, V](x: Map[K, V], y: Map[K, V])(f: (V, V) => V): Map[K, V] = { - val (small, big, g) = - if (x.size <= y.size) (x, y, f) - else (y, x, (v1: V, v2: V) => f(v2, v1)) - + // the caller should arrange so that the smaller map is the first + // argument, and the larger map is the second. + def addMap[K, V](small: Map[K, V], big: Map[K, V])(f: (V, V) => V): Map[K, V] = { val m = initMutableMap(big) small.foreach { case (k, v1) => m(k) = m.get(k) match { - case Some(v2) => g(v1, v2) + case Some(v2) => f(v1, v2) case None => v1 } } diff --git a/kernel/src/main/scala/cats/kernel/std/all.scala b/kernel/src/main/scala/cats/kernel/std/all.scala index fca7d96228..a7c1528958 100644 --- a/kernel/src/main/scala/cats/kernel/std/all.scala +++ b/kernel/src/main/scala/cats/kernel/std/all.scala @@ -4,8 +4,7 @@ package std package object all extends AllInstances trait AllInstances - extends ArrayInstances - with BigIntInstances + extends BigIntInstances with BooleanInstances with ByteInstances with CharInstances diff --git a/kernel/src/main/scala/cats/kernel/std/array.scala b/kernel/src/main/scala/cats/kernel/std/array.scala deleted file mode 100644 index dc10038d70..0000000000 --- a/kernel/src/main/scala/cats/kernel/std/array.scala +++ /dev/null @@ -1,73 +0,0 @@ -package cats.kernel -package std - -import scala.{ specialized => sp } - -package object array extends ArrayInstances - -trait ArrayInstances { - implicit def arrayEq[@sp A: Eq]: Eq[Array[A]] = - new ArrayEq[A] - implicit def arrayOrder[@sp A: Order]: Order[Array[A]] = - new ArrayOrder[A] - implicit def arrayPartialOrder[@sp A: PartialOrder]: PartialOrder[Array[A]] = - new ArrayPartialOrder[A] -} - -object ArraySupport { - - private def signum(x: Int): Int = - if(x < 0) -1 - else if(x > 0) 1 - else 0 - - def eqv[@sp A: Eq](x: Array[A], y: Array[A]): Boolean = { - if (x eq y) return true - var i = 0 - if (x.length != y.length) return false - while (i < x.length && i < y.length && Eq.eqv(x(i), y(i))) i += 1 - i == x.length - } - - def compare[@sp A: Order](x: Array[A], y: Array[A]): Int = { - if (x eq y) return 0 - var i = 0 - while (i < x.length && i < y.length) { - val cmp = Order.compare(x(i), y(i)) - if (cmp != 0) return cmp - i += 1 - } - signum(x.length - y.length) - } - - def partialCompare[@sp A: PartialOrder](x: Array[A], y: Array[A]): Double = { - if (x eq y) return 0.0 - var i = 0 - while (i < x.length && i < y.length) { - val cmp = PartialOrder.partialCompare(x(i), y(i)) - // Double.NaN is also != 0.0 - if (cmp != 0.0) return cmp - i += 1 - } - signum(x.length - y.length).toDouble - } -} - -final class ArrayEq[@sp A: Eq] extends Eq[Array[A]] { - def eqv(x: Array[A], y: Array[A]): Boolean = - ArraySupport.eqv(x, y) -} - -final class ArrayOrder[@sp A: Order] extends Order[Array[A]] { - override def eqv(x: Array[A], y: Array[A]): Boolean = - ArraySupport.eqv(x, y) - def compare(x: Array[A], y: Array[A]): Int = - ArraySupport.compare(x, y) -} - -final class ArrayPartialOrder[@sp A: PartialOrder] extends PartialOrder[Array[A]] { - override def eqv(x: Array[A], y: Array[A]): Boolean = - ArraySupport.eqv(x, y) - override def partialCompare(x: Array[A], y: Array[A]): Double = - ArraySupport.partialCompare(x, y) -} diff --git a/kernel/src/main/scala/cats/kernel/std/list.scala b/kernel/src/main/scala/cats/kernel/std/list.scala index 3baeb84b58..056e4f36f2 100644 --- a/kernel/src/main/scala/cats/kernel/std/list.scala +++ b/kernel/src/main/scala/cats/kernel/std/list.scala @@ -7,16 +7,20 @@ import scala.collection.mutable package object list extends ListInstances trait ListInstances extends ListInstances1 { - implicit def listOrder[A: Order] = new ListOrder[A] - implicit def listMonoid[A] = new ListMonoid[A] + implicit def listOrder[A: Order]: Order[List[A]] = + new ListOrder[A] + implicit def listMonoid[A]: Monoid[List[A]] = + new ListMonoid[A] } trait ListInstances1 extends ListInstances2 { - implicit def listPartialOrder[A: PartialOrder] = new ListPartialOrder[A] + implicit def listPartialOrder[A: PartialOrder]: PartialOrder[List[A]] = + new ListPartialOrder[A] } trait ListInstances2 { - implicit def listEq[A: Eq] = new ListEq[A] + implicit def listEq[A: Eq]: Eq[List[A]] = + new ListEq[A] } class ListOrder[A](implicit ev: Order[A]) extends Order[List[A]] { diff --git a/kernel/src/main/scala/cats/kernel/std/map.scala b/kernel/src/main/scala/cats/kernel/std/map.scala index 61c8f65625..3743be1518 100644 --- a/kernel/src/main/scala/cats/kernel/std/map.scala +++ b/kernel/src/main/scala/cats/kernel/std/map.scala @@ -8,7 +8,7 @@ package object map extends MapInstances trait MapInstances { implicit def mapEq[K, V: Eq]: Eq[Map[K, V]] = new MapEq[K, V] - implicit def mapMonoid[K, V: Semigroup]: MapMonoid[K, V] = + implicit def mapMonoid[K, V: Semigroup]: Monoid[Map[K, V]] = new MapMonoid[K, V] } @@ -26,6 +26,10 @@ class MapEq[K, V](implicit V: Eq[V]) extends Eq[Map[K, V]] { class MapMonoid[K, V](implicit V: Semigroup[V]) extends Monoid[Map[K, V]] { def empty: Map[K, V] = Map.empty - def combine(x: Map[K, V], y: Map[K, V]): Map[K, V] = - addMap(x, y)(V.combine) + def combine(xs: Map[K, V], ys: Map[K, V]): Map[K, V] = + if (xs.size <= ys.size) { + addMap(xs, ys)((x, y) => V.combine(x, y)) + } else { + addMap(ys, xs)((y, x) => V.combine(x, y)) + } } diff --git a/kernel/src/main/scala/cats/kernel/std/option.scala b/kernel/src/main/scala/cats/kernel/std/option.scala index db0c8a7126..4a6fcf36d0 100644 --- a/kernel/src/main/scala/cats/kernel/std/option.scala +++ b/kernel/src/main/scala/cats/kernel/std/option.scala @@ -4,16 +4,20 @@ package std package object option extends OptionInstances trait OptionInstances extends OptionInstances1 { - implicit def optionOrder[A: Order] = new OptionOrder[A] - implicit def optionMonoid[A: Semigroup] = new OptionMonoid[A] + implicit def optionOrder[A: Order]: Order[Option[A]] = + new OptionOrder[A] + implicit def optionMonoid[A: Semigroup]: Monoid[Option[A]] = + new OptionMonoid[A] } trait OptionInstances1 extends OptionInstances0 { - implicit def optionPartialOrder[A: PartialOrder] = new OptionPartialOrder[A] + implicit def optionPartialOrder[A: PartialOrder]: PartialOrder[Option[A]] = + new OptionPartialOrder[A] } trait OptionInstances0 { - implicit def optionEq[A: Eq] = new OptionEq[A] + implicit def optionEq[A: Eq]: Eq[Option[A]] = + new OptionEq[A] } class OptionOrder[A](implicit A: Order[A]) extends Order[Option[A]] { diff --git a/kernel/src/main/scala/cats/kernel/std/stream.scala b/kernel/src/main/scala/cats/kernel/std/stream.scala index f924c786d3..edea35a471 100644 --- a/kernel/src/main/scala/cats/kernel/std/stream.scala +++ b/kernel/src/main/scala/cats/kernel/std/stream.scala @@ -6,16 +6,20 @@ import cats.kernel.std.util.StaticMethods package object stream extends StreamInstances trait StreamInstances extends StreamInstances1 { - implicit def streamOrder[A: Order] = new StreamOrder[A] - implicit def streamMonoid[A] = new StreamMonoid[A] + implicit def streamOrder[A: Order]: Order[Stream[A]] = + new StreamOrder[A] + implicit def streamMonoid[A]: Monoid[Stream[A]] = + new StreamMonoid[A] } trait StreamInstances1 extends StreamInstances2 { - implicit def streamPartialOrder[A: PartialOrder] = new StreamPartialOrder[A] + implicit def streamPartialOrder[A: PartialOrder]: PartialOrder[Stream[A]] = + new StreamPartialOrder[A] } trait StreamInstances2 { - implicit def streamEq[A: Eq] = new StreamEq[A] + implicit def streamEq[A: Eq]: Eq[Stream[A]] = + new StreamEq[A] } class StreamOrder[A](implicit ev: Order[A]) extends Order[Stream[A]] { diff --git a/kernel/src/main/scala/cats/kernel/std/string.scala b/kernel/src/main/scala/cats/kernel/std/string.scala index b7394a3086..6e46989761 100644 --- a/kernel/src/main/scala/cats/kernel/std/string.scala +++ b/kernel/src/main/scala/cats/kernel/std/string.scala @@ -5,7 +5,7 @@ package object string extends StringInstances trait StringInstances { implicit val stringOrder: Order[String] = new StringOrder - implicit val stringMonoid = new StringMonoid + implicit val stringMonoid: Monoid[String] = new StringMonoid } class StringOrder extends Order[String] { diff --git a/kernel/src/main/scala/cats/kernel/std/vector.scala b/kernel/src/main/scala/cats/kernel/std/vector.scala index 3bb99381a8..77ed2ae954 100644 --- a/kernel/src/main/scala/cats/kernel/std/vector.scala +++ b/kernel/src/main/scala/cats/kernel/std/vector.scala @@ -6,16 +6,20 @@ import cats.kernel.std.util.StaticMethods package object vector extends VectorInstances trait VectorInstances extends VectorInstances1 { - implicit def vectorOrder[A: Order] = new VectorOrder[A] - implicit def vectorMonoid[A] = new VectorMonoid[A] + implicit def vectorOrder[A: Order]: Order[Vector[A]] = + new VectorOrder[A] + implicit def vectorMonoid[A]: Monoid[Vector[A]] = + new VectorMonoid[A] } trait VectorInstances1 extends VectorInstances2 { - implicit def vectorPartialOrder[A: PartialOrder] = new VectorPartialOrder[A] + implicit def vectorPartialOrder[A: PartialOrder]: PartialOrder[Vector[A]] = + new VectorPartialOrder[A] } trait VectorInstances2 { - implicit def vectorEq[A: Eq] = new VectorEq[A] + implicit def vectorEq[A: Eq]: Eq[Vector[A]] = + new VectorEq[A] } class VectorOrder[A](implicit ev: Order[A]) extends Order[Vector[A]] { From 8fe89386ba4001b77fc08170c54051392df17f3f Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Wed, 27 Apr 2016 00:25:53 -0400 Subject: [PATCH 098/185] Change function traits to be abstract classes. This commit also adds some "missing" function traits (as abstract classes) and replaces an instance of c.isNaN with isNaN(c) for performance. --- kernel/src/main/scala/cats/kernel/Band.scala | 2 +- .../cats/kernel/BoundedSemilattice.scala | 2 +- kernel/src/main/scala/cats/kernel/Eq.scala | 11 ++++--- kernel/src/main/scala/cats/kernel/Group.scala | 3 +- .../src/main/scala/cats/kernel/Monoid.scala | 8 +++-- kernel/src/main/scala/cats/kernel/Order.scala | 27 +++++------------ .../main/scala/cats/kernel/PartialOrder.scala | 29 +++++++++---------- .../main/scala/cats/kernel/Semigroup.scala | 2 +- .../main/scala/cats/kernel/Semilattice.scala | 9 +++++- 9 files changed, 48 insertions(+), 45 deletions(-) diff --git a/kernel/src/main/scala/cats/kernel/Band.scala b/kernel/src/main/scala/cats/kernel/Band.scala index 5404005633..e2d6c6ab05 100644 --- a/kernel/src/main/scala/cats/kernel/Band.scala +++ b/kernel/src/main/scala/cats/kernel/Band.scala @@ -9,7 +9,7 @@ import scala.{specialized => sp} */ trait Band[@sp(Int, Long, Float, Double) A] extends Any with Semigroup[A] -object Band { +object Band extends SemigroupFunctions[Band] { /** * Access an implicit `Band[A]`. diff --git a/kernel/src/main/scala/cats/kernel/BoundedSemilattice.scala b/kernel/src/main/scala/cats/kernel/BoundedSemilattice.scala index b9497ec63e..9838164ea7 100644 --- a/kernel/src/main/scala/cats/kernel/BoundedSemilattice.scala +++ b/kernel/src/main/scala/cats/kernel/BoundedSemilattice.scala @@ -4,7 +4,7 @@ import scala.{specialized => sp} trait BoundedSemilattice[@sp(Int, Long, Float, Double) A] extends Any with Semilattice[A] with CommutativeMonoid[A] -object BoundedSemilattice { +object BoundedSemilattice extends SemilatticeFunctions[BoundedSemilattice] { /** * Access an implicit `BoundedSemilattice[A]`. diff --git a/kernel/src/main/scala/cats/kernel/Eq.scala b/kernel/src/main/scala/cats/kernel/Eq.scala index c76f51ad28..5ae4ec752e 100644 --- a/kernel/src/main/scala/cats/kernel/Eq.scala +++ b/kernel/src/main/scala/cats/kernel/Eq.scala @@ -48,15 +48,17 @@ trait Eq[@sp A] extends Any with Serializable { self => } } -trait EqFunctions { - def eqv[@sp A](x: A, y: A)(implicit ev: Eq[A]): Boolean = +abstract class EqFunctions[E[T] <: Eq[T]] { + + def eqv[@sp A](x: A, y: A)(implicit ev: E[A]): Boolean = ev.eqv(x, y) - def neqv[@sp A](x: A, y: A)(implicit ev: Eq[A]): Boolean = + def neqv[@sp A](x: A, y: A)(implicit ev: E[A]): Boolean = ev.neqv(x, y) + } -object Eq extends EqFunctions { +object Eq extends EqFunctions[Eq] { /** * Access an implicit `Eq[A]`. @@ -120,6 +122,7 @@ object Eq extends EqFunctions { }) } } + /** * This is a monoid that creates an Eq that * checks that at least one equality check passes diff --git a/kernel/src/main/scala/cats/kernel/Group.scala b/kernel/src/main/scala/cats/kernel/Group.scala index 11776f69bd..bb4f7b3a50 100644 --- a/kernel/src/main/scala/cats/kernel/Group.scala +++ b/kernel/src/main/scala/cats/kernel/Group.scala @@ -45,9 +45,10 @@ trait Group[@sp(Int, Long, Float, Double) A] extends Any with Monoid[A] { } } -trait GroupFunctions[G[T] <: Group[T]] extends MonoidFunctions[Group] { +abstract class GroupFunctions[G[T] <: Group[T]] extends MonoidFunctions[Group] { def inverse[@sp(Int, Long, Float, Double) A](a: A)(implicit ev: G[A]): A = ev.inverse(a) + def remove[@sp(Int, Long, Float, Double) A](x: A, y: A)(implicit ev: G[A]): A = ev.remove(x, y) } diff --git a/kernel/src/main/scala/cats/kernel/Monoid.scala b/kernel/src/main/scala/cats/kernel/Monoid.scala index aa2cbaf317..0f3b58709f 100644 --- a/kernel/src/main/scala/cats/kernel/Monoid.scala +++ b/kernel/src/main/scala/cats/kernel/Monoid.scala @@ -18,7 +18,8 @@ trait Monoid[@sp(Int, Long, Float, Double) A] extends Any with Semigroup[A] { /** * Tests if `a` is the identity. */ - def isEmpty(a: A)(implicit ev: Eq[A]) = ev.eqv(a, empty) + def isEmpty(a: A)(implicit ev: Eq[A]): Boolean = + ev.eqv(a, empty) /** * Return `a` appended to itself `n` times. @@ -35,10 +36,13 @@ trait Monoid[@sp(Int, Long, Float, Double) A] extends Any with Semigroup[A] { as.foldLeft(empty)(combine) } -trait MonoidFunctions[M[T] <: Monoid[T]] extends SemigroupFunctions[M] { +abstract class MonoidFunctions[M[T] <: Monoid[T]] extends SemigroupFunctions[M] { def empty[@sp(Int, Long, Float, Double) A](implicit ev: M[A]): A = ev.empty + def isEmpty[@sp(Int, Long, Float, Double) A](a: A)(implicit m: M[A], ev: Eq[A]): Boolean = + m.isEmpty(a) + def combineAll[@sp(Int, Long, Float, Double) A](as: TraversableOnce[A])(implicit ev: M[A]): A = ev.combineAll(as) } diff --git a/kernel/src/main/scala/cats/kernel/Order.scala b/kernel/src/main/scala/cats/kernel/Order.scala index 53fcf6a931..e982c866dc 100644 --- a/kernel/src/main/scala/cats/kernel/Order.scala +++ b/kernel/src/main/scala/cats/kernel/Order.scala @@ -122,38 +122,27 @@ trait Order[@sp A] extends Any with PartialOrder[A] { self => } } -trait OrderFunctions { - def compare[@sp A](x: A, y: A)(implicit ev: Order[A]): Int = +abstract class OrderFunctions[O[T] <: Order[T]] extends PartialOrderFunctions[O] { + + def compare[@sp A](x: A, y: A)(implicit ev: O[A]): Int = ev.compare(x, y) - def eqv[@sp A](x: A, y: A)(implicit ev: Order[A]): Boolean = - ev.eqv(x, y) - def neqv[@sp A](x: A, y: A)(implicit ev: Order[A]): Boolean = - ev.neqv(x, y) - def gt[@sp A](x: A, y: A)(implicit ev: Order[A]): Boolean = - ev.gt(x, y) - def gteqv[@sp A](x: A, y: A)(implicit ev: Order[A]): Boolean = - ev.gteqv(x, y) - def lt[@sp A](x: A, y: A)(implicit ev: Order[A]): Boolean = - ev.lt(x, y) - def lteqv[@sp A](x: A, y: A)(implicit ev: Order[A]): Boolean = - ev.lteqv(x, y) - - def min[@sp A](x: A, y: A)(implicit ev: Order[A]): A = + def min[@sp A](x: A, y: A)(implicit ev: O[A]): A = ev.min(x, y) - def max[@sp A](x: A, y: A)(implicit ev: Order[A]): A = + + def max[@sp A](x: A, y: A)(implicit ev: O[A]): A = ev.max(x, y) } object Order extends OrderFunctions { /** - * Access an implicit `Eq[A]`. + * Access an implicit `Order[A]`. */ @inline final def apply[A](implicit ev: Order[A]) = ev /** - * Convert an implicit `Order[A]` to an `Order[B]` using the given + * Convert an implicit `Order[B]` to an `Order[A]` using the given * function `f`. */ def by[@sp A, @sp B](f: A => B)(implicit ev: Order[B]): Order[A] = diff --git a/kernel/src/main/scala/cats/kernel/PartialOrder.scala b/kernel/src/main/scala/cats/kernel/PartialOrder.scala index e3adbe0f9a..334f661139 100644 --- a/kernel/src/main/scala/cats/kernel/PartialOrder.scala +++ b/kernel/src/main/scala/cats/kernel/PartialOrder.scala @@ -1,5 +1,6 @@ package cats.kernel +import java.lang.Double.isNaN import scala.{specialized => sp} /** @@ -43,7 +44,7 @@ trait PartialOrder[@sp A] extends Any with Eq[A] { self => */ def tryCompare(x: A, y: A): Option[Int] = { val c = partialCompare(x, y) - if (c.isNaN) None else Some(c.signum) + if (isNaN(c)) None else Some(c.signum) } /** @@ -111,40 +112,38 @@ trait PartialOrder[@sp A] extends Any with Eq[A] { self => def gt(x: A, y: A): Boolean = partialCompare(x, y) > 0 } -trait PartialOrderFunctions { +abstract class PartialOrderFunctions[P[T] <: PartialOrder[T]] extends EqFunctions[P] { - def partialCompare[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Double = + def partialCompare[@sp A](x: A, y: A)(implicit ev: P[A]): Double = ev.partialCompare(x, y) - def tryCompare[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Option[Int] = + def tryCompare[@sp A](x: A, y: A)(implicit ev: P[A]): Option[Int] = ev.tryCompare(x, y) - def pmin[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Option[A] = + def pmin[@sp A](x: A, y: A)(implicit ev: P[A]): Option[A] = ev.pmin(x, y) - def pmax[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Option[A] = + def pmax[@sp A](x: A, y: A)(implicit ev: P[A]): Option[A] = ev.pmax(x, y) - def eqv[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Boolean = - ev.eqv(x, y) - def lteqv[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Boolean = + def lteqv[@sp A](x: A, y: A)(implicit ev: P[A]): Boolean = ev.lteqv(x, y) - def lt[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Boolean = + def lt[@sp A](x: A, y: A)(implicit ev: P[A]): Boolean = ev.lt(x, y) - def gteqv[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Boolean = + def gteqv[@sp A](x: A, y: A)(implicit ev: P[A]): Boolean = ev.gteqv(x, y) - def gt[@sp A](x: A, y: A)(implicit ev: PartialOrder[A]): Boolean = + def gt[@sp A](x: A, y: A)(implicit ev: P[A]): Boolean = ev.gt(x, y) } object PartialOrder extends PartialOrderFunctions { /** - * Access an implicit `Eq[A]`. + * Access an implicit `PartialOrder[A]`. */ @inline final def apply[A](implicit ev: PartialOrder[A]) = ev /** - * Convert an implicit `PartialOrder[A]` to an `PartialOrder[B]` - * using the given function `f`. + * Convert an implicit `PartialOrder[B]` to an `PartialOrder[A]` using the given + * function `f`. */ def by[@sp A, @sp B](f: A => B)(implicit ev: PartialOrder[B]): PartialOrder[A] = ev.on(f) diff --git a/kernel/src/main/scala/cats/kernel/Semigroup.scala b/kernel/src/main/scala/cats/kernel/Semigroup.scala index 8e17529793..736e5da2ad 100644 --- a/kernel/src/main/scala/cats/kernel/Semigroup.scala +++ b/kernel/src/main/scala/cats/kernel/Semigroup.scala @@ -41,7 +41,7 @@ trait Semigroup[@sp(Int, Long, Float, Double) A] extends Any with Serializable { as.reduceOption(combine) } -trait SemigroupFunctions[S[T] <: Semigroup[T]] { +abstract class SemigroupFunctions[S[T] <: Semigroup[T]] { def combine[@sp(Int, Long, Float, Double) A](x: A, y: A)(implicit ev: S[A]): A = ev.combine(x, y) diff --git a/kernel/src/main/scala/cats/kernel/Semilattice.scala b/kernel/src/main/scala/cats/kernel/Semilattice.scala index 92cc80db86..77a69ff2d5 100644 --- a/kernel/src/main/scala/cats/kernel/Semilattice.scala +++ b/kernel/src/main/scala/cats/kernel/Semilattice.scala @@ -53,7 +53,14 @@ trait Semilattice[@sp(Int, Long, Float, Double) A] extends Any } } -object Semilattice { +abstract class SemilatticeFunctions[S[T] <: Semilattice[T]] extends SemigroupFunctions[S] { + def asMeetPartialOrder[A](implicit s: S[A], ev: Eq[A]): PartialOrder[A] = + s.asMeetPartialOrder(ev) + def asJoinPartialOrder[A](implicit s: S[A], ev: Eq[A]): PartialOrder[A] = + s.asJoinPartialOrder(ev) +} + +object Semilattice extends SemilatticeFunctions[Semilattice] { /** * Access an implicit `Semilattice[A]`. From 0acca0a2637dd49105ea2a84d65a08dabef0ec9f Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Wed, 27 Apr 2016 09:12:14 -0400 Subject: [PATCH 099/185] Since the last release notes updates I merged a few more things. I also noticed a bunch of folks who were missing from our list of authors, so I made a pass through git log to add people that I noticed were missing. --- AUTHORS.md | 16 ++++++++++++++++ CHANGES.md | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/AUTHORS.md b/AUTHORS.md index 856db33596..d1d925c6cd 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -12,22 +12,28 @@ A successful open-source project relies upon the community to: This file lists the people whose contributions have made Cats possible: + * 3rdLaw * Adelbert Chang + * Aldo Stracquadanio * Alessandro Lacava * Alexey Levan * Alissa Pajer * Alistair Johnson * Amir Mohammad Saied * Andrew Jones + * Angelo Genovese * Antoine Comte * Arya Irani * Ash Pook * Benjamin Thuillier + * Binh Nguyen * Bobby Rauchenberg * Brendan McAdams * Cody Allen * Colt Frederickson * Dale Wijnand + * Daniel Spiewak + * Dave Gurnell * Dave Rostron * David Allsopp * David Gregory @@ -40,19 +46,25 @@ possible: * Eugene Yokota * Feynman Liang * Frank S. Thomas + * Ian McIntosh + * ImLiar * Jean-Rémi Desjardins * Jisoo Park * Josh Marcus * Juan Pedro Moreno * Julien Richard-Foy * Julien Truffaut + * Kailuo Wang * Kenji Yoshida * Long Cao * Luis Angel Vicente Sanchez + * Luis Sanchez * Luke Wyman * Marc Siegel * Markus Hauck + * Matt Martin * Matthias Lüneberg + * Max Worgan * Michael Pilquist * Mike Curry * Miles Sabin @@ -61,18 +73,22 @@ possible: * Pascal Voitot * Paul Phillips * Pavkin Vladimir + * Pere Villega + * Peter Neyens * Philip Wills * Raúl Raja Martínez * Rintcius Blok * Rob Norris * Romain Ruetschi * Ross A. Baker + * Ryan Case * Sarunas Valaskevicius * Shunsuke Otani * Sinisa Louc * Stephen Judkins * Stew O'Connor * Sumedh Mungee + * Tomas Mikula * Travis Brown * Wedens * Yosef Fertel diff --git a/CHANGES.md b/CHANGES.md index 01756dee9c..bfb365aeab 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -69,7 +69,8 @@ And some other improvements to the documentation, tutorials, laws and tests, inc * [#972](https://github.com/typelevel/cats/pull/972): Fix swapped f and g in `invariant` docs * [#979](https://github.com/typelevel/cats/pull/979): Fix outdated import for `cats.syntax.apply._` * [#995](https://github.com/typelevel/cats/pull/995): Move coverage away from bash -* [#1002](https://github.com/typelevel/cats/pull/1002): correct the url to Data types à la carte +* [#1002](https://github.com/typelevel/cats/pull/1002): Correct the URL for *Data types à la carte* + As always thanks to everyone who filed issues, participated in the Cats Gitter channel, submitted code, or helped review pull requests. From 5edefa3a4621e226fa8b5d9779057a7b80825aa3 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Wed, 27 Apr 2016 09:16:37 -0400 Subject: [PATCH 100/185] Add missing notes around new SemigroupK instances. --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index bfb365aeab..c6a6b5b604 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -30,6 +30,8 @@ And additions: * [#973](https://github.com/typelevel/cats/pull/973): Add `flatMapF` for `StateT` * [#985](https://github.com/typelevel/cats/pull/985): Add object `reducible` for reducible syntax * [#994](https://github.com/typelevel/cats/pull/994): updated to latest algebra (brought in all the new goodies) +* [#996](https://github.com/typelevel/cats/pull/996): Add `SemigroupK` instance for `Xor` +* [#998](https://github.com/typelevel/cats/pull/998): Add `SemigroupK` instance for `Validated` And bug fixes: From da49118c3de76b4cc0fe43d835da8818228a1995 Mon Sep 17 00:00:00 2001 From: peterneyens Date: Mon, 18 Apr 2016 13:03:53 +0200 Subject: [PATCH 101/185] Add `Bitraverse` instances for `Validated` and `XorT`. --- core/src/main/scala/cats/data/Validated.scala | 30 ++++++++++++++--- core/src/main/scala/cats/data/Xor.scala | 2 +- core/src/main/scala/cats/data/XorT.scala | 32 ++++++++++++++++--- .../scala/cats/tests/ValidatedTests.scala | 5 +-- .../src/test/scala/cats/tests/XorTTests.scala | 2 ++ 5 files changed, 59 insertions(+), 12 deletions(-) diff --git a/core/src/main/scala/cats/data/Validated.scala b/core/src/main/scala/cats/data/Validated.scala index 7232acca70..88f9f12080 100644 --- a/core/src/main/scala/cats/data/Validated.scala +++ b/core/src/main/scala/cats/data/Validated.scala @@ -2,7 +2,6 @@ package cats package data import cats.data.Validated.{Invalid, Valid} -import cats.functor.Bifunctor import scala.reflect.ClassTag import scala.util.{Failure, Success, Try} @@ -238,10 +237,31 @@ private[data] sealed abstract class ValidatedInstances extends ValidatedInstance def show(f: Validated[A,B]): String = f.show } - implicit def validatedBifunctor: Bifunctor[Validated] = - new Bifunctor[Validated] { - override def bimap[A, B, C, D](fab: Validated[A, B])(f: A => C, g: B => D): Validated[C, D] = fab.bimap(f, g) - override def leftMap[A, B, C](fab: Validated[A, B])(f: A => C): Validated[C, B] = fab.leftMap(f) + implicit val validatedBitraverse: Bitraverse[Validated] = + new Bitraverse[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) + case Valid(b) => G.map(g(b))(Validated.valid) + } + + def bifoldLeft[A, B, C](fab: Validated[A, B], c: C)(f: (C, A) => C, g: (C, B) => C): C = + fab match { + case Invalid(a) => f(c, a) + case Valid(b) => g(c, b) + } + + def bifoldRight[A, B, C](fab: Validated[A, B], c: Eval[C])(f: (A, Eval[C]) => Eval[C], g: (B, Eval[C]) => Eval[C]): Eval[C] = + fab match { + case Invalid(a) => f(a, c) + case Valid(b) => g(b, c) + } + + override def bimap[A, B, C, D](fab: Validated[A, B])(f: A => C, g: B => D): Validated[C, D] = + fab.bimap(f, g) + + override def leftMap[A, B, C](fab: Validated[A, B])(f: A => C): Validated[C, B] = + fab.leftMap(f) } implicit def validatedInstances[E](implicit E: Semigroup[E]): Traverse[Validated[E, ?]] with ApplicativeError[Validated[E, ?], E] = diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index 16c64ce214..df729a0ef0 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -176,7 +176,7 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { } } - implicit def xorBifunctor: Bitraverse[Xor] = + implicit val xorBitraverse: Bitraverse[Xor] = new Bitraverse[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 { diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index 97b4cae357..60af12734c 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -65,6 +65,9 @@ final 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 bitraverse[G[_], C, D](f: A => G[C], g: B => G[D])(implicit traverseF: Traverse[F], applicativeG: Applicative[G]): G[XorT[F, C, D]] = + applicativeG.map(traverseF.traverse(value)(axb => Bitraverse[Xor].bitraverse(axb)(f, g)))(XorT.apply) + 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(xbd)(xb))) @@ -96,8 +99,8 @@ final case class XorT[F[_], A, B](value: F[A Xor B]) { def ===(that: XorT[F, A, B])(implicit eq: Eq[F[A Xor B]]): Boolean = eq.eqv(value, that.value) - def traverse[G[_], D](f: B => G[D])(implicit traverseF: Traverse[F], traverseXorA: Traverse[A Xor ?], applicativeG: Applicative[G]): G[XorT[F, A, D]] = - applicativeG.map(traverseF.traverse(value)(axb => traverseXorA.traverse(axb)(f)))(XorT.apply) + def traverse[G[_], D](f: B => G[D])(implicit traverseF: Traverse[F], applicativeG: Applicative[G]): G[XorT[F, A, D]] = + applicativeG.map(traverseF.traverse(value)(axb => Traverse[A Xor ?].traverse(axb)(f)))(XorT.apply) def foldLeft[C](c: C)(f: (C, B) => C)(implicit F: Foldable[F]): C = F.foldLeft(value, c)((c, axb) => axb.foldLeft(c)(f)) @@ -185,11 +188,10 @@ private[data] abstract class XorTInstances extends XorTInstances1 { implicit def xorTShow[F[_], L, R](implicit sh: Show[F[L Xor R]]): Show[XorT[F, L, R]] = functor.Contravariant[Show].contramap(sh)(_.value) - implicit def xorTBifunctor[F[_]](implicit F: Functor[F]): Bifunctor[XorT[F, ?, ?]] = { + implicit def xorTBifunctor[F[_]](implicit F: Functor[F]): Bifunctor[XorT[F, ?, ?]] = new Bifunctor[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) } - } implicit def xorTTraverse[F[_], L](implicit F: Traverse[F]): Traverse[XorT[F, L, ?]] = new XorTTraverse[F, L] { @@ -224,6 +226,11 @@ private[data] abstract class XorTInstances1 extends XorTInstances2 { new XorTPartialOrder[F, L, R] { val F0: PartialOrder[F[L Xor R]] = F } + + implicit def xorTBitraverse[F[_]](implicit F: Traverse[F]): Bitraverse[XorT[F, ?, ?]] = + new XorTBitraverse[F] { + val F0: Traverse[F] = F + } } private[data] abstract class XorTInstances2 extends XorTInstances3 { @@ -311,6 +318,23 @@ 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, ?, ?]] { + 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 = + F0.foldLeft(fab.value, c)( (acc, axb) => Bifoldable[Xor].bifoldLeft(axb, acc)(f, g)) + + def bifoldRight[A, B, C](fab: XorT[F, A, B], c: Eval[C])(f: (A, Eval[C]) => Eval[C], g: (B, Eval[C]) => Eval[C]): Eval[C] = + 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] { + 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]] = + fab.bitraverse(f, g) +} + private[data] sealed trait XorTEq[F[_], L, A] extends Eq[XorT[F, L, A]] { implicit def F0: Eq[F[L Xor A]] diff --git a/tests/src/test/scala/cats/tests/ValidatedTests.scala b/tests/src/test/scala/cats/tests/ValidatedTests.scala index aeeaa2acc3..c2f8d39416 100644 --- a/tests/src/test/scala/cats/tests/ValidatedTests.scala +++ b/tests/src/test/scala/cats/tests/ValidatedTests.scala @@ -3,7 +3,7 @@ package tests import cats.data.{NonEmptyList, Validated, ValidatedNel, Xor, XorT} import cats.data.Validated.{Valid, Invalid} -import cats.laws.discipline.{BifunctorTests, TraverseTests, ApplicativeErrorTests, SerializableTests, CartesianTests} +import cats.laws.discipline.{BitraverseTests, TraverseTests, ApplicativeErrorTests, SerializableTests, CartesianTests} import org.scalacheck.Arbitrary._ import cats.laws.discipline.{SemigroupKTests} import cats.laws.discipline.arbitrary._ @@ -16,7 +16,8 @@ class ValidatedTests extends CatsSuite { checkAll("Validated[String, Int]", CartesianTests[Validated[String,?]].cartesian[Int, Int, Int]) checkAll("Cartesian[Validated[String,?]]", SerializableTests.serializable(Cartesian[Validated[String,?]])) - checkAll("Validated[?, ?]", BifunctorTests[Validated].bifunctor[Int, Int, Int, Int, Int, Int]) + checkAll("Validated[?, ?]", BitraverseTests[Validated].bitraverse[Option, Int, Int, Int, String, String, String]) + checkAll("Bitraverse[Validated]", SerializableTests.serializable(Bitraverse[Validated])) implicit val eq0 = XorT.xorTEq[Validated[String, ?], String, Int] diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index 59fc4f41c1..6c375bc34b 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -15,6 +15,8 @@ class XorTTests extends CatsSuite { checkAll("MonadError[XorT[List, ?, ?]]", SerializableTests.serializable(MonadError[XorT[List, String, ?], String])) checkAll("XorT[List, ?, ?]", BifunctorTests[XorT[List, ?, ?]].bifunctor[Int, Int, Int, String, String, String]) checkAll("Bifunctor[XorT[List, ?, ?]]", SerializableTests.serializable(Bifunctor[XorT[List, ?, ?]])) + checkAll("XorT[List, ?, ?]", BitraverseTests[XorT[List, ?, ?]].bitraverse[Option, Int, Int, Int, String, String, String]) + checkAll("Bitraverse[XorT[List, ?, ?]]", SerializableTests.serializable(Bitraverse[XorT[List, ?, ?]])) checkAll("XorT[List, Int, ?]", TraverseTests[XorT[List, Int, ?]].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[XorT[List, Int, ?]]", SerializableTests.serializable(Traverse[XorT[List, Int, ?]])) checkAll("XorT[List, String, Int]", OrderLaws[XorT[List, String, Int]].order) From 5deef5af1e1e4d4b0143717665972c223570f360 Mon Sep 17 00:00:00 2001 From: hamishdickson Date: Wed, 27 Apr 2016 18:44:13 +0100 Subject: [PATCH 102/185] fix broken link in foldable docs --- docs/src/main/tut/foldable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/tut/foldable.md b/docs/src/main/tut/foldable.md index 3b863bf8a3..81e4ea09fa 100644 --- a/docs/src/main/tut/foldable.md +++ b/docs/src/main/tut/foldable.md @@ -21,7 +21,7 @@ used by the associated `Foldable[_]` instance. - `foldRight(fa, b)(f)` lazily folds `fa` from right-to-left. These form the basis for many other operations, see also: -[A tutorial on the universality and expressiveness of fold](https://www.cs.nott.ac.uk/~gmh/fold.pdf) +[A tutorial on the universality and expressiveness of fold](http://www.cs.nott.ac.uk/~gmh/fold.pdf) First some standard imports. From ff203c5000b7f4b08a3f55b427d512da70cc8e6c Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Wed, 27 Apr 2016 15:55:49 -0400 Subject: [PATCH 103/185] Revert "Disable scaladoc during packaging for scala 2.10" --- build.sbt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/build.sbt b/build.sbt index 17266c1dc0..a538560b39 100644 --- a/build.sbt +++ b/build.sbt @@ -233,12 +233,6 @@ lazy val publishSettings = Seq( scmInfo := Some(ScmInfo(url("https://github.com/typelevel/cats"), "scm:git:git@github.com:typelevel/cats.git")), autoAPIMappings := true, apiURL := Some(url("http://typelevel.org/cats/api/")), - publishArtifact in (Compile, packageDoc) := { - CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, 10)) => false // don't package scaladoc when publishing for 2.10 - case _ => true - } - }, pomExtra := ( From b57f485367c75e2ef5f84979bfbc527a9ce910e1 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Wed, 27 Apr 2016 19:52:33 -0400 Subject: [PATCH 104/185] updated release notes with latest change --- CHANGES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index c6a6b5b604..1cf6cd29e6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ ## Version 0.5.0 -> 2016 April 15 +> 2016 April 28 Version 0.5.0 is the fifth release. @@ -32,6 +32,7 @@ And additions: * [#994](https://github.com/typelevel/cats/pull/994): updated to latest algebra (brought in all the new goodies) * [#996](https://github.com/typelevel/cats/pull/996): Add `SemigroupK` instance for `Xor` * [#998](https://github.com/typelevel/cats/pull/998): Add `SemigroupK` instance for `Validated` +* [#986](https://github.com/typelevel/cats/pull/986): Add `Bitraverse` instances for `Validated` and `XorT` And bug fixes: @@ -72,6 +73,7 @@ And some other improvements to the documentation, tutorials, laws and tests, inc * [#979](https://github.com/typelevel/cats/pull/979): Fix outdated import for `cats.syntax.apply._` * [#995](https://github.com/typelevel/cats/pull/995): Move coverage away from bash * [#1002](https://github.com/typelevel/cats/pull/1002): Correct the URL for *Data types à la carte* +* [#1005](https://github.com/typelevel/cats/pull/1005): fix broken link in foldable docs As always thanks to everyone who filed issues, participated in the Cats Gitter From d7bed8ea7e8effc1c741a5d967cfbe0755c36980 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Wed, 27 Apr 2016 23:31:11 -0400 Subject: [PATCH 105/185] Setting version to 0.5.0 --- version.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.sbt b/version.sbt index c9f9c294f1..53270e0a1e 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.5.0-SNAPSHOT" \ No newline at end of file +version in ThisBuild := "0.5.0" \ No newline at end of file From 63f9d76437610ac63ae33e65fe69ad86a9942416 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Wed, 27 Apr 2016 23:35:47 -0400 Subject: [PATCH 106/185] Setting version to 0.6.0-SNAPSHOT --- version.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.sbt b/version.sbt index 53270e0a1e..01fa65ac61 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.5.0" \ No newline at end of file +version in ThisBuild := "0.6.0-SNAPSHOT" \ No newline at end of file From b9b550a943b2feb74bc2b732aa1271ea761cba6a Mon Sep 17 00:00:00 2001 From: Andrew Mohrland Date: Wed, 27 Apr 2016 21:32:34 -0700 Subject: [PATCH 107/185] Update version in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b10091d2f3..76e302692f 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ To get started with SBT, simply add the following to your `build.sbt` file: ```scala -libraryDependencies += "org.typelevel" %% "cats" % "0.4.1" +libraryDependencies += "org.typelevel" %% "cats" % "0.5.0" ``` This will pull in all of Cats' modules. If you only require some @@ -35,7 +35,7 @@ functionality, you can pick-and-choose from amongst these modules Release notes for Cats are available in [CHANGES.md](CHANGES.md). -*Cats 0.4.1 is a pre-release: there are not currently source- or +*Cats 0.5.0 is a pre-release: there are not currently source- or binary-compatibility guarantees.* ### Documentation From 305a7c1092880f075e2988d3750694443fc93c6e Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 28 Apr 2016 09:47:32 -0400 Subject: [PATCH 108/185] added a migration note to the release notes IRT tuple instances --- CHANGES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 1cf6cd29e6..1bba92b2f2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,10 +6,12 @@ Version 0.5.0 is the fifth release. This release includes some API changes: +`cats.laws.discipline.eq` no longer provides `Eq` instances for `tuple2` and `tuple3`, these instances and together with some other new instances for `tuple`s are not provided by `cats.std.tuple` (through inheriting the instance trait defined in algebra 0.4.2). + * [#910](https://github.com/typelevel/cats/pull/910): Remove `Streaming` and `StreamingT` * [#967](https://github.com/typelevel/cats/pull/967): `product` and `map` can be implemented in terms of `ap` * [#970](https://github.com/typelevel/cats/pull/970): Renamed `Kleisli#apply`to `ap` - +* [#994](https://github.com/typelevel/cats/pull/994): updated to latest algebra (brought in all the new goodies) And additions: From f858baf907ff564fd08db2b51e9188aed505c7dc Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 28 Apr 2016 09:49:20 -0400 Subject: [PATCH 109/185] updated the version in site --- docs/src/site/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/site/index.md b/docs/src/site/index.md index b43c1f3671..67d5966e6a 100644 --- a/docs/src/site/index.md +++ b/docs/src/site/index.md @@ -21,7 +21,7 @@ Cats is currently available for Scala 2.10 and 2.11. To get started with SBT, simply add the following to your build.sbt file: - libraryDependencies += "org.typelevel" %% "cats" % "0.4.1" + libraryDependencies += "org.typelevel" %% "cats" % "0.5.0" This will pull in all of Cats' modules. If you only require some functionality, you can pick-and-choose from amongst these modules From 5609a26335712a72370a85fb1e4a81fe18b1c411 Mon Sep 17 00:00:00 2001 From: aaron levin Date: Thu, 28 Apr 2016 16:00:14 +0200 Subject: [PATCH 110/185] Add Aaron Levin to AUTHORS.md --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index d1d925c6cd..bf4201fca6 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -13,6 +13,7 @@ This file lists the people whose contributions have made Cats possible: * 3rdLaw + * Aaron Levin * Adelbert Chang * Aldo Stracquadanio * Alessandro Lacava From 6906620ffa60ad2973303976f238004e34ff7a36 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 28 Apr 2016 23:01:32 -0400 Subject: [PATCH 111/185] fixed typo --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 1bba92b2f2..7a00bc92e9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,7 @@ Version 0.5.0 is the fifth release. This release includes some API changes: -`cats.laws.discipline.eq` no longer provides `Eq` instances for `tuple2` and `tuple3`, these instances and together with some other new instances for `tuple`s are not provided by `cats.std.tuple` (through inheriting the instance trait defined in algebra 0.4.2). +`cats.laws.discipline.eq` no longer provides `Eq` instances for `tuple2` and `tuple3`, these instances and together with some other new instances for `tuple`s are now provided by `cats.std.tuple` (through inheriting the instance trait defined in algebra 0.4.2). * [#910](https://github.com/typelevel/cats/pull/910): Remove `Streaming` and `StreamingT` * [#967](https://github.com/typelevel/cats/pull/967): `product` and `map` can be implemented in terms of `ap` From 68475094015a37f6aaf6b528a9319db11b219184 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 29 Apr 2016 08:58:55 -0400 Subject: [PATCH 112/185] addressed minor PR feedback --- CHANGES.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7a00bc92e9..246163b01e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,7 +6,7 @@ Version 0.5.0 is the fifth release. This release includes some API changes: -`cats.laws.discipline.eq` no longer provides `Eq` instances for `tuple2` and `tuple3`, these instances and together with some other new instances for `tuple`s are now provided by `cats.std.tuple` (through inheriting the instance trait defined in algebra 0.4.2). +`cats.laws.discipline.eq` no longer provides `Eq` instances for `Tuple2` and `Tuple3`, these instances and together with some other new instances for `Tuple`s are now provided by `cats.std.tuple` (through inheriting the instance trait defined in algebra 0.4.2). * [#910](https://github.com/typelevel/cats/pull/910): Remove `Streaming` and `StreamingT` * [#967](https://github.com/typelevel/cats/pull/967): `product` and `map` can be implemented in terms of `ap` @@ -31,7 +31,6 @@ And additions: * [#971](https://github.com/typelevel/cats/pull/971): Add `toValidatedNel` to `Xor` * [#973](https://github.com/typelevel/cats/pull/973): Add `flatMapF` for `StateT` * [#985](https://github.com/typelevel/cats/pull/985): Add object `reducible` for reducible syntax -* [#994](https://github.com/typelevel/cats/pull/994): updated to latest algebra (brought in all the new goodies) * [#996](https://github.com/typelevel/cats/pull/996): Add `SemigroupK` instance for `Xor` * [#998](https://github.com/typelevel/cats/pull/998): Add `SemigroupK` instance for `Validated` * [#986](https://github.com/typelevel/cats/pull/986): Add `Bitraverse` instances for `Validated` and `XorT` From 147ab26a7e8495340eb005c9376c9f2882ea4fea Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Fri, 29 Apr 2016 12:02:02 -0400 Subject: [PATCH 113/185] Add missing type parameters. --- kernel/src/main/scala/cats/kernel/Order.scala | 2 +- kernel/src/main/scala/cats/kernel/PartialOrder.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/src/main/scala/cats/kernel/Order.scala b/kernel/src/main/scala/cats/kernel/Order.scala index e982c866dc..4cc7e0a7e4 100644 --- a/kernel/src/main/scala/cats/kernel/Order.scala +++ b/kernel/src/main/scala/cats/kernel/Order.scala @@ -134,7 +134,7 @@ abstract class OrderFunctions[O[T] <: Order[T]] extends PartialOrderFunctions[O] ev.max(x, y) } -object Order extends OrderFunctions { +object Order extends OrderFunctions[Order] { /** * Access an implicit `Order[A]`. diff --git a/kernel/src/main/scala/cats/kernel/PartialOrder.scala b/kernel/src/main/scala/cats/kernel/PartialOrder.scala index 334f661139..c962ca5444 100644 --- a/kernel/src/main/scala/cats/kernel/PartialOrder.scala +++ b/kernel/src/main/scala/cats/kernel/PartialOrder.scala @@ -134,7 +134,7 @@ abstract class PartialOrderFunctions[P[T] <: PartialOrder[T]] extends EqFunction ev.gt(x, y) } -object PartialOrder extends PartialOrderFunctions { +object PartialOrder extends PartialOrderFunctions[PartialOrder] { /** * Access an implicit `PartialOrder[A]`. From 542b971f1c442eb8f04389f99ff695c943d463bf Mon Sep 17 00:00:00 2001 From: Zainab Date: Tue, 3 May 2016 00:30:04 +0100 Subject: [PATCH 114/185] Adding WriterT Monoid --- core/src/main/scala/cats/data/WriterT.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 1db757f28f..d6e59d83f5 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -75,6 +75,11 @@ private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { implicit def writerTShow[F[_], L, V](implicit F: Show[F[(L, V)]]): Show[WriterT[F, L, V]] = new Show[WriterT[F, L, V]] { override def show(f: WriterT[F, L, V]): String = f.show } + + implicit def writerTMonoid[F[_], L, V](implicit W: Monoid[F[(L, V)]]): Monoid[WriterT[F, L, V]] = new Monoid[WriterT[F, L, V]] { + def empty = WriterT[F, L, V](W.empty) + def combine(x: WriterT[F, L, V], y: WriterT[F, L, V]): WriterT[F, L, V] = WriterT(W.combine(x.run, y.run)) + } } private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 { From cfc2ccb045bfffda53a371efa5ef21c8b4c08e15 Mon Sep 17 00:00:00 2001 From: Zainab Date: Tue, 3 May 2016 00:59:59 +0100 Subject: [PATCH 115/185] Adding test to show Semigroup and Functor --- tests/src/test/scala/cats/tests/WriterTTests.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index 39c7e82b5d..4f0c86f110 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -246,4 +246,12 @@ class WriterTTests extends CatsSuite { checkAll("WriterT[ListWrapper, ListWrapper[Int], ?]", MonadCombineTests[WriterT[ListWrapper, ListWrapper[Int], ?]].monadCombine[Int, Int, Int]) checkAll("MonadCombine[WriterT[ListWrapper, ListWrapper[Int], ?]]", SerializableTests.serializable(MonadCombine[WriterT[ListWrapper, ListWrapper[Int], ?]])) } + + { + //F[(L, V)] has a monoid + implicit val FLV: Monoid[ListWrapper[(ListWrapper[Int], ListWrapper[Int])]] = ListWrapper.monoid[(ListWrapper[Int], ListWrapper[Int])] + + Monoid[WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]] + Semigroup[WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]] + } } From 5eaaa358bceaeb0dc20e5bb251b1a7d75dbcfbaf Mon Sep 17 00:00:00 2001 From: Zainab Date: Tue, 3 May 2016 02:00:57 +0100 Subject: [PATCH 116/185] Add monoid law for WriterT --- tests/src/test/scala/cats/tests/WriterTTests.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index 4f0c86f110..37f3df4839 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -253,5 +253,6 @@ class WriterTTests extends CatsSuite { Monoid[WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]] Semigroup[WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]] + checkAll("WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]", cats.kernel.laws.GroupLaws[WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]].monoid) } } From d48c051a8c530eeeedcbeb7e387cfe285d0ec70e Mon Sep 17 00:00:00 2001 From: Zainab Date: Tue, 3 May 2016 02:02:39 +0100 Subject: [PATCH 117/185] Remove redundant cats. --- tests/src/test/scala/cats/tests/WriterTTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index 37f3df4839..9cd8a04af3 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -253,6 +253,6 @@ class WriterTTests extends CatsSuite { Monoid[WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]] Semigroup[WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]] - checkAll("WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]", cats.kernel.laws.GroupLaws[WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]].monoid) + checkAll("WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]", kernel.laws.GroupLaws[WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]].monoid) } } From 1236cf8782757a154caf78ce80563146e5362adc Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Wed, 4 May 2016 08:01:42 -0400 Subject: [PATCH 118/185] Add Apply.map2Eval and allow traverse laziness Fixes #513. I think this should solve the majority of use-cases for lazy traversal. One thing I noticed is that both `reduceRightToOption` and `traverse1_` (which uses the former) seem to be impossible to implement lazily such that they allow for short-circuiting. At least I couldn't figure out a way. I left a note in the `traverse1_` scaladoc, but in practice I don't anticipate this being a problem, because at the cost of just having an `Applicative` instance as opposed to `Apply`, you can use `traverse_` which is sufficiently lazy. --- core/src/main/scala/cats/Apply.scala | 39 +++++++++++++++-- core/src/main/scala/cats/Foldable.scala | 6 +-- core/src/main/scala/cats/Reducible.scala | 19 ++++++--- core/src/main/scala/cats/data/OneAnd.scala | 4 +- core/src/main/scala/cats/data/Xor.scala | 8 ++++ core/src/main/scala/cats/std/either.scala | 9 ++++ core/src/main/scala/cats/std/list.scala | 9 ++-- core/src/main/scala/cats/std/map.scala | 11 +++-- core/src/main/scala/cats/std/option.scala | 6 +++ core/src/main/scala/cats/std/stream.scala | 5 +-- core/src/main/scala/cats/std/vector.scala | 4 +- .../test/scala/cats/tests/EitherTests.scala | 6 +++ .../test/scala/cats/tests/OptionTests.scala | 5 +++ .../scala/cats/tests/RegressionTests.scala | 42 ++++++++++++++++++- .../src/test/scala/cats/tests/XorTests.scala | 6 +++ 15 files changed, 146 insertions(+), 33 deletions(-) diff --git a/core/src/main/scala/cats/Apply.scala b/core/src/main/scala/cats/Apply.scala index f70b153ce5..85d8eb1016 100644 --- a/core/src/main/scala/cats/Apply.scala +++ b/core/src/main/scala/cats/Apply.scala @@ -33,15 +33,46 @@ trait Apply[F[_]] extends Functor[F] with Cartesian[F] with ApplyArityFunctions[ def map2[A, B, Z](fa: F[A], fb: F[B])(f: (A, B) => Z): F[Z] = map(product(fa, fb)) { case (a, b) => f(a, b) } + /** + * Similar to [[map2]] but uses [[Eval]] to allow for laziness in the `F[B]` + * argument. This can allow for "short-circuiting" of computations. + * + * NOTE: the default implementation of `map2Eval` does does not short-circuit + * computations. For data structures that can benefit from laziness, [[Apply]] + * instances should override this method. + * + * In the following example, `x.map2(bomb)(_ + _)` would result in an error, + * but `map2Eval` "short-circuits" the computation. `x` is `None` and thus the + * result of `bomb` doesn't even need to be evaluated in order to determine + * that the result of `map2Eval` should be `None`. + * + * {{{ + * scala> import cats.{Eval, Later} + * scala> import cats.implicits._ + * scala> val bomb: Eval[Option[Int]] = Later(sys.error("boom")) + * scala> val x: Option[Int] = None + * scala> x.map2Eval(bomb)(_ + _).value + * res0: Option[Int] = None + * }}} + */ + 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. * - * val ap = Apply[Option].compose[List] - * val x = Some(List(1, 2)) - * val y = Some(List(10, 20)) - * ap.map2(x, y)(_ + _) == Some(List(11, 12, 21, 22)) + * 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] { diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index aa83ebf49e..bf1b865c04 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -107,9 +107,9 @@ import simulacrum.typeclass * needed. */ def traverse_[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] = - foldLeft(fa, G.pure(())) { (acc, a) => - G.map2(acc, f(a)) { (_, _) => () } - } + foldRight(fa, Later(G.pure(()))) { (a, acc) => + G.map2Eval(f(a), acc) { (_, _) => () } + }.value /** * Behaves like traverse_, but uses [[Unapply]] to find the diff --git a/core/src/main/scala/cats/Reducible.scala b/core/src/main/scala/cats/Reducible.scala index 2f92905e38..adfcb48a69 100644 --- a/core/src/main/scala/cats/Reducible.scala +++ b/core/src/main/scala/cats/Reducible.scala @@ -83,9 +83,16 @@ import simulacrum.typeclass * `A` values will be mapped into `G[B]` and combined using * `Applicative#map2`. * - * This method does the same thing as `Foldable#traverse_`. The - * difference is that we only need `Apply[G]` here, since we don't - * need to call `Applicative#pure` for a starting value. + * This method is similar to [[Foldable.traverse_]]. There are two + * main differences: + * + * 1. We only need an [[Apply]] instance for `G` here, since we + * don't need to call [[Applicative.pure]] for a starting value. + * 2. This performs a strict left-associative traversal and thus + * must always traverse the entire data structure. Prefer + * [[Foldable.traverse_]] if you have an [[Applicative]] instance + * available for `G` and want to take advantage of short-circuiting + * the traversal. */ def traverse1_[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Apply[G]): G[Unit] = G.map(reduceLeftTo(fa)(f)((x, y) => G.map2(x, f(y))((_, b) => b)))(_ => ()) @@ -93,9 +100,9 @@ import simulacrum.typeclass /** * Sequence `F[G[A]]` using `Apply[G]`. * - * This method is similar to `Foldable#sequence_`. The difference is - * that we only need `Apply[G]` here, since we don't need to call - * `Applicative#pure` for a starting value. + * This method is similar to [[Foldable.sequence_]] but requires only + * an [[Apply]] instance for `G` instead of [[Applicative]]. See the + * [[traverse1_]] documentation for a description of the differences. */ def sequence1_[G[_], A](fga: F[G[A]])(implicit G: Apply[G]): G[Unit] = G.map(reduceLeft(fga)((x, y) => G.map2(x, y)((_, b) => b)))(_ => ()) diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 7bafd5ca76..3e8a36ba51 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -170,9 +170,7 @@ trait OneAndLowPriority2 extends OneAndLowPriority1 { implicit def oneAndTraverse[F[_]](implicit F: Traverse[F]): Traverse[OneAnd[F, ?]] = new Traverse[OneAnd[F, ?]] { def traverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[OneAnd[F, B]] = { - val tail = F.traverse(fa.tail)(f) - val head = f(fa.head) - G.ap2[B, F[B], OneAnd[F, B]](G.pure(OneAnd(_, _)))(head, tail) + G.map2Eval(f(fa.head), Later(F.traverse(fa.tail)(f)))(OneAnd(_, _)).value } def foldLeft[A, B](fa: OneAnd[F, A], b: B)(f: (B, A) => B): B = { diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index df729a0ef0..1bf5e115b6 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -90,6 +90,12 @@ sealed abstract class Xor[+A, +B] extends Product with Serializable { case Xor.Right(b) => Xor.Right(f(b)) } + def map2Eval[AA >: A, C, Z](fc: Eval[AA Xor C])(f: (B, C) => Z): Eval[AA Xor Z] = + this match { + case l @ Xor.Left(_) => Now(l) + case Xor.Right(b) => fc.map(_.map(f(b, _))) + } + def leftMap[C](f: A => C): C Xor B = this match { case Xor.Left(a) => Xor.Left(f(a)) case r @ Xor.Right(_) => r @@ -211,6 +217,8 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { } def raiseError[B](e: A): Xor[A, B] = Xor.left(e) override def map[B, C](fa: A Xor B)(f: B => C): A Xor C = fa.map(f) + override def map2Eval[B, C, Z](fb: A Xor B, fc: Eval[A Xor C])(f: (B, C) => Z): Eval[A Xor Z] = + fb.map2Eval(fc)(f) override def attempt[B](fab: A Xor B): A Xor (A Xor B) = Xor.right(fab) override def recover[B](fab: A Xor B)(pf: PartialFunction[A, B]): A Xor B = fab recover pf diff --git a/core/src/main/scala/cats/std/either.scala b/core/src/main/scala/cats/std/either.scala index cbabb0d8fa..567ee29aec 100644 --- a/core/src/main/scala/cats/std/either.scala +++ b/core/src/main/scala/cats/std/either.scala @@ -33,6 +33,15 @@ trait EitherInstances extends EitherInstances1 { override def map[B, C](fa: Either[A, B])(f: B => C): Either[A, C] = fa.right.map(f) + override def map2Eval[B, C, Z](fb: Either[A, B], fc: Eval[Either[A, C]])(f: (B, C) => Z): Eval[Either[A, Z]] = + fb match { + // This should be safe, but we are forced to use `asInstanceOf`, + // because `Left[+A, +B]` extends Either[A, B] instead of + // `Either[A, Nothing]` + case l @ Left(_) => Now(l.asInstanceOf[Either[A, Z]]) + case Right(b) => fc.map(_.right.map(f(b, _))) + } + def traverse[F[_], B, C](fa: Either[A, B])(f: B => F[C])(implicit F: Applicative[F]): F[Either[A, C]] = fa.fold( a => F.pure(Left(a)), diff --git a/core/src/main/scala/cats/std/list.scala b/core/src/main/scala/cats/std/list.scala index 372e91ab5d..57a169482e 100644 --- a/core/src/main/scala/cats/std/list.scala +++ b/core/src/main/scala/cats/std/list.scala @@ -47,11 +47,10 @@ trait ListInstances extends cats.kernel.std.ListInstances { Eval.defer(loop(fa)) } - def traverse[G[_], A, B](fa: List[A])(f: A => G[B])(implicit G: Applicative[G]): G[List[B]] = { - val gba = G.pure(Vector.empty[B]) - val gbb = fa.foldLeft(gba)((buf, a) => G.map2(buf, f(a))(_ :+ _)) - G.map(gbb)(_.toList) - } + def traverse[G[_], A, B](fa: List[A])(f: A => G[B])(implicit G: Applicative[G]): G[List[B]] = + foldRight[A, G[List[B]]](fa, Later(G.pure(List.empty))){ (a, lglb) => + G.map2Eval(f(a), lglb)(_ :: _) + }.value override def exists[A](fa: List[A])(p: A => Boolean): Boolean = fa.exists(p) diff --git a/core/src/main/scala/cats/std/map.scala b/core/src/main/scala/cats/std/map.scala index 9d4dd43b95..62df1505ea 100644 --- a/core/src/main/scala/cats/std/map.scala +++ b/core/src/main/scala/cats/std/map.scala @@ -14,12 +14,11 @@ trait MapInstances extends cats.kernel.std.MapInstances { implicit def mapInstance[K]: Traverse[Map[K, ?]] with FlatMap[Map[K, ?]] = new Traverse[Map[K, ?]] with FlatMap[Map[K, ?]] { - def traverse[G[_] : Applicative, A, B](fa: Map[K, A])(f: (A) => G[B]): G[Map[K, B]] = { - val G = Applicative[G] - val gba = G.pure(Map.empty[K, B]) - val gbb = fa.foldLeft(gba) { (buf, a) => - G.map2(buf, f(a._2))({ case(x, y) => x + (a._1 -> y)}) - } + def traverse[G[_], A, B](fa: Map[K, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[Map[K, B]] = { + val gba: Eval[G[Map[K, B]]] = Later(G.pure(Map.empty)) + val gbb = Foldable.iterateRight(fa.iterator, gba){ (kv, lbuf) => + G.map2Eval(f(kv._2), lbuf)({ (b, buf) => buf + (kv._1 -> b)}) + }.value G.map(gbb)(_.toMap) } diff --git a/core/src/main/scala/cats/std/option.scala b/core/src/main/scala/cats/std/option.scala index d70e978ea4..96c6d76658 100644 --- a/core/src/main/scala/cats/std/option.scala +++ b/core/src/main/scala/cats/std/option.scala @@ -21,6 +21,12 @@ trait OptionInstances extends cats.kernel.std.OptionInstances { override def map2[A, B, Z](fa: Option[A], fb: Option[B])(f: (A, B) => Z): Option[Z] = fa.flatMap(a => fb.map(b => f(a, b))) + override def map2Eval[A, B, Z](fa: Option[A], fb: Eval[Option[B]])(f: (A, B) => Z): Eval[Option[Z]] = + fa match { + case None => Now(None) + case Some(a) => fb.map(_.map(f(a, _))) + } + def coflatMap[A, B](fa: Option[A])(f: Option[A] => B): Option[B] = if (fa.isDefined) Some(f(fa)) else None diff --git a/core/src/main/scala/cats/std/stream.scala b/core/src/main/scala/cats/std/stream.scala index d865c37555..da26f238ba 100644 --- a/core/src/main/scala/cats/std/stream.scala +++ b/core/src/main/scala/cats/std/stream.scala @@ -41,11 +41,8 @@ trait StreamInstances extends cats.kernel.std.StreamInstances { // We use foldRight to avoid possible stack overflows. Since // we don't want to return a Eval[_] instance, we call .value // at the end. - // - // (We don't worry about internal laziness because traverse - // has to evaluate the entire stream anyway.) foldRight(fa, Later(init)) { (a, lgsb) => - lgsb.map(gsb => G.map2(f(a), gsb)(_ #:: _)) + G.map2Eval(f(a), lgsb)(_ #:: _) }.value } diff --git a/core/src/main/scala/cats/std/vector.scala b/core/src/main/scala/cats/std/vector.scala index 04d1b71133..9542394e42 100644 --- a/core/src/main/scala/cats/std/vector.scala +++ b/core/src/main/scala/cats/std/vector.scala @@ -45,7 +45,9 @@ trait VectorInstances extends cats.kernel.std.VectorInstances { } def traverse[G[_], A, B](fa: Vector[A])(f: A => G[B])(implicit G: Applicative[G]): G[Vector[B]] = - fa.foldLeft(G.pure(Vector.empty[B]))((buf, a) => G.map2(buf, f(a))(_ :+ _)) + foldRight[A, G[Vector[B]]](fa, Later(G.pure(Vector.empty))){ (a, lgvb) => + G.map2Eval(f(a), lgvb)(_ +: _) + }.value override def exists[A](fa: Vector[A])(p: A => Boolean): Boolean = fa.exists(p) diff --git a/tests/src/test/scala/cats/tests/EitherTests.scala b/tests/src/test/scala/cats/tests/EitherTests.scala index 61383c2365..d0ab3237ee 100644 --- a/tests/src/test/scala/cats/tests/EitherTests.scala +++ b/tests/src/test/scala/cats/tests/EitherTests.scala @@ -49,4 +49,10 @@ class EitherTests extends CatsSuite { show.show(e).nonEmpty should === (true) } } + + test("map2Eval is lazy") { + val bomb: Eval[Either[String, Int]] = Later(sys.error("boom")) + val x: Either[String, Int] = Left("l") + x.map2Eval(bomb)(_ + _).value should === (x) + } } diff --git a/tests/src/test/scala/cats/tests/OptionTests.scala b/tests/src/test/scala/cats/tests/OptionTests.scala index 717eca34b1..01ab65b3c7 100644 --- a/tests/src/test/scala/cats/tests/OptionTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTests.scala @@ -83,4 +83,9 @@ class OptionTests extends CatsSuite { // can't use `s.some should === (Some(null))` here, because it leads to NullPointerException s.some.exists(_ == null) should ===(true) } + + test("map2Eval is lazy") { + val bomb: Eval[Option[Int]] = Later(sys.error("boom")) + none[Int].map2Eval(bomb)(_ + _).value should === (None) + } } diff --git a/tests/src/test/scala/cats/tests/RegressionTests.scala b/tests/src/test/scala/cats/tests/RegressionTests.scala index 2bb7e87db6..912c79715d 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.data.Const +import cats.data.{Const, NonEmptyList, Xor} import scala.collection.mutable @@ -83,4 +83,44 @@ class RegressionTests extends CatsSuite { List(1,2,3).traverseU(i => Const(List(i))).getConst == List(1,2,3).foldMap(List(_)) ) } + + test("#513: traverse short circuits - Xor") { + var count = 0 + def validate(i: Int): Xor[String, Int] = { + count = count + 1 + if (i < 5) Xor.right(i) else Xor.left(s"$i is greater than 5") + } + + def checkAndResetCount(expected: Int): Unit = { + count should === (expected) + count = 0 + } + + List(1,2,6,8).traverseU(validate) should === (Xor.left("6 is greater than 5")) + // shouldn't have ever evaluted validate(8) + checkAndResetCount(3) + + Stream(1,2,6,8).traverseU(validate) should === (Xor.left("6 is greater than 5")) + checkAndResetCount(3) + + type StringMap[A] = Map[String, A] + val intMap: StringMap[Int] = Map("one" -> 1, "two" -> 2, "six" -> 6, "eight" -> 8) + intMap.traverseU(validate) should === (Xor.left("6 is greater than 5")) + checkAndResetCount(3) + + NonEmptyList(1,2,6,8).traverseU(validate) should === (Xor.left("6 is greater than 5")) + checkAndResetCount(3) + + NonEmptyList(6,8).traverseU(validate) should === (Xor.left("6 is greater than 5")) + checkAndResetCount(1) + + List(1,2,6,8).traverseU_(validate) should === (Xor.left("6 is greater than 5")) + checkAndResetCount(3) + + NonEmptyList(1,2,6,7,8).traverseU_(validate) should === (Xor.left("6 is greater than 5")) + checkAndResetCount(3) + + NonEmptyList(6,7,8).traverseU_(validate) should === (Xor.left("6 is greater than 5")) + checkAndResetCount(1) + } } diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index d4b04e04c3..ed123b545d 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -235,4 +235,10 @@ class XorTests extends CatsSuite { } } + test("map2Eval is lazy") { + val bomb: Eval[String Xor Int] = Later(sys.error("boom")) + val x = Xor.left[String, Int]("l") + x.map2Eval(bomb)(_ + _).value should === (x) + } + } From b8aaf1fbd0d1d7ce64e930f4ff074b8cc02a533e Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Wed, 4 May 2016 21:46:48 -0400 Subject: [PATCH 119/185] Use instance hierarchy for WriterT group instances In particular, this helps implicit resolution when `F` is `Id`. --- core/src/main/scala/cats/data/WriterT.scala | 32 ++++++++++++++++--- .../test/scala/cats/tests/WriterTTests.scala | 23 ++++++++++--- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index d6e59d83f5..2ee07cc5cd 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -76,10 +76,8 @@ private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { override def show(f: WriterT[F, L, V]): String = f.show } - implicit def writerTMonoid[F[_], L, V](implicit W: Monoid[F[(L, V)]]): Monoid[WriterT[F, L, V]] = new Monoid[WriterT[F, L, V]] { - def empty = WriterT[F, L, V](W.empty) - def combine(x: WriterT[F, L, V], y: WriterT[F, L, V]): WriterT[F, L, V] = WriterT(W.combine(x.run, y.run)) - } + implicit def writerTIdMonoid[L:Monoid, V:Monoid]: Monoid[WriterT[Id, L, V]] = + writerTMonoid[Id, L, V] } private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 { @@ -97,6 +95,9 @@ private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 implicit def writerTEq[F[_], L, V](implicit F: Eq[F[(L, V)]]): Eq[WriterT[F, L, V]] = F.on(_.run) + + implicit def writerTIdSemigroup[L:Semigroup, V:Semigroup]: Semigroup[WriterT[Id, L, V]] = + writerTSemigroup[Id, L, V] } private[data] sealed abstract class WriterTInstances1 extends WriterTInstances2 { @@ -105,6 +106,11 @@ private[data] sealed abstract class WriterTInstances1 extends WriterTInstances2 implicit val F0: MonadFilter[F] = F implicit val L0: Monoid[L] = L } + + implicit def writerTMonoid[F[_], L, V](implicit W: Monoid[F[(L, V)]]): Monoid[WriterT[F, L, V]] = + new WriterTMonoid[F, L, V] { + implicit val F0: Monoid[F[(L, V)]] = W + } } private[data] sealed abstract class WriterTInstances2 extends WriterTInstances3 { implicit def writerTMonadWriter[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadWriter[WriterT[F, L, ?], L] = @@ -112,6 +118,11 @@ private[data] sealed abstract class WriterTInstances2 extends WriterTInstances3 implicit val F0: Monad[F] = F implicit val L0: Monoid[L] = L } + + implicit def writerTSemigroup[F[_], L, V](implicit W: Semigroup[F[(L, V)]]): Semigroup[WriterT[F, L, V]] = + new WriterTSemigroup[F, L, V] { + implicit val F0: Semigroup[F[(L, V)]] = W + } } private[data] sealed abstract class WriterTInstances3 extends WriterTInstances4 { @@ -241,6 +252,19 @@ private[data] sealed trait WriterTMonadCombine[F[_], L] extends MonadCombine[Wri override implicit def F0: MonadCombine[F] } +private[data] sealed trait WriterTSemigroup[F[_], L, A] extends Semigroup[WriterT[F, L, A]] { + implicit def F0: Semigroup[F[(L, A)]] + + def combine(x: WriterT[F, L, A], y: WriterT[F, L, A]): WriterT[F, L, A] = + WriterT(F0.combine(x.run, y.run)) +} + +private[data] sealed trait WriterTMonoid[F[_], L, A] extends Monoid[WriterT[F, L, A]] with WriterTSemigroup[F, L, A]{ + override implicit def F0: Monoid[F[(L, A)]] + + def empty: WriterT[F, L, A] = WriterT(F0.empty) +} + trait WriterTFunctions { def putT[F[_], L, V](vf: F[V])(l: L)(implicit functorF: Functor[F]): WriterT[F, L, V] = WriterT(functorF.map(vf)(v => (l, v))) diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index 9cd8a04af3..f6223e879f 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -248,11 +248,24 @@ class WriterTTests extends CatsSuite { } { - //F[(L, V)] has a monoid - implicit val FLV: Monoid[ListWrapper[(ListWrapper[Int], ListWrapper[Int])]] = ListWrapper.monoid[(ListWrapper[Int], ListWrapper[Int])] + // F[(L, V)] has a monoid + implicit val FLV: Monoid[ListWrapper[(Int, Int)]] = ListWrapper.monoid[(Int, Int)] - Monoid[WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]] - Semigroup[WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]] - checkAll("WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]", kernel.laws.GroupLaws[WriterT[ListWrapper, ListWrapper[Int], ListWrapper[Int]]].monoid) + Monoid[WriterT[ListWrapper, Int, Int]] + Semigroup[WriterT[ListWrapper, Int, Int]] + checkAll("WriterT[ListWrapper, Int, Int]", kernel.laws.GroupLaws[WriterT[ListWrapper, Int, Int]].monoid) + + Monoid[WriterT[Id, Int, Int]] + Semigroup[WriterT[Id, Int, Int]] + } + + { + // F[(L, V)] has a semigroup + implicit val FLV: Semigroup[ListWrapper[(Int, Int)]] = ListWrapper.semigroup[(Int, Int)] + + Semigroup[WriterT[ListWrapper, Int, Int]] + checkAll("WriterT[ListWrapper, Int, Int]", kernel.laws.GroupLaws[WriterT[ListWrapper, Int, Int]].semigroup) + + Semigroup[WriterT[Id, Int, Int]] } } From 02587fb1a7be06f4225fa53d7c8ddf8ad2caec34 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Thu, 5 May 2016 07:00:21 -0400 Subject: [PATCH 120/185] Run free tests as part of validate command --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 088c0daee5..86dcde4eb7 100644 --- a/build.sbt +++ b/build.sbt @@ -338,7 +338,7 @@ lazy val publishSettings = Seq( ) ++ credentialSettings ++ sharedPublishSettings ++ sharedReleaseProcess // These aliases serialise the build for the benefit of Travis-CI. -addCommandAlias("buildJVM", ";macrosJVM/compile;coreJVM/compile;coreJVM/test;lawsJVM/compile;testsJVM/test;jvm/test;bench/test") +addCommandAlias("buildJVM", ";macrosJVM/compile;coreJVM/compile;coreJVM/test;lawsJVM/compile;freeJVM/compile;testsJVM/test;freeJVM/test;jvm/test;bench/test") addCommandAlias("validateJVM", ";scalastyle;buildJVM;makeSite") From c11ddcfe795598172de06a9ad111e32dfa23bb49 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Thu, 5 May 2016 09:20:08 -0400 Subject: [PATCH 121/185] Update documentation --- docs/src/main/tut/freeapplicative.md | 7 ++++++- docs/src/main/tut/freemonad.md | 5 ++++- docs/src/site/index.md | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/src/main/tut/freeapplicative.md b/docs/src/main/tut/freeapplicative.md index ea72908677..65f20329db 100644 --- a/docs/src/main/tut/freeapplicative.md +++ b/docs/src/main/tut/freeapplicative.md @@ -2,7 +2,7 @@ layout: default title: "FreeApplicatives" section: "data" -source: "core/src/main/scala/cats/free/FreeApplicative.scala" +source: "free/src/main/scala/cats/free/FreeApplicative.scala" scaladoc: "#cats.free.FreeApplicative" --- # Free Applicative @@ -12,6 +12,11 @@ computations as data and are useful for building embedded DSLs (EDSLs). However, from `Free` in that the kinds of operations they support are limited, much like the distinction between `Applicative` and `Monad`. +## Dependency + +If you'd like to use cats' free applicative, you'll need to add a library dependency +for the `cats-free` module. + ## Example Consider building an EDSL for validating strings - to keep things simple we'll just have a way to check a string is at least a certain size and to ensure the string contains numbers. diff --git a/docs/src/main/tut/freemonad.md b/docs/src/main/tut/freemonad.md index 3b63e9e882..00e6997356 100644 --- a/docs/src/main/tut/freemonad.md +++ b/docs/src/main/tut/freemonad.md @@ -2,7 +2,7 @@ layout: default title: "FreeMonads" section: "data" -source: "core/src/main/scala/cats/free/Free.scala" +source: "free/src/main/scala/cats/free/Free.scala" scaladoc: "#cats.free.Free" --- @@ -25,6 +25,9 @@ In particular, *free monads* provide a practical way to: ## Using Free Monads +If you'd like to use cats' free monad, you'll need to add a library dependency +for the `cats-free` module. + A good way to get a sense for how *free monads* work is to see them in action. The next section uses `Free[_]` to create an embedded DSL (Domain Specific Language). diff --git a/docs/src/site/index.md b/docs/src/site/index.md index 67d5966e6a..461b7a6526 100644 --- a/docs/src/site/index.md +++ b/docs/src/site/index.md @@ -30,6 +30,7 @@ functionality, you can pick-and-choose from amongst these modules * `cats-macros`: Macros used by Cats syntax (*required*). * `cats-core`: Core type classes and functionality (*required*). * `cats-laws`: Laws for testing type class instances. + * `cats-free`: Free structures such as the free monad, and supporting type classes. Release notes for Cats are available in [CHANGES.md](https://github.com/typelevel/cats/blob/master/CHANGES.md). From 01a94571ec59d60d13537f09163f07c38fd47897 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Thu, 5 May 2016 09:38:40 -0400 Subject: [PATCH 122/185] Update docs a bit more --- README.md | 2 ++ docs/src/site/index.md | 1 + 2 files changed, 3 insertions(+) diff --git a/README.md b/README.md index 76e302692f..c4c943efcc 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ functionality, you can pick-and-choose from amongst these modules * `cats-macros`: Macros used by Cats syntax (*required*). * `cats-core`: Core type classes and functionality (*required*). * `cats-laws`: Laws for testing type class instances. + * `cats-free`: Free structures such as the free monad, and supporting type classes. Release notes for Cats are available in [CHANGES.md](CHANGES.md). @@ -110,6 +111,7 @@ Initially Cats will support the following modules: * `macros`: Macro definitions needed for `core` and other projects. * `core`: Definitions for widely-used type classes and data types. * `laws`: The encoded laws for type classes, exported to assist third-party testing. + * `cats-free`: Free structures such as the free monad, and supporting type classes. * `tests`: Verifies the laws, and runs any other tests. Not published. As the type class families grow, it's possible that additional modules diff --git a/docs/src/site/index.md b/docs/src/site/index.md index 461b7a6526..7f04546cfc 100644 --- a/docs/src/site/index.md +++ b/docs/src/site/index.md @@ -87,6 +87,7 @@ In an attempt to be more modular, Cats is broken up into a number of sub-project * *core* - contains type class definitions (e.g. Functor, Applicative, Monad), essential datatypes, and type class instances for those datatypes and standard library types * *laws* - laws for the type classes, used to validate type class instances + * *cats-free* - free structures such as the free monad, and supporting type classes. * *tests* - tests that check type class instances with laws from *laws* * *docs* - The source for this website From 05bf655d38b71305348a179b73c2e6c12126a360 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 3 May 2016 10:02:45 +0200 Subject: [PATCH 123/185] Add MonadError.ensure method --- core/src/main/scala/cats/MonadError.scala | 10 ++++++- core/src/main/scala/cats/data/Xor.scala | 2 ++ core/src/main/scala/cats/syntax/all.scala | 1 + .../main/scala/cats/syntax/monadError.scala | 16 ++++++++++++ .../scala/cats/tests/MonadErrorSuite.scala | 26 +++++++++++++++++++ 5 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 core/src/main/scala/cats/syntax/monadError.scala create mode 100644 tests/src/test/scala/cats/tests/MonadErrorSuite.scala diff --git a/core/src/main/scala/cats/MonadError.scala b/core/src/main/scala/cats/MonadError.scala index 763b0b68ff..7748cdd036 100644 --- a/core/src/main/scala/cats/MonadError.scala +++ b/core/src/main/scala/cats/MonadError.scala @@ -5,7 +5,15 @@ package cats * * This type class allows one to abstract over error-handling monads. */ -trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] +trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] { + + /** + * Turns a successful value into an error if it does not satisfy a given predicate. + */ + def ensure[A](fa: F[A])(error: => E)(predicate: A => Boolean): F[A] = + flatMap(fa)(a => if (predicate(a)) pure(a) else raiseError(error)) + +} object MonadError { def apply[F[_], E](implicit F: MonadError[F, E]): MonadError[F, E] = F diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index 470fa448d4..007aceac1e 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -208,6 +208,8 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { fab recover pf override def recoverWith[B](fab: A Xor B)(pf: PartialFunction[A, A Xor B]): A Xor B = fab recoverWith pf + override def ensure[B](fab: A Xor B)(error: => A)(predicate: B => Boolean): A Xor B = + fab.ensure(error)(predicate) } } diff --git a/core/src/main/scala/cats/syntax/all.scala b/core/src/main/scala/cats/syntax/all.scala index bbb196d91b..e325045130 100644 --- a/core/src/main/scala/cats/syntax/all.scala +++ b/core/src/main/scala/cats/syntax/all.scala @@ -22,6 +22,7 @@ trait AllSyntax with InvariantSyntax with ListSyntax with MonadCombineSyntax + with MonadErrorSyntax with MonadFilterSyntax with OptionSyntax with OrderSyntax diff --git a/core/src/main/scala/cats/syntax/monadError.scala b/core/src/main/scala/cats/syntax/monadError.scala new file mode 100644 index 0000000000..93023431d5 --- /dev/null +++ b/core/src/main/scala/cats/syntax/monadError.scala @@ -0,0 +1,16 @@ +package cats +package syntax + +trait MonadErrorSyntax { + + implicit def monadErrorSyntax[F[_], E, A](fa: F[A])(implicit F: MonadError[F, E]): MonadErrorOps[F, E, A] = + new MonadErrorOps(fa) + +} + +final class MonadErrorOps[F[_], E, A](fa: F[A])(implicit F: MonadError[F, E]) { + + def ensure(error: => E)(predicate: A => Boolean): F[A] = + F.ensure(fa)(error)(predicate) + +} diff --git a/tests/src/test/scala/cats/tests/MonadErrorSuite.scala b/tests/src/test/scala/cats/tests/MonadErrorSuite.scala new file mode 100644 index 0000000000..3b752339e1 --- /dev/null +++ b/tests/src/test/scala/cats/tests/MonadErrorSuite.scala @@ -0,0 +1,26 @@ +package cats +package tests + +import cats.data.Xor + +class MonadErrorSuite extends CatsSuite { + + type ErrorOr[A] = String Xor A + + val successful: ErrorOr[Int] = 42.right + val failed: ErrorOr[Int] = "Oops".left + + test("ensure raises an error if the predicate fails") { + monadErrorSyntax(successful).ensure("Error")(i => false) should === ("Error".left) + } + + test("ensure returns the successful value if the predicate succeeds") { + monadErrorSyntax(successful).ensure("Error")(i => true) should === (successful) + } + + test("ensure returns the failure, when applied to a failure") { + monadErrorSyntax(failed).ensure("Error")(i => false) should === (failed) + monadErrorSyntax(failed).ensure("Error")(i => true) should === (failed) + } + +} From 3c544483cbad2f1ffe7c67f04919e6828ce30c67 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Fri, 6 May 2016 07:06:52 -0400 Subject: [PATCH 124/185] Remove blocking (JVM-only) Future instances Fixes #589. If anyone is set on using these, they can be added to alleycats. However, I suspect that few to no people are using these. They all throw exceptions on failed futures, which seems like it would be a show-stopper in most codebases. --- build.sbt | 13 ++--- jvm/src/main/scala/cats/jvm/std/future.scala | 43 ---------------- .../test/scala/cats/tests/FutureTests.scala | 49 ------------------- 3 files changed, 3 insertions(+), 102 deletions(-) delete mode 100644 jvm/src/main/scala/cats/jvm/std/future.scala delete mode 100644 jvm/src/test/scala/cats/tests/FutureTests.scala diff --git a/build.sbt b/build.sbt index 86dcde4eb7..429364ec8a 100644 --- a/build.sbt +++ b/build.sbt @@ -148,8 +148,8 @@ lazy val catsJVM = project.in(file(".catsJVM")) .settings(moduleName := "cats") .settings(catsSettings) .settings(commonJvmSettings) - .aggregate(macrosJVM, kernelJVM, kernelLawsJVM, coreJVM, lawsJVM, freeJVM, testsJVM, jvm, docs, bench) - .dependsOn(macrosJVM, kernelJVM, kernelLawsJVM, coreJVM, lawsJVM, freeJVM, testsJVM % "test-internal -> test", jvm, bench % "compile-internal;test-internal -> test") + .aggregate(macrosJVM, kernelJVM, kernelLawsJVM, coreJVM, lawsJVM, freeJVM, testsJVM, docs, bench) + .dependsOn(macrosJVM, kernelJVM, kernelLawsJVM, coreJVM, lawsJVM, freeJVM, testsJVM % "test-internal -> test", bench % "compile-internal;test-internal -> test") lazy val catsJS = project.in(file(".catsJS")) .settings(moduleName := "cats") @@ -247,13 +247,6 @@ lazy val tests = crossProject.crossType(CrossType.Pure) lazy val testsJVM = tests.jvm lazy val testsJS = tests.js -// cats-jvm is JVM-only -lazy val jvm = project - .dependsOn(macrosJVM, coreJVM, testsJVM % "test-internal -> test") - .settings(moduleName := "cats-jvm") - .settings(catsSettings:_*) - .settings(commonJvmSettings:_*) - // bench is currently JVM-only lazy val bench = project.dependsOn(macrosJVM, coreJVM, freeJVM, lawsJVM) .settings(moduleName := "cats-bench") @@ -338,7 +331,7 @@ lazy val publishSettings = Seq( ) ++ credentialSettings ++ sharedPublishSettings ++ sharedReleaseProcess // These aliases serialise the build for the benefit of Travis-CI. -addCommandAlias("buildJVM", ";macrosJVM/compile;coreJVM/compile;coreJVM/test;lawsJVM/compile;freeJVM/compile;testsJVM/test;freeJVM/test;jvm/test;bench/test") +addCommandAlias("buildJVM", ";macrosJVM/compile;coreJVM/compile;coreJVM/test;lawsJVM/compile;freeJVM/compile;testsJVM/test;freeJVM/test;bench/test") addCommandAlias("validateJVM", ";scalastyle;buildJVM;makeSite") diff --git a/jvm/src/main/scala/cats/jvm/std/future.scala b/jvm/src/main/scala/cats/jvm/std/future.scala deleted file mode 100644 index b01d8eb89b..0000000000 --- a/jvm/src/main/scala/cats/jvm/std/future.scala +++ /dev/null @@ -1,43 +0,0 @@ -package cats -package jvm -package std - -import scala.concurrent.{Await, Future} -import scala.concurrent.{ExecutionContext => E} -import scala.concurrent.duration.FiniteDuration - -import cats.std.FutureCoflatMap -import cats.syntax.all._ - -object future extends FutureInstances0 - -private[std] sealed trait FutureInstances0 extends FutureInstances1 { - def futureComonad(atMost: FiniteDuration)(implicit ec: E): Comonad[Future] = - new FutureCoflatMap with Comonad[Future] { - def extract[A](x: Future[A]): A = - Await.result(x, atMost) - } - - def futureOrder[A: Order](atMost: FiniteDuration)(implicit ec: E): Order[Future[A]] = - new Order[Future[A]] { - def compare(x: Future[A], y: Future[A]): Int = - Await.result((x zip y).map { case (x, y) => x compare y }, atMost) - } -} - -private[std] sealed trait FutureInstances1 extends FutureInstances2 { - def futurePartialOrder[A: PartialOrder](atMost: FiniteDuration)(implicit ec: E): PartialOrder[Future[A]] = - new PartialOrder[Future[A]] { - def partialCompare(x: Future[A], y: Future[A]): Double = - Await.result((x zip y).map { case (x, y) => x partialCompare y }, atMost) - } - -} - -private[std] sealed trait FutureInstances2 { - def futureEq[A: Eq](atMost: FiniteDuration)(implicit ec: E): Eq[Future[A]] = - new Eq[Future[A]] { - def eqv(x: Future[A], y: Future[A]): Boolean = - Await.result((x zip y).map { case (x, y) => x === y }, atMost) - } -} diff --git a/jvm/src/test/scala/cats/tests/FutureTests.scala b/jvm/src/test/scala/cats/tests/FutureTests.scala deleted file mode 100644 index 8fba502584..0000000000 --- a/jvm/src/test/scala/cats/tests/FutureTests.scala +++ /dev/null @@ -1,49 +0,0 @@ -package cats -package jvm -package tests - -import cats.data.Xor -import cats.laws.discipline._ -import cats.laws.discipline.arbitrary.evalArbitrary -import cats.jvm.std.future.futureComonad -import cats.tests.CatsSuite - -import scala.concurrent.{Await, Future} -import scala.concurrent.duration._ -import scala.concurrent.ExecutionContext.Implicits.global - -import org.scalacheck.Arbitrary -import org.scalacheck.Arbitrary.arbitrary - -class FutureTests extends CatsSuite { - val timeout = 3.seconds - - def futureXor[A](f: Future[A]): Future[Xor[Throwable, A]] = - f.map(Xor.right[Throwable, A]).recover { case t => Xor.left(t) } - - implicit def eqfa[A: Eq]: Eq[Future[A]] = - new Eq[Future[A]] { - def eqv(fx: Future[A], fy: Future[A]): Boolean = { - val fz = futureXor(fx) zip futureXor(fy) - Await.result(fz.map { case (tx, ty) => tx === ty }, timeout) - } - } - - implicit val throwableEq: Eq[Throwable] = - Eq.fromUniversalEquals - - implicit val comonad: Comonad[Future] = futureComonad(timeout) - - // Need non-fatal Throwables for Future recoverWith/handleError - implicit val nonFatalArbitrary: Arbitrary[Throwable] = - Arbitrary(arbitrary[Exception].map(identity)) - - checkAll("Future[Int]", MonadErrorTests[Future, Throwable].monadError[Int, Int, Int]) - checkAll("Future[Int]", ComonadTests[Future].comonad[Int, Int, Int]) - - test("pureEval lifts a potentially lazy value into Future") { - forAll { e: Eval[Int] => - e.pureEval[Future].extract should === (e.value) - } - } -} From 0e13ff9b79672f5e401249f7ae20f4c9234f04a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20G=C3=B3mez?= Date: Fri, 6 May 2016 16:43:01 +0200 Subject: [PATCH 125/185] Use Future#successful in pureEval when possible Since `Now` already has performed the work required to calculate the result value, we can lift the result into a Future using `Future#successful` instead of unwrapping the `Now` inside the block run by the `Future` (which potentially has to use another thread). --- core/src/main/scala/cats/std/future.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/std/future.scala b/core/src/main/scala/cats/std/future.scala index 3c74ddc723..e1453870d6 100644 --- a/core/src/main/scala/cats/std/future.scala +++ b/core/src/main/scala/cats/std/future.scala @@ -13,7 +13,10 @@ trait FutureInstances extends FutureInstances1 { new FutureCoflatMap with MonadError[Future, Throwable]{ def pure[A](x: A): Future[A] = Future.successful(x) - override def pureEval[A](x: Eval[A]): Future[A] = Future(x.value) + override def pureEval[A](x: Eval[A]): Future[A] = x match { + case Now(x) => Future.successful(x) + case _ => Future(x.value) + } def flatMap[A, B](fa: Future[A])(f: A => Future[B]): Future[B] = fa.flatMap(f) From 7ef5e5b1af9555852c0f31e281622bdc23f4d775 Mon Sep 17 00:00:00 2001 From: peterneyens Date: Sat, 7 May 2016 17:58:02 +0200 Subject: [PATCH 126/185] Add Traverse.traverseM and add examples to ScalaDoc for traverse syntax. --- core/src/main/scala/cats/Traverse.scala | 6 ++ .../src/main/scala/cats/syntax/traverse.scala | 87 ++++++++++++++++++- .../test/scala/cats/tests/SyntaxTests.scala | 9 +- 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/cats/Traverse.scala b/core/src/main/scala/cats/Traverse.scala index 08ad00ddb3..299acb844b 100644 --- a/core/src/main/scala/cats/Traverse.scala +++ b/core/src/main/scala/cats/Traverse.scala @@ -29,6 +29,12 @@ import simulacrum.typeclass def traverseU[A, GB](fa: F[A])(f: A => GB)(implicit U: Unapply[Applicative, GB]): U.M[F[U.A]] = U.TC.traverse(fa)(a => U.subst(f(a)))(this) + /** + * A traverse followed by flattening the inner result. + */ + def traverseM[G[_], A, B](fa: F[A])(f: A => G[F[B]])(implicit G: Applicative[G], F: FlatMap[F]): G[F[B]] = + G.map(traverse(fa)(f))(F.flatten) + /** * Thread all the G effects through the F structure to invert the * structure from F[G[A]] to G[F[A]]. diff --git a/core/src/main/scala/cats/syntax/traverse.scala b/core/src/main/scala/cats/syntax/traverse.scala index 0fb0c866cc..56ac664682 100644 --- a/core/src/main/scala/cats/syntax/traverse.scala +++ b/core/src/main/scala/cats/syntax/traverse.scala @@ -16,17 +16,98 @@ trait TraverseSyntax extends TraverseSyntax1 { } final class TraverseOps[F[_], A](fa: F[A])(implicit F: Traverse[F]) { - def traverse[G[_]: Applicative, B](f: A => G[B]): G[F[B]] = F.traverse(fa)(f) + /** + * @see [[Traverse.traverse]] + * + * Example: + * {{{ + * scala> import cats.data.Xor + * scala> import cats.std.list._ + * scala> import cats.std.option._ + * scala> import cats.syntax.traverse._ + * scala> def parseInt(s: String): Option[Int] = Xor.catchOnly[NumberFormatException](s.toInt).toOption + * scala> List("1", "2", "3").traverse(parseInt) + * res0: Option[List[Int]] = Some(List(1, 2, 3)) + * scala> List("1", "two", "3").traverse(parseInt) + * res1: Option[List[Int]] = None + * }}} + */ + def traverse[G[_]: Applicative, B](f: A => G[B]): G[F[B]] = + F.traverse(fa)(f) + /** + * @see [[Traverse.traverse]] + * + * Example: + * {{{ + * scala> import cats.data.Xor + * scala> import cats.std.list._ + * scala> import cats.syntax.traverse._ + * scala> def parseInt(s: String): Xor[String, Int] = Xor.catchOnly[NumberFormatException](s.toInt).leftMap(_ => "no number") + * scala> val ns = List("1", "2", "3") + * scala> ns.traverseU(parseInt) + * res0: Xor[String, List[Int]] = Right(List(1, 2, 3)) + * scala> ns.traverse[Xor[String, ?], Int](parseInt) + * res1: Xor[String, List[Int]] = Right(List(1, 2, 3)) + * }}} + */ def traverseU[GB](f: A => GB)(implicit U: Unapply[Applicative, GB]): U.M[F[U.A]] = F.traverseU[A, GB](fa)(f)(U) - def sequence[G[_], B](implicit G: Applicative[G], ev: A =:= G[B]): G[F[B]] = + /** + * @see [[Traverse.traverseM]] + * + * Example: + * {{{ + * scala> import cats.data.Xor + * scala> import cats.std.list._ + * scala> import cats.std.option._ + * scala> import cats.syntax.traverse._ + * scala> def parseInt(s: String): Option[Int] = Xor.catchOnly[NumberFormatException](s.toInt).toOption + * scala> val x = Option(List("1", "two", "3")) + * scala> x.traverseM(_.map(parseInt)) + * res0: List[Option[Int]] = List(Some(1), None, Some(3)) + * }}} + */ + def traverseM[G[_]: Applicative, B](f: A => G[F[B]])(implicit F2: FlatMap[F]): G[F[B]] = + F.traverseM(fa)(f) + + /** + * @see [[Traverse.sequence]] + * + * Example: + * {{{ + * scala> import cats.std.list._ + * scala> import cats.std.option._ + * scala> import cats.syntax.traverse._ + * scala> val x: List[Option[Int]] = List(Some(1), Some(2)) + * scala> val y: List[Option[Int]] = List(None, Some(2)) + * scala> x.sequence + * res0: Option[List[Int]] = Some(List(1, 2)) + * scala> y.sequence + * res1: Option[List[Int]] = None + * }}} + */ + def sequence[G[_], B](implicit G: Applicative[G], ev: A =:= G[B]): G[F[B]] = F.sequence(fa.asInstanceOf[F[G[B]]]) + /** + * @see [[Traverse.sequenceU]] + * + * Example: + * {{{ + * scala> import cats.data.{Validated, ValidatedNel} + * scala> import cats.std.list._ + * scala> import cats.syntax.traverse._ + * scala> val x: List[ValidatedNel[String, Int]] = List(Validated.valid(1), Validated.invalid("a"), Validated.invalid("b")).map(_.toValidatedNel) + * scala> x.sequenceU + * res0: cats.data.ValidatedNel[String,List[Int]] = Invalid(OneAnd(a,List(b))) + * scala> x.sequence[ValidatedNel[String, ?], Int] + * res1: cats.data.ValidatedNel[String,List[Int]] = Invalid(OneAnd(a,List(b))) + * }}} + */ def sequenceU(implicit U: Unapply[Applicative,A]): U.M[F[U.A]] = F.sequenceU[A](fa)(U) - } final class NestedTraverseOps[F[_], G[_], A](fga: F[G[A]])(implicit F: Traverse[F]) { diff --git a/tests/src/test/scala/cats/tests/SyntaxTests.scala b/tests/src/test/scala/cats/tests/SyntaxTests.scala index 9208c26b06..8228c73e68 100644 --- a/tests/src/test/scala/cats/tests/SyntaxTests.scala +++ b/tests/src/test/scala/cats/tests/SyntaxTests.scala @@ -110,7 +110,14 @@ class SyntaxTests extends AllInstances with AllSyntax { val as2: List[A] = fa.dropWhile_(f5) } - def testTraverse[F[_]: Traverse, G[_]: Applicative, A]: Unit = { + def testTraverse[F[_]: Traverse: FlatMap, G[_]: Applicative, A, B]: Unit = { + val fa = mock[F[A]] + val f1 = mock[A => G[B]] + val gfb: G[F[B]] = fa.traverse(f1) + + val f2 = mock[A => G[F[B]]] + val gfb2: G[F[B]] = fa.traverseM(f2) + val fga = mock[F[G[A]]] val gunit: G[F[A]] = fga.sequence } From 331e739a87f37ee76d4ef71d4e9c83d58d088763 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Sun, 8 May 2016 10:09:24 -0400 Subject: [PATCH 127/185] Add law-checking for asMeetPartialOrder and asJoinPartialOrder --- kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala index d504f99114..b6e60c85a4 100644 --- a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala +++ b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala @@ -36,6 +36,8 @@ class LawTests extends FunSuite with Discipline { laws[OrderLaws, List[HasPartialOrder[Int]]].check(_.partialOrder) laws[OrderLaws, Vector[HasPartialOrder[Int]]].check(_.partialOrder) laws[OrderLaws, Stream[HasPartialOrder[Int]]].check(_.partialOrder) + laws[OrderLaws, Set[Int]]("asMeetPartialOrder").check(_.partialOrder(Semilattice.asMeetPartialOrder[Set[Int]])) + laws[OrderLaws, Set[Int]]("asJoinPartialOrder").check(_.partialOrder(Semilattice.asJoinPartialOrder[Set[Int]])) laws[OrderLaws, Unit].check(_.order) laws[OrderLaws, Boolean].check(_.order) From 4edc956189c3f10a0ea7cd3dc3d21ac4fe82eff8 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Mon, 9 May 2016 11:21:11 -0400 Subject: [PATCH 128/185] Run kernel-laws tests as part of build --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 86dcde4eb7..b292aa2980 100644 --- a/build.sbt +++ b/build.sbt @@ -338,11 +338,11 @@ lazy val publishSettings = Seq( ) ++ credentialSettings ++ sharedPublishSettings ++ sharedReleaseProcess // These aliases serialise the build for the benefit of Travis-CI. -addCommandAlias("buildJVM", ";macrosJVM/compile;coreJVM/compile;coreJVM/test;lawsJVM/compile;freeJVM/compile;testsJVM/test;freeJVM/test;jvm/test;bench/test") +addCommandAlias("buildJVM", ";macrosJVM/compile;coreJVM/compile;kenerelLawsJVM/compile;lawsJVM/compile;freeJVM/compile;kernelLawsJVM/test;coreJVM/test;testsJVM/test;freeJVM/test;jvm/test;bench/test") addCommandAlias("validateJVM", ";scalastyle;buildJVM;makeSite") -addCommandAlias("validateJS", ";macrosJS/compile;coreJS/compile;lawsJS/compile;testsJS/test;js/test") +addCommandAlias("validateJS", ";macrosJS/compile;kernelJS/compile;coreJS/compile;kernelLawsJS/compile;lawsJS/compile;kernelLawsJS/test;testsJS/test;js/test") addCommandAlias("validate", ";validateJS;validateJVM") From e21b6e8201796eef1eac252b6d1a83525fff899d Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Mon, 9 May 2016 11:44:35 -0400 Subject: [PATCH 129/185] Fix misspelled "kernel" in build --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index b292aa2980..7977a15cfb 100644 --- a/build.sbt +++ b/build.sbt @@ -338,7 +338,7 @@ lazy val publishSettings = Seq( ) ++ credentialSettings ++ sharedPublishSettings ++ sharedReleaseProcess // These aliases serialise the build for the benefit of Travis-CI. -addCommandAlias("buildJVM", ";macrosJVM/compile;coreJVM/compile;kenerelLawsJVM/compile;lawsJVM/compile;freeJVM/compile;kernelLawsJVM/test;coreJVM/test;testsJVM/test;freeJVM/test;jvm/test;bench/test") +addCommandAlias("buildJVM", ";macrosJVM/compile;coreJVM/compile;kernelLawsJVM/compile;lawsJVM/compile;freeJVM/compile;kernelLawsJVM/test;coreJVM/test;testsJVM/test;freeJVM/test;jvm/test;bench/test") addCommandAlias("validateJVM", ";scalastyle;buildJVM;makeSite") From 29cd7adb0bda5dd2357807d87744d18a74c24af0 Mon Sep 17 00:00:00 2001 From: Markus Hauck Date: Tue, 10 May 2016 08:30:48 +0200 Subject: [PATCH 130/185] Add XorT#fromEither --- core/src/main/scala/cats/data/XorT.scala | 7 +++++++ tests/src/test/scala/cats/tests/XorTTests.scala | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index 60af12734c..a3bfb686ec 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -168,6 +168,13 @@ trait XorTFunctions { def apply[E, A](xor: Xor[E, A])(implicit F: Applicative[F]): XorT[F, E, A] = XorT(F.pure(xor)) } + + final def fromEither[F[_]]: FromEitherPartiallyApplied[F] = new FromEitherPartiallyApplied + + final class FromEitherPartiallyApplied[F[_]] private[XorTFunctions] { + def apply[E, A](eit: Either[E,A])(implicit F: Applicative[F]): XorT[F, E, A] = + XorT(F.pure(Xor.fromEither(eit))) + } } private[data] abstract class XorTInstances extends XorTInstances1 { diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index 0bd8e66977..13c279a76b 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -70,6 +70,12 @@ class XorTTests extends CatsSuite { } } + test("fromEither") { + forAll { (either: Either[String, Int]) => + Some(either.isLeft) should === (XorT.fromEither[Option](either).isLeft) + } + } + test("isLeft negation of isRight") { forAll { (xort: XorT[List, String, Int]) => xort.isLeft should === (xort.isRight.map(! _)) From caaf3f9aedf11a21d3012bff0c103bc366bdf74e Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Tue, 10 May 2016 06:29:44 -0400 Subject: [PATCH 131/185] Override reverse on reversed PartialOrder to return original instance Also add some law-checking for reversed and double-reversed Order/PartialOrder instances. --- .../src/test/scala/cats/kernel/laws/LawTests.scala | 4 ++++ kernel/src/main/scala/cats/kernel/PartialOrder.scala | 12 +++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala index b6e60c85a4..57ee14aa15 100644 --- a/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala +++ b/kernel-laws/src/test/scala/cats/kernel/laws/LawTests.scala @@ -32,6 +32,8 @@ class LawTests extends FunSuite with Discipline { laws[OrderLaws, Stream[HasEq[Int]]].check(_.eqv) laws[OrderLaws, Set[Int]].check(_.partialOrder) + laws[OrderLaws, Set[Int]]("reverse").check(_.partialOrder(PartialOrder[Set[Int]].reverse)) + laws[OrderLaws, Set[Int]]("reverse.reverse").check(_.partialOrder(PartialOrder[Set[Int]].reverse.reverse)) laws[OrderLaws, Option[HasPartialOrder[Int]]].check(_.partialOrder) laws[OrderLaws, List[HasPartialOrder[Int]]].check(_.partialOrder) laws[OrderLaws, Vector[HasPartialOrder[Int]]].check(_.partialOrder) @@ -53,6 +55,8 @@ class LawTests extends FunSuite with Discipline { laws[OrderLaws, Vector[Int]].check(_.order) laws[OrderLaws, Stream[Int]].check(_.order) laws[OrderLaws, Int]("fromOrdering").check(_.order(Order.fromOrdering[Int])) + laws[OrderLaws, Int]("reverse").check(_.order(Order[Int].reverse)) + laws[OrderLaws, Int]("reverse.reverse").check(_.order(Order[Int].reverse.reverse)) laws[GroupLaws, String].check(_.monoid) laws[GroupLaws, Option[Int]].check(_.monoid) diff --git a/kernel/src/main/scala/cats/kernel/PartialOrder.scala b/kernel/src/main/scala/cats/kernel/PartialOrder.scala index c962ca5444..fa7aeb607a 100644 --- a/kernel/src/main/scala/cats/kernel/PartialOrder.scala +++ b/kernel/src/main/scala/cats/kernel/PartialOrder.scala @@ -5,17 +5,17 @@ import scala.{specialized => sp} /** * The `PartialOrder` type class is used to define a partial ordering on some type `A`. - * + * * A partial order is defined by a relation <=, which obeys the following laws: - * + * * - x <= x (reflexivity) * - if x <= y and y <= x, then x = y (anti-symmetry) * - if x <= y and y <= z, then x <= z (transitivity) - * + * * To compute both <= and >= at the same time, we use a Double number * to encode the result of the comparisons x <= y and x >= y. * The truth table is defined as follows: - * + * * x <= y x >= y Double * true true = 0.0 (corresponds to x = y) * false false = NaN (x and y cannot be compared) @@ -41,7 +41,7 @@ trait PartialOrder[@sp A] extends Any with Eq[A] { self => * - negative iff `x < y` * - zero iff `x = y` * - positive iff `x > y` - */ + */ def tryCompare(x: A, y: A): Option[Int] = { val c = partialCompare(x, y) if (isNaN(c)) None else Some(c.signum) @@ -82,6 +82,8 @@ trait PartialOrder[@sp A] extends Any with Eq[A] { self => def reverse: PartialOrder[A] = new PartialOrder[A] { def partialCompare(x: A, y: A): Double = self.partialCompare(y, x) + + override def reverse: PartialOrder[A] = self } // The following may be overridden for performance: From 41d46600ab095755bec832041ca6bcb4c02726c1 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Tue, 10 May 2016 11:51:28 -0400 Subject: [PATCH 132/185] Use Always instead of Later in some traverse impls This avoids a bit of memoization overhead in places that we only expect to be evaluated once. --- core/src/main/scala/cats/Foldable.scala | 2 +- core/src/main/scala/cats/data/OneAnd.scala | 2 +- core/src/main/scala/cats/std/list.scala | 2 +- core/src/main/scala/cats/std/map.scala | 2 +- core/src/main/scala/cats/std/vector.scala | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/scala/cats/Foldable.scala b/core/src/main/scala/cats/Foldable.scala index bf1b865c04..a25943547c 100644 --- a/core/src/main/scala/cats/Foldable.scala +++ b/core/src/main/scala/cats/Foldable.scala @@ -107,7 +107,7 @@ import simulacrum.typeclass * needed. */ def traverse_[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] = - foldRight(fa, Later(G.pure(()))) { (a, acc) => + foldRight(fa, Always(G.pure(()))) { (a, acc) => G.map2Eval(f(a), acc) { (_, _) => () } }.value diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 3e8a36ba51..84f81d9bc7 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -170,7 +170,7 @@ trait OneAndLowPriority2 extends OneAndLowPriority1 { implicit def oneAndTraverse[F[_]](implicit F: Traverse[F]): Traverse[OneAnd[F, ?]] = new Traverse[OneAnd[F, ?]] { def traverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[OneAnd[F, B]] = { - G.map2Eval(f(fa.head), Later(F.traverse(fa.tail)(f)))(OneAnd(_, _)).value + G.map2Eval(f(fa.head), Always(F.traverse(fa.tail)(f)))(OneAnd(_, _)).value } def foldLeft[A, B](fa: OneAnd[F, A], b: B)(f: (B, A) => B): B = { diff --git a/core/src/main/scala/cats/std/list.scala b/core/src/main/scala/cats/std/list.scala index 57a169482e..85a5231740 100644 --- a/core/src/main/scala/cats/std/list.scala +++ b/core/src/main/scala/cats/std/list.scala @@ -48,7 +48,7 @@ trait ListInstances extends cats.kernel.std.ListInstances { } def traverse[G[_], A, B](fa: List[A])(f: A => G[B])(implicit G: Applicative[G]): G[List[B]] = - foldRight[A, G[List[B]]](fa, Later(G.pure(List.empty))){ (a, lglb) => + foldRight[A, G[List[B]]](fa, Always(G.pure(List.empty))){ (a, lglb) => G.map2Eval(f(a), lglb)(_ :: _) }.value diff --git a/core/src/main/scala/cats/std/map.scala b/core/src/main/scala/cats/std/map.scala index 62df1505ea..3b6d4d4a2b 100644 --- a/core/src/main/scala/cats/std/map.scala +++ b/core/src/main/scala/cats/std/map.scala @@ -15,7 +15,7 @@ trait MapInstances extends cats.kernel.std.MapInstances { new Traverse[Map[K, ?]] with FlatMap[Map[K, ?]] { def traverse[G[_], A, B](fa: Map[K, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[Map[K, B]] = { - val gba: Eval[G[Map[K, B]]] = Later(G.pure(Map.empty)) + val gba: Eval[G[Map[K, B]]] = Always(G.pure(Map.empty)) val gbb = Foldable.iterateRight(fa.iterator, gba){ (kv, lbuf) => G.map2Eval(f(kv._2), lbuf)({ (b, buf) => buf + (kv._1 -> b)}) }.value diff --git a/core/src/main/scala/cats/std/vector.scala b/core/src/main/scala/cats/std/vector.scala index 9542394e42..fd6591127a 100644 --- a/core/src/main/scala/cats/std/vector.scala +++ b/core/src/main/scala/cats/std/vector.scala @@ -45,7 +45,7 @@ trait VectorInstances extends cats.kernel.std.VectorInstances { } def traverse[G[_], A, B](fa: Vector[A])(f: A => G[B])(implicit G: Applicative[G]): G[Vector[B]] = - foldRight[A, G[Vector[B]]](fa, Later(G.pure(Vector.empty))){ (a, lgvb) => + foldRight[A, G[Vector[B]]](fa, Always(G.pure(Vector.empty))){ (a, lgvb) => G.map2Eval(f(a), lgvb)(_ +: _) }.value From cc0669677ec91224484967941b0194f1688d421f Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Tue, 10 May 2016 21:57:50 -0400 Subject: [PATCH 133/185] Remove unnecessary nelSemigroup from traverse doc The necessary `Semigroup` instance has been added to cats, so this is no longer necessary. --- docs/src/main/tut/traverse.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/docs/src/main/tut/traverse.md b/docs/src/main/tut/traverse.md index 66bcaf4738..9281a90b21 100644 --- a/docs/src/main/tut/traverse.md +++ b/docs/src/main/tut/traverse.md @@ -108,19 +108,7 @@ Examples. val x1 = List("1", "2", "3").traverseU(parseIntXor) val x2 = List("1", "abc", "3").traverseU(parseIntXor) val x3 = List("1", "abc", "def").traverseU(parseIntXor) -``` - -We need proof that `NonEmptyList[A]` is a `Semigroup `for there to be an `Applicative` instance for -`ValidatedNel`. -```tut:silent -implicit def nelSemigroup[A]: Semigroup[NonEmptyList[A]] = - OneAnd.oneAndSemigroupK[List].algebra[A] -``` - -Thus. - -```tut val v1 = List("1", "2", "3").traverseU(parseIntValidated) val v2 = List("1", "abc", "3").traverseU(parseIntValidated) val v3 = List("1", "abc", "def").traverseU(parseIntValidated) From 2869e7f42e1fd93169d02527b1bbae80da993c55 Mon Sep 17 00:00:00 2001 From: Stephen Carman Date: Wed, 11 May 2016 11:17:17 -0400 Subject: [PATCH 134/185] Added withFilter for OptionT --- core/src/main/scala/cats/data/OptionT.scala | 5 ++++- tests/src/test/scala/cats/tests/OptionTTests.scala | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index 4ab1d31eb7..6db53b4243 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -2,7 +2,7 @@ package cats package data /** - * `OptionT[F[_], A` is a light wrapper on an `F[Option[A]]` with some + * `OptionT[F[_], A]` is a light wrapper on an `F[Option[A]]` with some * convenient methods for working with this nested structure. * * It may also be said that `OptionT` is a monad transformer for `Option`. @@ -59,6 +59,9 @@ final case class OptionT[F[_], A](value: F[Option[A]]) { def filter(p: A => Boolean)(implicit F: Functor[F]): OptionT[F, A] = OptionT(F.map(value)(_.filter(p))) + def withFilter(p: A => Boolean)(implicit F: Functor[F]): OptionT[F, A] = + filter(p)(F) + def filterNot(p: A => Boolean)(implicit F: Functor[F]): OptionT[F, A] = OptionT(F.map(value)(_.filterNot(p))) diff --git a/tests/src/test/scala/cats/tests/OptionTTests.scala b/tests/src/test/scala/cats/tests/OptionTTests.scala index 0873a8cff9..95cb4fac7d 100644 --- a/tests/src/test/scala/cats/tests/OptionTTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTTests.scala @@ -50,6 +50,12 @@ class OptionTTests extends CatsSuite { } } + test("OptionT[Id, A].withFilter consistent with Option.withFilter"){ + forAll { (o: Option[Int], f: Int => Boolean) => + (for {x <- o if f(x)} yield x) should === ((for {x <- OptionT[Id, Int](o) if f(x)} yield x).value) + } + } + test("OptionT[Id, A].filterNot consistent with Option.filterNot") { forAll { (o: Option[Int], f: Int => Boolean) => o.filterNot(f) should === (OptionT[Id, Int](o).filterNot(f).value) From c16d9c56daee9a6fedb819d4a9264cc946bf34b2 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 13 May 2016 09:12:28 -0400 Subject: [PATCH 135/185] added `MonadError[Option, Unit]` to `Option` --- core/src/main/scala/cats/std/option.scala | 8 ++++++-- tests/src/test/scala/cats/tests/OptionTests.scala | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/std/option.scala b/core/src/main/scala/cats/std/option.scala index 96c6d76658..7ffef7c4bb 100644 --- a/core/src/main/scala/cats/std/option.scala +++ b/core/src/main/scala/cats/std/option.scala @@ -3,8 +3,8 @@ package std trait OptionInstances extends cats.kernel.std.OptionInstances { - implicit val optionInstance: Traverse[Option] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] = - new Traverse[Option] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] { + implicit val optionInstance: Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] = + new Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] { def empty[A]: Option[A] = None @@ -48,6 +48,10 @@ trait OptionInstances extends cats.kernel.std.OptionInstances { case Some(a) => Applicative[G].map(f(a))(Some(_)) } + def raiseError[A](e: Unit): Option[A] = None + + def handleErrorWith[A](fa: Option[A])(f: (Unit) => Option[A]): Option[A] = fa orElse f(()) + override def exists[A](fa: Option[A])(p: A => Boolean): Boolean = fa.exists(p) diff --git a/tests/src/test/scala/cats/tests/OptionTests.scala b/tests/src/test/scala/cats/tests/OptionTests.scala index 01ab65b3c7..6dea12f645 100644 --- a/tests/src/test/scala/cats/tests/OptionTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTests.scala @@ -2,7 +2,7 @@ package cats package tests import cats.laws.{ApplicativeLaws, CoflatMapLaws, FlatMapLaws, MonadLaws} -import cats.laws.discipline.{TraverseTests, CoflatMapTests, MonadCombineTests, SerializableTests, CartesianTests} +import cats.laws.discipline._ class OptionTests extends CatsSuite { checkAll("Option[Int]", CartesianTests[Option].cartesian[Int, Int, Int]) @@ -17,6 +17,8 @@ class OptionTests extends CatsSuite { checkAll("Option[Int] with Option", TraverseTests[Option].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[Option]", SerializableTests.serializable(Traverse[Option])) + checkAll("MonadError[Option, Unit]", MonadErrorTests[Option, Unit].monadError[Int, Int, Int]) + test("show") { none[Int].show should === ("None") 1.some.show should === ("Some(1)") From 2068080f9ee83cdc25d2e09a4e97c8c08e4327ca Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 13 May 2016 09:12:53 -0400 Subject: [PATCH 136/185] added `ApplicativeError` instance to `Kleisli` --- core/src/main/scala/cats/data/Kleisli.scala | 51 ++++++++++++++----- .../test/scala/cats/tests/KleisliTests.scala | 5 +- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index 1fbdf1e5f7..069b92fe82 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -123,6 +123,9 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 { def liftT[M[_], B](ma: M[B])(implicit ev: Trivial): Kleisli[M, A, B] = Kleisli[M, A, B](a => ma) } + + implicit def kleisliApplicativeError[F[_], A, E](implicit AE: ApplicativeError[F, E]): ApplicativeError[Kleisli[F, A, ?], E] + = new KleisliApplicativeError[F, A, E] { implicit def AF: ApplicativeError[F, E] = AE } } private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 { @@ -145,21 +148,12 @@ private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 implicit def kleisliSemigroupK[F[_]](implicit ev: FlatMap[F]): SemigroupK[Lambda[A => Kleisli[F, A, A]]] = new KleisliSemigroupK[F] { def F: FlatMap[F] = ev } + } private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 { - implicit def kleisliApplicative[F[_]: Applicative, A]: Applicative[Kleisli[F, A, ?]] = new Applicative[Kleisli[F, A, ?]] { - def pure[B](x: B): Kleisli[F, A, B] = - Kleisli.pure[F, A, B](x) - - def ap[B, C](f: Kleisli[F, A, B => C])(fa: Kleisli[F, A, B]): Kleisli[F, A, C] = - fa.ap(f) - - override def map[B, C](fb: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = - fb.map(f) - - override def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] = - Kleisli(a => Applicative[F].product(fb.run(a), fc.run(a))) + implicit def kleisliApplicative[F[_], A](implicit A : Applicative[F]): Applicative[Kleisli[F, A, ?]] = new KleisliApplicative[F, A] { + implicit def F: Applicative[F] = A } } @@ -269,3 +263,36 @@ private trait KleisliMonoidK[F[_]] extends MonoidK[Lambda[A => Kleisli[F, A, A]] override def empty[A]: Kleisli[F, A, A] = Kleisli(F.pure[A]) } + + +private trait KleisliApplicativeError[F[_], A, E] extends KleisliApplicative[F, A] with ApplicativeError[Kleisli[F, A, ?], E] { + type K[T] = Kleisli[F, A, T] + + implicit def AF: ApplicativeError[F, E] + + implicit def F: Applicative[F] = AF + + def raiseError[B](e: E): K[B] = Kleisli(_ => AF.raiseError(e)) + + def handleErrorWith[B](kb: K[B])(f: E => K[B]): K[B] = Kleisli { a: A => + AF.handleErrorWith(kb.run(a))((e: E) => f(e).run(a)) + } + +} + + +private trait KleisliApplicative[F[_], A] extends Applicative[Kleisli[F, A, ?]] { + implicit def F: Applicative[F] + + def pure[B](x: B): Kleisli[F, A, B] = + Kleisli.pure[F, A, B](x) + + def ap[B, C](f: Kleisli[F, A, B => C])(fa: Kleisli[F, A, B]): Kleisli[F, A, C] = + fa.ap(f) + + override def map[B, C](fb: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = + fb.map(f) + + override def product[B, C](fb: Kleisli[F, A, B], fc: Kleisli[F, A, C]): Kleisli[F, A, (B, C)] = + Kleisli(a => Applicative[F].product(fb.run(a), fc.run(a))) +} diff --git a/tests/src/test/scala/cats/tests/KleisliTests.scala b/tests/src/test/scala/cats/tests/KleisliTests.scala index 27350621db..d1f484a045 100644 --- a/tests/src/test/scala/cats/tests/KleisliTests.scala +++ b/tests/src/test/scala/cats/tests/KleisliTests.scala @@ -2,7 +2,7 @@ package cats package tests import cats.arrow.{Arrow, Choice, Split} -import cats.data.{Kleisli, Reader} +import cats.data.{XorT, Kleisli, Reader} import cats.functor.{Contravariant, Strong} import cats.laws.discipline._ import cats.laws.discipline.arbitrary._ @@ -15,8 +15,11 @@ class KleisliTests extends CatsSuite { implicit def kleisliEq[F[_], A, B](implicit A: Arbitrary[A], FB: Eq[F[B]]): Eq[Kleisli[F, A, B]] = Eq.by[Kleisli[F, A, B], A => F[B]](_.run) + implicit val xorTEq = XorT.xorTEq[Kleisli[Option, Int, ?], Unit, Int] + implicit val iso = CartesianTests.Isomorphisms.invariant[Kleisli[Option, Int, ?]] + checkAll("ApplicativeError[Klesili[Option, Int, Int], Unit]", ApplicativeErrorTests[Kleisli[Option, Int, ?], Unit].applicativeError[Int, Int, Int]) checkAll("Kleisli[Option, Int, Int]", CartesianTests[Kleisli[Option, Int, ?]].cartesian[Int, Int, Int]) checkAll("Cartesian[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Cartesian[Kleisli[Option, Int, ?]])) From c52c6ecaa057fc5cba93619b92bab67fc6abee64 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 13 May 2016 09:28:42 -0400 Subject: [PATCH 137/185] added serializableTests for MonadError[Option, Unit] and ApplicativeError[Kleisli] --- tests/src/test/scala/cats/tests/KleisliTests.scala | 4 +++- tests/src/test/scala/cats/tests/OptionTests.scala | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/src/test/scala/cats/tests/KleisliTests.scala b/tests/src/test/scala/cats/tests/KleisliTests.scala index d1f484a045..c8f83db8fc 100644 --- a/tests/src/test/scala/cats/tests/KleisliTests.scala +++ b/tests/src/test/scala/cats/tests/KleisliTests.scala @@ -19,7 +19,9 @@ class KleisliTests extends CatsSuite { implicit val iso = CartesianTests.Isomorphisms.invariant[Kleisli[Option, Int, ?]] - checkAll("ApplicativeError[Klesili[Option, Int, Int], Unit]", ApplicativeErrorTests[Kleisli[Option, Int, ?], Unit].applicativeError[Int, Int, Int]) + checkAll("Klesili[Option, Int, Int] with Unit", ApplicativeErrorTests[Kleisli[Option, Int, ?], Unit].applicativeError[Int, Int, Int]) + checkAll("ApplicativeError[Klesili[Option, Int, Int], Unit]", SerializableTests.serializable(ApplicativeError[Kleisli[Option, Int, ?], Unit])) + checkAll("Kleisli[Option, Int, Int]", CartesianTests[Kleisli[Option, Int, ?]].cartesian[Int, Int, Int]) checkAll("Cartesian[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Cartesian[Kleisli[Option, Int, ?]])) diff --git a/tests/src/test/scala/cats/tests/OptionTests.scala b/tests/src/test/scala/cats/tests/OptionTests.scala index 6dea12f645..8c6ef464b9 100644 --- a/tests/src/test/scala/cats/tests/OptionTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTests.scala @@ -17,7 +17,8 @@ class OptionTests extends CatsSuite { checkAll("Option[Int] with Option", TraverseTests[Option].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[Option]", SerializableTests.serializable(Traverse[Option])) - checkAll("MonadError[Option, Unit]", MonadErrorTests[Option, Unit].monadError[Int, Int, Int]) + checkAll("Option with Unit", MonadErrorTests[Option, Unit].monadError[Int, Int, Int]) + checkAll("MonadError[Option, Unit]", SerializableTests.serializable(MonadError[Option, Unit])) test("show") { none[Int].show should === ("None") From faf05f9279cf91a2ba70386579f690ae875526d6 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Sat, 14 May 2016 10:54:00 -0400 Subject: [PATCH 138/185] Don't combine lefts on Xor and XorT combine Resolves #888 I don't think there's really a _right_ answer here. Both methods of combining `Xor`s are straightforward and law-abiding. However, I think that since in pretty much every other context (including the `SemigroupK` instances), `Xor` does not combine failures and `Validated` does, it is less surprising behavior to not combine left values in `combine` methods for `Xor` and `XorT`. Some notes about related implementations: - Scalaz's respective methods (and semigroup instances) _do_ combine errors, so this is a deviation from that. - The `Semigroup` for `Either` in Haskell has doesn't combine values at all, but returns the first `Right` (if any), making it equivalent to the behavior of `orElse` and the `SemigroupK` instance for `Xor`. Since we have already decided to not go with `orElse`-like behavior for our `Option` semigroup, I'm inclined to not take this approach for `Xor`. See also https://github.com/typelevel/cats/pull/996#issuecomment-214010455 --- core/src/main/scala/cats/data/Xor.scala | 46 +++++++++++++++++++----- core/src/main/scala/cats/data/XorT.scala | 40 ++++++++++++++++++++- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index 1bf5e115b6..f7f2a1d467 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -133,13 +133,43 @@ sealed abstract class Xor[+A, +B] extends Product with Serializable { def merge[AA >: A](implicit ev: B <:< AA): AA = fold(identity, ev.apply) - final def combine[AA >: A, BB >: B](that: AA Xor BB)(implicit AA: Semigroup[AA], BB: Semigroup[BB]): AA Xor BB = this match { - case Xor.Left(a1) => that match { - case Xor.Left(a2) => Xor.Left(AA.combine(a1, a2)) - case Xor.Right(b2) => Xor.Left(a1) - } + /** + * Combine with another `Xor` value. + * + * If this `Xor` is a `Left` then it will be returned as-is. + * If this `Xor` is a `Right` and `that` `Xor` is a left, then `that` will be + * returned. + * If both `Xor`s are `Right`s, then the `Semigroup[BB]` instance will be used + * to combine both values and return them as a `Right`. + * Note: If both `Xor`s are `Left`s then their values are not combined. Use + * `Validated` if you prefer to combine `Left` values. + * + * Examples: + * {{{ + * scala> import cats.data.Xor + * scala> import cats.implicits._ + * scala> val l1: Xor[String, Int] = Xor.left("error 1") + * scala> val l2: Xor[String, Int] = Xor.left("error 2") + * scala> val r3: Xor[String, Int] = Xor.right(3) + * scala> val r4: Xor[String, Int] = Xor.right(4) + * + * scala> l1 combine l2 + * res0: Xor[String, Int] = Left(error 1) + * + * scala> l1 combine r3 + * res1: Xor[String, Int] = Left(error 1) + * + * scala> r3 combine l1 + * res2: Xor[String, Int] = Left(error 1) + * + * scala> r3 combine r4 + * res3: Xor[String, Int] = Right(7) + * }}} + */ + final def combine[AA >: A, BB >: B](that: AA Xor BB)(implicit BB: Semigroup[BB]): AA Xor BB = this match { + case l @ Xor.Left(_) => l case Xor.Right(b1) => that match { - case Xor.Left(a2) => Xor.Left(a2) + case l @ Xor.Left(_) => l case Xor.Right(b2) => Xor.Right(BB.combine(b1, b2)) } } @@ -168,7 +198,7 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { def show(f: A Xor B): String = f.show } - implicit def xorMonoid[A, B](implicit A: Semigroup[A], B: Monoid[B]): Monoid[A Xor B] = + implicit def xorMonoid[A, B](implicit B: Monoid[B]): Monoid[A Xor B] = new Monoid[A Xor B] { def empty: A Xor B = Xor.Right(B.empty) def combine(x: A Xor B, y: A Xor B): A Xor B = x combine y @@ -229,7 +259,7 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { private[data] sealed abstract class XorInstances1 extends XorInstances2 { - implicit def xorSemigroup[A, B](implicit A: Semigroup[A], B: Semigroup[B]): Semigroup[A Xor B] = + implicit def xorSemigroup[A, B](implicit B: Semigroup[B]): Semigroup[A Xor B] = new Semigroup[A Xor B] { def combine(x: A Xor B, y: A Xor B): A Xor B = x combine y } diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index a3bfb686ec..2fdf95282e 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -110,7 +110,45 @@ final case class XorT[F[_], A, B](value: F[A Xor B]) { def merge[AA >: A](implicit ev: B <:< AA, F: Functor[F]): F[AA] = F.map(value)(_.fold(identity, ev.apply)) - def combine(that: XorT[F, A, B])(implicit F: Apply[F], A: Semigroup[A], B: Semigroup[B]): XorT[F, A, B] = + /** + * Similar to [[Xor.combine]] but mapped over an `F` context. + * + * Examples: + * {{{ + * scala> import cats.data.XorT + * scala> import cats.implicits._ + * scala> val l1: XorT[Option, String, Int] = XorT.left(Some("error 1")) + * scala> val l2: XorT[Option, String, Int] = XorT.left(Some("error 2")) + * scala> val r3: XorT[Option, String, Int] = XorT.right(Some(3)) + * scala> val r4: XorT[Option, String, Int] = XorT.right(Some(4)) + * scala> val noneXorT: XorT[Option, String, Int] = XorT.left(None) + * + * scala> l1 combine l2 + * res0: XorT[Option, String, Int] = XorT(Some(Left(error 1))) + * + * scala> l1 combine r3 + * res1: XorT[Option, String, Int] = XorT(Some(Left(error 1))) + * + * scala> r3 combine l1 + * res2: XorT[Option, String, Int] = XorT(Some(Left(error 1))) + * + * scala> r3 combine r4 + * res3: XorT[Option, String, Int] = XorT(Some(Right(7))) + * + * scala> l1 combine noneXorT + * res4: XorT[Option, String, Int] = XorT(None) + * + * scala> noneXorT combine l1 + * res5: XorT[Option, String, Int] = XorT(None) + * + * scala> r3 combine noneXorT + * res6: XorT[Option, String, Int] = XorT(None) + * + * scala> noneXorT combine r4 + * res7: XorT[Option, String, Int] = XorT(None) + * }}} + */ + def combine(that: XorT[F, A, B])(implicit F: Apply[F], B: Semigroup[B]): XorT[F, A, B] = XorT(F.map2(this.value, that.value)(_ combine _)) def toValidated(implicit F: Functor[F]): F[Validated[A, B]] = From a8a7587f558541cbabc5c40053181928b4baf78c Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Sat, 14 May 2016 12:46:14 -0400 Subject: [PATCH 139/185] Run kernel-law tests for JS as part of build This seems to have been missed in the build script --- scripts/travis-publish.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/travis-publish.sh b/scripts/travis-publish.sh index ca96a2c817..bab7df20f6 100755 --- a/scripts/travis-publish.sh +++ b/scripts/travis-publish.sh @@ -28,7 +28,7 @@ fi sbt_cmd="sbt ++$TRAVIS_SCALA_VERSION" coverage="$sbt_cmd coverage validateJVM coverageReport && codecov" -scala_js="$sbt_cmd macrosJS/compile coreJS/compile lawsJS/compile && $sbt_cmd testsJS/test && $sbt_cmd js/test" +scala_js="$sbt_cmd macrosJS/compile coreJS/compile lawsJS/compile && $sbt_cmd kernelLawsJS/test && $sbt_cmd testsJS/test && $sbt_cmd js/test" scala_jvm="$sbt_cmd validateJVM" run_cmd="$coverage && $scala_js && $scala_jvm $publish_cmd" From 0aa659be80eb49450a0ad6ae3b694179bec867ad Mon Sep 17 00:00:00 2001 From: peterneyens Date: Sat, 14 May 2016 15:27:44 +0200 Subject: [PATCH 140/185] Add Coproduct fold (fixes #987) --- core/src/main/scala/cats/data/Coproduct.scala | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/data/Coproduct.scala b/core/src/main/scala/cats/data/Coproduct.scala index e2b4a55ca8..8e963b07a5 100644 --- a/core/src/main/scala/cats/data/Coproduct.scala +++ b/core/src/main/scala/cats/data/Coproduct.scala @@ -1,6 +1,7 @@ package cats package data +import cats.arrow.NaturalTransformation import cats.functor.Contravariant /** `F` on the left and `G` on the right of [[Xor]]. @@ -57,6 +58,28 @@ final case class Coproduct[F[_], G[_], A](run: F[A] Xor G[A]) { def toValidated: Validated[F[A], G[A]] = run.toValidated + /** + * Fold this coproduct into a new type constructor using two natural transformations. + * + * Example: + * {{{ + * scala> import cats.arrow.NaturalTransformation + * scala> import cats.data.Coproduct + * scala> val listToOption = + * | new NaturalTransformation[List, Option] { + * | def apply[A](fa: List[A]): Option[A] = fa.headOption + * | } + * scala> val optionToOption = NaturalTransformation.id[Option] + * scala> val cp1: Coproduct[List, Option, Int] = Coproduct.leftc(List(1,2,3)) + * scala> val cp2: Coproduct[List, Option, Int] = Coproduct.rightc(Some(4)) + * scala> cp1.fold(listToOption, optionToOption) + * res0: Option[Int] = Some(1) + * scala> cp2.fold(listToOption, optionToOption) + * res1: Option[Int] = Some(4) + * }}} + */ + def fold[H[_]](f: NaturalTransformation[F, H], g: NaturalTransformation[G, H]): H[A] = + run.fold(f.apply, g.apply) } object Coproduct extends CoproductInstances { @@ -78,7 +101,6 @@ object Coproduct extends CoproductInstances { def left[G[_]]: CoproductLeft[G] = new CoproductLeft[G] def right[F[_]]: CoproductRight[F] = new CoproductRight[F] - } private[data] sealed abstract class CoproductInstances3 { From 79b74a573364488dfdbe3de565365ddf3e3989e5 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sun, 15 May 2016 18:43:14 +0200 Subject: [PATCH 141/185] Add FPiS to the "Resources for Learners" section --- docs/src/site/resources_for_learners.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/src/site/resources_for_learners.md b/docs/src/site/resources_for_learners.md index 12b808ce7e..133467dd3b 100644 --- a/docs/src/site/resources_for_learners.md +++ b/docs/src/site/resources_for_learners.md @@ -3,5 +3,12 @@ layout: default title: "Resources for Learners" section: "resources_for_learners" --- -# Links - * [herding cats](http://eed3si9n.com/herding-cats/) +# Books + * [Functional Programming in Scala](https://www.manning.com/books/functional-programming-in-scala) + by Paul Chiusano and Rúnar Bjarnason - While this book does not + specifically cover Cats, it is a great introduction to functional + programming in Scala in general and covers some of the abstractions + that can be found in Cats. + +# Tutorials + * [herding cats](http://eed3si9n.com/herding-cats/) by Eugene Yokota From 2f8e0feaaca37a4cc37e9c09d3a4c426843a6196 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Tue, 17 May 2016 05:55:27 -0400 Subject: [PATCH 142/185] Use more descriptive binding name in Xor.combine Per @non's suggestion. --- core/src/main/scala/cats/data/Xor.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index f7f2a1d467..427fd011c1 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -167,9 +167,9 @@ sealed abstract class Xor[+A, +B] extends Product with Serializable { * }}} */ final def combine[AA >: A, BB >: B](that: AA Xor BB)(implicit BB: Semigroup[BB]): AA Xor BB = this match { - case l @ Xor.Left(_) => l + case left @ Xor.Left(_) => left case Xor.Right(b1) => that match { - case l @ Xor.Left(_) => l + case left @ Xor.Left(_) => left case Xor.Right(b2) => Xor.Right(BB.combine(b1, b2)) } } From e53782a486d38d0aa100c561f54f9646214d1fe3 Mon Sep 17 00:00:00 2001 From: Adam Fisher Date: Tue, 17 May 2016 10:50:07 +0100 Subject: [PATCH 143/185] Replace 0.5.0 pre-release with more general notice Cats `0.5.0` is up in Maven and there's already a `0.6.0-M1` release up. With that in mind this message is a little unclear, and may be suggesting to users that they should use previous releases (e.g. `0.4.x`). Replacing the `0.5.0` warning with a more general message about the state of Cats' development is clearer. --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c4c943efcc..bffa329078 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,12 @@ functionality, you can pick-and-choose from amongst these modules Release notes for Cats are available in [CHANGES.md](CHANGES.md). -*Cats 0.5.0 is a pre-release: there are not currently source- or -binary-compatibility guarantees.* +*Cats is still under active development. While we don't anticipate any + major redesigns, changes that are neither source nor binary + compatibility are to be expected in upcoming cats releases. We will + update the minor version of cats accordingly for such changes. Once + cats 1.0 is released (ETA: Q3 2016), there will be an increased focus + on making changes in compatible ways.* ### Documentation Among the goals of Cats is to provide approachable and useful documentation. From 78d5262e484f5a140e8ebeef083fce88d006e259 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Tue, 17 May 2016 07:05:01 -0400 Subject: [PATCH 144/185] Add notes about kind-projector usage in docs Resolves #1033 although in all but one place, a simple type alias was not possible. --- docs/src/main/tut/const.md | 2 ++ docs/src/main/tut/freeapplicative.md | 7 +++++-- docs/src/main/tut/freemonad.md | 2 +- docs/src/main/tut/kleisli.md | 4 +++- docs/src/main/tut/monad.md | 2 ++ docs/src/main/tut/validated.md | 2 ++ docs/src/main/tut/xor.md | 2 ++ 7 files changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/src/main/tut/const.md b/docs/src/main/tut/const.md index 5a958c3f45..225f4a1a01 100644 --- a/docs/src/main/tut/const.md +++ b/docs/src/main/tut/const.md @@ -134,6 +134,8 @@ is to take an `A` and return it right back (lifted into `Const`). Before we plug and play however, note that `modifyF` has a `Functor` constraint on `F[_]`. This means we need to define a `Functor` instance for `Const`, where the first type parameter is fixed. +*Note*: the example below assumes usage of the [kind-projector compiler plugin](https://github.com/non/kind-projector) and will not compile if it is not being used in a project. + ```tut:silent import cats.data.Const diff --git a/docs/src/main/tut/freeapplicative.md b/docs/src/main/tut/freeapplicative.md index 65f20329db..80a5605a36 100644 --- a/docs/src/main/tut/freeapplicative.md +++ b/docs/src/main/tut/freeapplicative.md @@ -57,8 +57,11 @@ import cats.Id import cats.arrow.NaturalTransformation import cats.std.function._ +// a function that takes a string as input +type FromString[A] = String => A + val compiler = - new NaturalTransformation[ValidationOp, String => ?] { + new NaturalTransformation[ValidationOp, FromString] { def apply[A](fa: ValidationOp[A]): String => A = str => fa match { @@ -69,7 +72,7 @@ val compiler = ``` ```tut -val validator = prog.foldMap[String => ?](compiler) +val validator = prog.foldMap[FromString](compiler) validator("1234") validator("12345") ``` diff --git a/docs/src/main/tut/freemonad.md b/docs/src/main/tut/freemonad.md index 00e6997356..121de3b030 100644 --- a/docs/src/main/tut/freemonad.md +++ b/docs/src/main/tut/freemonad.md @@ -215,7 +215,7 @@ behavior, such as: - `Future[_]` for asynchronous computation - `List[_]` for gathering multiple results - `Option[_]` to support optional results - - `Validated[_]` (or `Xor[E, ?]`) to support failure + - `Xor[E, ?]` to support failure - a pseudo-random monad to support non-determinism - and so on... diff --git a/docs/src/main/tut/kleisli.md b/docs/src/main/tut/kleisli.md index 8e9383cd65..3b03916e0f 100644 --- a/docs/src/main/tut/kleisli.md +++ b/docs/src/main/tut/kleisli.md @@ -120,7 +120,9 @@ instance, `Kleisli[F, A, B]` has a `Functor` instance so long as the chosen `F[_ instance so long as the chosen `F[_]` does. The instances in Cats are laid out in a way such that implicit resolution will pick up the most specific instance it can (depending on the `F[_]`). -An example of a `Monad` instance for `Kleisli` would be: +An example of a `Monad` instance for `Kleisli` is shown below. + +*Note*: the example below assumes usage of the [kind-projector compiler plugin](https://github.com/non/kind-projector) and will not compile if it is not being used in a project. ```tut:silent import cats.syntax.flatMap._ diff --git a/docs/src/main/tut/monad.md b/docs/src/main/tut/monad.md index d9e5a5c950..372bd59975 100644 --- a/docs/src/main/tut/monad.md +++ b/docs/src/main/tut/monad.md @@ -94,6 +94,8 @@ instructions on how to compose any outer monad (`F` in the following example) with a specific inner monad (`Option` in the following example). +*Note*: the example below assumes usage of the [kind-projector compiler plugin](https://github.com/non/kind-projector) and will not compile if it is not being used in a project. + ```tut:silent case class OptionT[F[_], A](value: F[Option[A]]) diff --git a/docs/src/main/tut/validated.md b/docs/src/main/tut/validated.md index 724aeaaa2e..ea26d16b8e 100644 --- a/docs/src/main/tut/validated.md +++ b/docs/src/main/tut/validated.md @@ -185,6 +185,8 @@ Which can be defined in terms of `Apply#ap` and `Apply#map`, the very functions Can we perhaps define an `Apply` instance for `Validated`? Better yet, can we define an `Applicative` instance? +*Note*: the example below assumes usage of the [kind-projector compiler plugin](https://github.com/non/kind-projector) and will not compile if it is not being used in a project. + ```tut:silent import cats.Applicative diff --git a/docs/src/main/tut/xor.md b/docs/src/main/tut/xor.md index 264571cb89..3873a8f69f 100644 --- a/docs/src/main/tut/xor.md +++ b/docs/src/main/tut/xor.md @@ -100,6 +100,8 @@ over `M[_] : Monad`). Since we only ever want the computation to continue in the case of `Xor.Right` (as captured by the right-bias nature), we fix the left type parameter and leave the right one free. +*Note*: the example below assumes usage of the [kind-projector compiler plugin](https://github.com/non/kind-projector) and will not compile if it is not being used in a project. + ```tut:silent import cats.Monad From 70e19499a313037eed4beb32748d3faf1c950102 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Tue, 17 May 2016 08:12:52 -0400 Subject: [PATCH 145/185] Add updating site and release notes to release process At the Typelevel Summit in Philly, people brought up confusion around the site not always being up to date with the latest release. Also, we always seem to forget to update the release notes until @travisbrown kindly volunteers to :) --- PROCESS.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/PROCESS.md b/PROCESS.md index ee3b6f7a15..15db8b7e69 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -84,6 +84,13 @@ You can get a list of changes between release tags `v0.1.2` and messages is a good way to get a summary of what happened, although it does not account for conversations that occured on Github. +Once the relevant documentation changes have been committed, new +[release notes](https://github.com/typelevel/cats/releases) should be +added. You can add a release by clicking the "Draft a new release" button +on that page. + +The website should then be updated via `sbt docs/ghpagesPushSite`. + ### Conclusion Ideally this document will evolve and grow as the project From a1003a53cf3b945636f3ace78b58fe71d98df01a Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Tue, 17 May 2016 08:25:51 -0400 Subject: [PATCH 146/185] Setting version to 0.6.0-M2 --- version.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.sbt b/version.sbt index 01fa65ac61..709e9d35ec 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.6.0-SNAPSHOT" \ No newline at end of file +version in ThisBuild := "0.6.0-M2" \ No newline at end of file From 271c1977845a38dddb653f067aa34d82859e45c6 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Tue, 17 May 2016 08:32:06 -0400 Subject: [PATCH 147/185] Setting version to 0.6.0 --- version.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.sbt b/version.sbt index 709e9d35ec..113a45a1b2 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.6.0-M2" \ No newline at end of file +version in ThisBuild := "0.6.0" \ No newline at end of file From 40e7cf7a99dfb2c4909cf20627ff3dc41423cdd3 Mon Sep 17 00:00:00 2001 From: peterneyens Date: Tue, 17 May 2016 14:51:19 +0200 Subject: [PATCH 148/185] Add a link to the OptionT documentation from the part about monad composition in the Monad documentation. --- docs/src/main/tut/monad.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/main/tut/monad.md b/docs/src/main/tut/monad.md index d9e5a5c950..b8607826f6 100644 --- a/docs/src/main/tut/monad.md +++ b/docs/src/main/tut/monad.md @@ -113,3 +113,4 @@ implicit def optionTMonad[F[_]](implicit F : Monad[F]) = { This sort of construction is called a monad transformer. +Cats has an [`OptionT`](optiont.html) monad transformer, which adds a lot of useful functions to the simple implementation above. \ No newline at end of file From 1752755e48d00476b9dc899bd33e2361026e1ae0 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 17 May 2016 15:37:56 +0200 Subject: [PATCH 149/185] Use MonadError[Option, Unit] in tests --- .../test/scala/cats/tests/MonadErrorSuite.scala | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/src/test/scala/cats/tests/MonadErrorSuite.scala b/tests/src/test/scala/cats/tests/MonadErrorSuite.scala index 3b752339e1..4e7ddd7919 100644 --- a/tests/src/test/scala/cats/tests/MonadErrorSuite.scala +++ b/tests/src/test/scala/cats/tests/MonadErrorSuite.scala @@ -1,26 +1,22 @@ package cats package tests -import cats.data.Xor - class MonadErrorSuite extends CatsSuite { - type ErrorOr[A] = String Xor A - - val successful: ErrorOr[Int] = 42.right - val failed: ErrorOr[Int] = "Oops".left + val successful: Option[Int] = 42.some + val failed: Option[Int] = None test("ensure raises an error if the predicate fails") { - monadErrorSyntax(successful).ensure("Error")(i => false) should === ("Error".left) + successful.ensure(())(i => false) should === (None) } test("ensure returns the successful value if the predicate succeeds") { - monadErrorSyntax(successful).ensure("Error")(i => true) should === (successful) + successful.ensure(())(i => true) should === (successful) } test("ensure returns the failure, when applied to a failure") { - monadErrorSyntax(failed).ensure("Error")(i => false) should === (failed) - monadErrorSyntax(failed).ensure("Error")(i => true) should === (failed) + failed.ensure(())(i => false) should === (failed) + failed.ensure(())(i => true) should === (failed) } } From 642d1fe80067af30ab3e36ead787ec7aa3b999a6 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Tue, 17 May 2016 10:29:05 -0400 Subject: [PATCH 150/185] added support for summon `ApplicativeErrorSyntax` for `F[_]` --- core/src/main/scala/cats/syntax/applicativeError.scala | 10 ++++++++-- .../test/scala/cats/tests/ApplicativeErrorTests.scala | 7 ++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/syntax/applicativeError.scala b/core/src/main/scala/cats/syntax/applicativeError.scala index 22e0927c5b..0107b4eb7f 100644 --- a/core/src/main/scala/cats/syntax/applicativeError.scala +++ b/core/src/main/scala/cats/syntax/applicativeError.scala @@ -3,12 +3,18 @@ package syntax import cats.data.{Xor, XorT} -trait ApplicativeErrorSyntax { +trait ApplicativeErrorSyntax1 { + implicit def applicativeErrorSyntax[F[_], E, A](fa: F[A])(implicit F: ApplicativeError[F, E]): ApplicativeErrorOps[F, E, A] = + new ApplicativeErrorOps[F, E, A](fa) +} + +trait ApplicativeErrorSyntax extends ApplicativeErrorSyntax1 { implicit def applicativeErrorIdSyntax[E](e: E): ApplicativeErrorIdOps[E] = new ApplicativeErrorIdOps(e) - implicit def applicativeErrorSyntax[F[_, _], E, A](fa: F[E, A])(implicit F: ApplicativeError[F[E, ?], E]): ApplicativeErrorOps[F[E, ?], E, A] = + implicit def applicativeErrorSyntax2[F[_, _], E, A](fa: F[E, A])(implicit F: ApplicativeError[F[E, ?], E]): ApplicativeErrorOps[F[E, ?], E, A] = new ApplicativeErrorOps[F[E, ?], E, A](fa) + } final class ApplicativeErrorIdOps[E](e: E) { diff --git a/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala b/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala index e23e35a20d..3677171a3b 100644 --- a/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala +++ b/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala @@ -38,4 +38,9 @@ class ApplicativeErrorCheck extends CatsSuite { failed.recoverWith { case error => error.length.right } should === (7.right) } -} \ No newline at end of file + test("syntax summon with F[_] ") { + val failedOption: Option[String] = None + failedOption.recoverWith { case error => Some("error") } should === (Some("error")) + } + +} From dfd04f09cfa114913bc8d322b07829d9ceea9c7d Mon Sep 17 00:00:00 2001 From: Markus Hauck Date: Mon, 25 Apr 2016 20:06:32 +0200 Subject: [PATCH 151/185] Replace occurences of `~>` with `NaturalTransformation` --- .../src/main/scala/cats/arrow/NaturalTransformation.scala | 4 ++-- core/src/main/scala/cats/data/Kleisli.scala | 4 ++-- docs/src/main/tut/freemonad.md | 7 +++++-- free/src/main/scala/cats/free/Coyoneda.scala | 4 +++- free/src/main/scala/cats/free/Free.scala | 7 +++---- free/src/main/scala/cats/free/FreeApplicative.scala | 8 ++++---- free/src/test/scala/cats/free/FreeApplicativeTests.scala | 4 ++-- free/src/test/scala/cats/free/FreeTests.scala | 4 ++-- free/src/test/scala/cats/free/InjectTests.scala | 7 ++++--- tests/src/test/scala/cats/tests/KleisliTests.scala | 4 ++-- .../scala/cats/tests/NaturalTransformationTests.scala | 4 ++-- 11 files changed, 31 insertions(+), 26 deletions(-) diff --git a/core/src/main/scala/cats/arrow/NaturalTransformation.scala b/core/src/main/scala/cats/arrow/NaturalTransformation.scala index 87a96e92fc..0b792bc9cf 100644 --- a/core/src/main/scala/cats/arrow/NaturalTransformation.scala +++ b/core/src/main/scala/cats/arrow/NaturalTransformation.scala @@ -14,8 +14,8 @@ trait NaturalTransformation[F[_], G[_]] extends Serializable { self => def andThen[H[_]](f: NaturalTransformation[G, H]): NaturalTransformation[F, H] = f.compose(self) - def or[H[_]](h: H ~> G): Coproduct[F, H, ?] ~> G = - new (Coproduct[F, H, ?] ~> G) { + def or[H[_]](h: NaturalTransformation[H,G]): NaturalTransformation[Coproduct[F, H, ?],G] = + new (NaturalTransformation[Coproduct[F, H, ?],G]) { def apply[A](fa: Coproduct[F, H, A]): G[A] = fa.run match { case Xor.Left(ff) => self(ff) case Xor.Right(gg) => h(gg) diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index 069b92fe82..83c3ca502e 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -1,7 +1,7 @@ package cats package data -import cats.arrow.{Arrow, Choice, Split} +import cats.arrow.{Arrow, Choice, Split, NaturalTransformation} import cats.functor.{Contravariant, Strong} /** @@ -48,7 +48,7 @@ final case class Kleisli[F[_], A, B](run: A => F[B]) { self => def local[AA](f: AA => A): Kleisli[F, AA, B] = Kleisli(f.andThen(run)) - def transform[G[_]](f: F ~> G): Kleisli[G, A, B] = + def transform[G[_]](f: NaturalTransformation[F,G]): Kleisli[G, A, B] = Kleisli(a => f(run(a))) def lower(implicit F: Applicative[F]): Kleisli[F, A, F[B]] = diff --git a/docs/src/main/tut/freemonad.md b/docs/src/main/tut/freemonad.md index 00e6997356..c366ed69bc 100644 --- a/docs/src/main/tut/freemonad.md +++ b/docs/src/main/tut/freemonad.md @@ -165,12 +165,15 @@ DSL. By itself, this DSL only represents a sequence of operations To do this, we will use a *natural transformation* between type containers. Natural transformations go between types like `F[_]` and -`G[_]` (this particular transformation would be written as `F ~> G`). +`G[_]` (this particular transformation would be written as +`NaturalTransformation[F,G]` or as done here using the symbolic +alternative as `F ~> G`). In our case, we will use a simple mutable map to represent our key value store: ```tut:silent +import cats.arrow.NaturalTransformation import cats.{Id, ~>} import scala.collection.mutable @@ -241,7 +244,7 @@ recursive structure by: This operation is called `Free.foldMap`: ```scala -final def foldMap[M[_]](f: S ~> M)(M: Monad[M]): M[A] = ... +final def foldMap[M[_]](f: NaturalTransformation[S,M])(M: Monad[M]): M[A] = ... ``` `M` must be a `Monad` to be flattenable (the famous monoid aspect diff --git a/free/src/main/scala/cats/free/Coyoneda.scala b/free/src/main/scala/cats/free/Coyoneda.scala index 20f9c9852d..9afa427ca3 100644 --- a/free/src/main/scala/cats/free/Coyoneda.scala +++ b/free/src/main/scala/cats/free/Coyoneda.scala @@ -1,6 +1,8 @@ package cats package free +import cats.arrow.NaturalTransformation + /** * The dual view of the Yoneda lemma. Also a free functor on `F`. * This is isomorphic to `F` as long as `F` itself is a functor. @@ -36,7 +38,7 @@ sealed abstract class Coyoneda[F[_], A] extends Serializable { self => final def map[B](f: A => B): Aux[F, B, Pivot] = apply(fi)(f compose k) - final def transform[G[_]](f: F ~> G): Aux[G, A, Pivot] = + final def transform[G[_]](f: NaturalTransformation[F,G]): Aux[G, A, Pivot] = apply(f(fi))(k) } diff --git a/free/src/main/scala/cats/free/Free.scala b/free/src/main/scala/cats/free/Free.scala index 585463ade0..d6a14d3a70 100644 --- a/free/src/main/scala/cats/free/Free.scala +++ b/free/src/main/scala/cats/free/Free.scala @@ -130,7 +130,7 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { * Run to completion, mapping the suspension with the given transformation at each step and * accumulating into the monad `M`. */ - final def foldMap[M[_]](f: S ~> M)(implicit M: Monad[M]): M[A] = + final def foldMap[M[_]](f: NaturalTransformation[S,M])(implicit M: Monad[M]): M[A] = step match { case Pure(a) => M.pure(a) case Suspend(s) => f(s) @@ -142,14 +142,13 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { * using the given natural transformation. * Be careful if your natural transformation is effectful, effects are applied by mapSuspension. */ - final def mapSuspension[T[_]](f: S ~> T): Free[T, A] = + final def mapSuspension[T[_]](f: NaturalTransformation[S,T]): Free[T, A] = foldMap[Free[T, ?]] { new NaturalTransformation[S, Free[T, ?]] { def apply[B](fa: S[B]): Free[T, B] = Suspend(f(fa)) } }(Free.freeMonad) - final def compile[T[_]](f: S ~> T): Free[T, A] = mapSuspension(f) + final def compile[T[_]](f: NaturalTransformation[S,T]): Free[T, A] = mapSuspension(f) } - diff --git a/free/src/main/scala/cats/free/FreeApplicative.scala b/free/src/main/scala/cats/free/FreeApplicative.scala index ad9c57aac4..e5a9268ac8 100644 --- a/free/src/main/scala/cats/free/FreeApplicative.scala +++ b/free/src/main/scala/cats/free/FreeApplicative.scala @@ -27,7 +27,7 @@ sealed abstract class FreeApplicative[F[_], A] extends Product with Serializable /** Interprets/Runs the sequence of operations using the semantics of Applicative G * Tail recursive only if G provides tail recursive interpretation (ie G is FreeMonad) */ - final def foldMap[G[_]](f: F ~> G)(implicit G: Applicative[G]): G[A] = + final def foldMap[G[_]](f: NaturalTransformation[F,G])(implicit G: Applicative[G]): G[A] = this match { case Pure(a) => G.pure(a) case Ap(pivot, fn) => G.map2(f(pivot), fn.foldMap(f))((a, g) => g(a)) @@ -40,7 +40,7 @@ sealed abstract class FreeApplicative[F[_], A] extends Product with Serializable foldMap(NaturalTransformation.id[F]) /** Interpret this algebra into another FreeApplicative */ - final def compile[G[_]](f: F ~> G): FA[G, A] = + final def compile[G[_]](f: NaturalTransformation[F,G]): FA[G, A] = foldMap[FA[G, ?]] { new NaturalTransformation[F, FA[G, ?]] { def apply[B](fa: F[B]): FA[G, B] = lift(f(fa)) @@ -48,8 +48,8 @@ sealed abstract class FreeApplicative[F[_], A] extends Product with Serializable } /** Interpret this algebra into a Monoid */ - final def analyze[M:Monoid](f: F ~> λ[α => M]): M = - foldMap[Const[M, ?]](new (F ~> Const[M, ?]) { + final def analyze[M:Monoid](f: NaturalTransformation[F,λ[α => M]]): M = + foldMap[Const[M, ?]](new (NaturalTransformation[F,Const[M, ?]]) { def apply[X](x: F[X]): Const[M,X] = Const(f(x)) }).getConst diff --git a/free/src/test/scala/cats/free/FreeApplicativeTests.scala b/free/src/test/scala/cats/free/FreeApplicativeTests.scala index 1ecfa11a3b..de60d901ec 100644 --- a/free/src/test/scala/cats/free/FreeApplicativeTests.scala +++ b/free/src/test/scala/cats/free/FreeApplicativeTests.scala @@ -97,7 +97,7 @@ class FreeApplicativeTests extends CatsSuite { type Tracked[A] = State[String, A] - val f: Foo ~> Tracked = new (Foo ~> Tracked) { + val f: NaturalTransformation[Foo,Tracked] = new NaturalTransformation[Foo,Tracked] { def apply[A](fa: Foo[A]): Tracked[A] = State[String, A]{ s0 => (s0 + fa.toString + ";", fa.getA) } @@ -120,7 +120,7 @@ class FreeApplicativeTests extends CatsSuite { val z = Apply[Dsl].map2(x, y)((_, _) => ()) - val asString: Id ~> λ[α => String] = new (Id ~> λ[α => String]) { + val asString: NaturalTransformation[Id,λ[α => String]] = new NaturalTransformation[Id,λ[α => String]] { def apply[A](a: A): String = a.toString } diff --git a/free/src/test/scala/cats/free/FreeTests.scala b/free/src/test/scala/cats/free/FreeTests.scala index 52a8e882fd..1b9c7d63be 100644 --- a/free/src/test/scala/cats/free/FreeTests.scala +++ b/free/src/test/scala/cats/free/FreeTests.scala @@ -56,7 +56,7 @@ class FreeTests extends CatsSuite { z <- if (j<10000) a(j) else Free.pure[FTestApi, Int](j) } yield z - def runner: FTestApi ~> Id = new (FTestApi ~> Id) { + def runner: NaturalTransformation[FTestApi,Id] = new NaturalTransformation[FTestApi,Id] { def apply[A](fa: FTestApi[A]): Id[A] = fa match { case TB(i) => i+1 } @@ -77,7 +77,7 @@ object FreeTests extends FreeTestsInstances { } sealed trait FreeTestsInstances { - val headOptionU: List ~> Option = new (List ~> Option) { + val headOptionU: NaturalTransformation[List,Option] = new NaturalTransformation[List,Option] { def apply[A](fa: List[A]): Option[A] = fa.headOption } diff --git a/free/src/test/scala/cats/free/InjectTests.scala b/free/src/test/scala/cats/free/InjectTests.scala index eda62074e1..e957da6f00 100644 --- a/free/src/test/scala/cats/free/InjectTests.scala +++ b/free/src/test/scala/cats/free/InjectTests.scala @@ -1,6 +1,7 @@ package cats package free +import cats.arrow.NaturalTransformation import cats.tests.CatsSuite import cats.data.{Xor, Coproduct} import org.scalacheck._ @@ -39,19 +40,19 @@ class InjectTests extends CatsSuite { implicit def test2Arbitrary[A](implicit seqArb: Arbitrary[Int], intAArb : Arbitrary[Int => A]): Arbitrary[Test2[A]] = Arbitrary(for {s <- seqArb.arbitrary; f <- intAArb.arbitrary} yield Test2(s, f)) - object Test1Interpreter extends (Test1Algebra ~> Id) { + object Test1Interpreter extends NaturalTransformation[Test1Algebra,Id] { override def apply[A](fa: Test1Algebra[A]): Id[A] = fa match { case Test1(k, h) => h(k) } } - object Test2Interpreter extends (Test2Algebra ~> Id) { + object Test2Interpreter extends NaturalTransformation[Test2Algebra,Id] { override def apply[A](fa: Test2Algebra[A]): Id[A] = fa match { case Test2(k, h) => h(k) } } - val coProductInterpreter: T ~> Id = Test1Interpreter or Test2Interpreter + val coProductInterpreter: NaturalTransformation[T,Id] = Test1Interpreter or Test2Interpreter val x: Free[T, Int] = Free.inject[Test1Algebra, T](Test1(1, identity)) diff --git a/tests/src/test/scala/cats/tests/KleisliTests.scala b/tests/src/test/scala/cats/tests/KleisliTests.scala index c8f83db8fc..a081346975 100644 --- a/tests/src/test/scala/cats/tests/KleisliTests.scala +++ b/tests/src/test/scala/cats/tests/KleisliTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.arrow.{Arrow, Choice, Split} +import cats.arrow.{Arrow, Choice, Split, NaturalTransformation} import cats.data.{XorT, Kleisli, Reader} import cats.functor.{Contravariant, Strong} import cats.laws.discipline._ @@ -126,7 +126,7 @@ class KleisliTests extends CatsSuite { test("transform") { val opt = Kleisli { (x: Int) => Option(x.toDouble) } - val optToList = new (Option ~> List) { def apply[A](fa: Option[A]): List[A] = fa.toList } + val optToList = new NaturalTransformation[Option,List] { def apply[A](fa: Option[A]): List[A] = fa.toList } val list = opt.transform(optToList) val is = 0.to(10).toList diff --git a/tests/src/test/scala/cats/tests/NaturalTransformationTests.scala b/tests/src/test/scala/cats/tests/NaturalTransformationTests.scala index b46fa0026b..3c69bc8a57 100644 --- a/tests/src/test/scala/cats/tests/NaturalTransformationTests.scala +++ b/tests/src/test/scala/cats/tests/NaturalTransformationTests.scala @@ -28,11 +28,11 @@ class NaturalTransformationTests extends CatsSuite { case class Test2[A](v : A) extends Test2Algebra[A] - object Test1NT extends (Test1Algebra ~> Id) { + object Test1NT extends NaturalTransformation[Test1Algebra,Id] { override def apply[A](fa: Test1Algebra[A]): Id[A] = fa.v } - object Test2NT extends (Test2Algebra ~> Id) { + object Test2NT extends NaturalTransformation[Test2Algebra,Id] { override def apply[A](fa: Test2Algebra[A]): Id[A] = fa.v } From a77beec0d58c267a4d8e32bcc9c856871d7c1184 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 19 May 2016 10:38:07 -0400 Subject: [PATCH 152/185] removed support for `ApplicativeErrorSyntax` summon for `F[_, _]`, let SI-2712 fix rule it all --- .../scala/cats/syntax/applicativeError.scala | 11 ++----- .../cats/tests/ApplicativeErrorTests.scala | 29 +++++++++---------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/core/src/main/scala/cats/syntax/applicativeError.scala b/core/src/main/scala/cats/syntax/applicativeError.scala index 0107b4eb7f..5cf4f30b21 100644 --- a/core/src/main/scala/cats/syntax/applicativeError.scala +++ b/core/src/main/scala/cats/syntax/applicativeError.scala @@ -3,17 +3,12 @@ package syntax import cats.data.{Xor, XorT} -trait ApplicativeErrorSyntax1 { - implicit def applicativeErrorSyntax[F[_], E, A](fa: F[A])(implicit F: ApplicativeError[F, E]): ApplicativeErrorOps[F, E, A] = - new ApplicativeErrorOps[F, E, A](fa) -} - -trait ApplicativeErrorSyntax extends ApplicativeErrorSyntax1 { +trait ApplicativeErrorSyntax { implicit def applicativeErrorIdSyntax[E](e: E): ApplicativeErrorIdOps[E] = new ApplicativeErrorIdOps(e) - implicit def applicativeErrorSyntax2[F[_, _], E, A](fa: F[E, A])(implicit F: ApplicativeError[F[E, ?], E]): ApplicativeErrorOps[F[E, ?], E, A] = - new ApplicativeErrorOps[F[E, ?], E, A](fa) + implicit def applicativeErrorSyntax[F[_], E, A](fa: F[A])(implicit F: ApplicativeError[F, E]): ApplicativeErrorOps[F, E, A] = + new ApplicativeErrorOps[F, E, A](fa) } diff --git a/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala b/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala index 3677171a3b..78b71ac964 100644 --- a/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala +++ b/tests/src/test/scala/cats/tests/ApplicativeErrorTests.scala @@ -1,46 +1,43 @@ package cats package tests -import cats.data.{Xor, XorT} +import cats.data.{XorT} + class ApplicativeErrorCheck extends CatsSuite { - type ErrorOr[A] = String Xor A - val failed: String Xor Int = - "Badness".raiseError[ErrorOr, Int] - test("raiseError syntax creates an Xor with the correct type parameters") { - failed should === ("Badness".left[Int]) + val failed: Option[Int] = + (()).raiseError[Option, Int] + + test("raiseError syntax creates an Option with the correct value") { + failed should === (None: Option[Int]) } test("handleError syntax transforms an error to a success") { - failed.handleError(error => error.length) should === (7.right) + failed.handleError(_ => 7) should === (Some(7)) } test("handleErrorWith transforms an error to a success") { - failed.handleErrorWith(error => error.length.right) should === (7.right) + failed.handleErrorWith(_ => Some(7)) should === (Some(7)) } test("attempt syntax creates a wrapped Xor") { - failed.attempt should === ("Badness".left.right) + failed.attempt should === (Option(().left)) } test("attemptT syntax creates an XorT") { - failed.attemptT should === (XorT[ErrorOr, String, Int](failed.right)) + failed.attemptT should === (XorT[Option, Unit, Int](Option(().left))) } test("recover syntax transforms an error to a success") { - failed.recover { case error => error.length } should === (7.right) + failed.recover { case _ => 7 } should === (Some(7)) } test("recoverWith transforms an error to a success") { - failed.recoverWith { case error => error.length.right } should === (7.right) + failed.recoverWith { case _ => Some(7) } should === (Some(7)) } - test("syntax summon with F[_] ") { - val failedOption: Option[String] = None - failedOption.recoverWith { case error => Some("error") } should === (Some("error")) - } } From 8f7667cb86d826d10753f70e41cdbfb783f0e14f Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Thu, 19 May 2016 22:34:59 -0400 Subject: [PATCH 153/185] added release notes for 0.6.0 and updated Authors --- AUTHORS.md | 3 +++ CHANGES.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/AUTHORS.md b/AUTHORS.md index bf4201fca6..c760149274 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -26,6 +26,7 @@ possible: * Antoine Comte * Arya Irani * Ash Pook + * Aλ * Benjamin Thuillier * Binh Nguyen * Bobby Rauchenberg @@ -86,6 +87,7 @@ possible: * Sarunas Valaskevicius * Shunsuke Otani * Sinisa Louc + * Stephen Carman * Stephen Judkins * Stew O'Connor * Sumedh Mungee @@ -93,6 +95,7 @@ possible: * Travis Brown * Wedens * Yosef Fertel + * yilinwei * Zach Abbott We've tried to include everyone, but if you've made a contribution to diff --git a/CHANGES.md b/CHANGES.md index 246163b01e..a8fa5d7cda 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,57 @@ +## Version 0.6.0 + +> 2016 May 19 + +Version 0.6.0 is the sixth release. + +Highlights of this release: + +* [#990](https://github.com/typelevel/cats/pull/990): Separate free package into its own module +* [#1001](https://github.com/typelevel/cats/pull/1001): Introduce cats-kernel and remove algebra dependency + +This release also includes some API changes: + +* [#1046](https://github.com/typelevel/cats/pull/1046): summon `ApplicativeErrorSyntax` for `F[_]` instead of `F[_, _]` +* [#1034](https://github.com/typelevel/cats/pull/1034): Don't combine lefts on `Xor` and `XorT` `combine` +* [#1018](https://github.com/typelevel/cats/pull/1018): Remove blocking (JVM-only) Future instances +* [#877](https://github.com/typelevel/cats/pull/877): Remove required laziness in Prod, fixes #615 + + +And additions: + +* [#1032](https://github.com/typelevel/cats/pull/1032): Added `Coproduct` `fold` +* [#1028](https://github.com/typelevel/cats/pull/1028): Added `withFilter` for `OptionT` +* [#1014](https://github.com/typelevel/cats/pull/1014): Added `Monoid` instance for `WriterT` +* [#1029](https://github.com/typelevel/cats/pull/1029): Added an `ApplicativeError` instance for `Kleisli` and a `MonadError[Option, Unit]` to `std.option` +* [#1023](https://github.com/typelevel/cats/pull/1023): Add `XorT#fromEither` +* [#984](https://github.com/typelevel/cats/pull/984): Add `Validated.ensure` +* [#1020](https://github.com/typelevel/cats/pull/1020): Add `Traverse.traverseM` + + +And some code improvements: + +* [#1015](https://github.com/typelevel/cats/pull/1015): Add `Apply.map2Eval` and allow traverse laziness +* [#1024](https://github.com/typelevel/cats/pull/1024): Override reverse on reversed `PartialOrder` to return original instance +* [#880](https://github.com/typelevel/cats/pull/880): Optimize `Eq[Vector[A]]` instance +* [#1019](https://github.com/typelevel/cats/pull/1019): Use `Future#successful` in `pureEval` when possible + +And bug fixes: + +* [#1011](https://github.com/typelevel/cats/pull/1011): Add missing type parameters. + +And some other improvements to the organization documentation, tutorials, laws and tests, including: + +* [#1045](https://github.com/typelevel/cats/pull/1045): Add a link to the `OptionT` documentation from the monad docs. +* [#1043](https://github.com/typelevel/cats/pull/1043): Add notes about kind-projector usage in docs +* [#1042](https://github.com/typelevel/cats/pull/1042): Cats 0.5.0 no longer pre-release +* [#1036](https://github.com/typelevel/cats/pull/1036): Add FPiS to the "Resources for Learners" section +* [#1035](https://github.com/typelevel/cats/pull/1035): Run kernel-law tests for JS as part of build +* [#991](https://github.com/typelevel/cats/pull/991): Replace `~>` with `NaturalTransformation` +* [#1027](https://github.com/typelevel/cats/pull/1027): Remove unnecessary `nelSemigroup` from `traverse` doc +* [#1022](https://github.com/typelevel/cats/pull/1022): Add law-checking for `asMeetPartialOrder` and `asJoinPartialOrder` +* [#990](https://github.com/typelevel/cats/pull/990): Separate free package into its own module + + ## Version 0.5.0 > 2016 April 28 From 327f6350d304c48dd127518c158b69dd07c6f335 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Thu, 19 May 2016 22:46:59 -0400 Subject: [PATCH 154/185] Setting version to 0.7.0-SNAPSHOT --- version.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.sbt b/version.sbt index 113a45a1b2..017a6f897f 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.6.0" \ No newline at end of file +version in ThisBuild := "0.7.0-SNAPSHOT" \ No newline at end of file From 79dd3e90c73587d45aa8f9d14e76bc7630c22ec4 Mon Sep 17 00:00:00 2001 From: Erik Osheim Date: Fri, 20 May 2016 00:54:42 -0400 Subject: [PATCH 155/185] Bump versions from 0.5.0 to 0.6.0. --- README.md | 5 +++-- docs/src/site/index.md | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bffa329078..f3e3cff3e9 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ To get started with SBT, simply add the following to your `build.sbt` file: ```scala -libraryDependencies += "org.typelevel" %% "cats" % "0.5.0" +libraryDependencies += "org.typelevel" %% "cats" % "0.6.0" ``` This will pull in all of Cats' modules. If you only require some @@ -30,7 +30,8 @@ functionality, you can pick-and-choose from amongst these modules (used in place of `"cats"`): * `cats-macros`: Macros used by Cats syntax (*required*). - * `cats-core`: Core type classes and functionality (*required*). + * `cats-kernel`: Small set of basic type classes (*required*). + * `cats-core`: Most core type classes and functionality (*required*). * `cats-laws`: Laws for testing type class instances. * `cats-free`: Free structures such as the free monad, and supporting type classes. diff --git a/docs/src/site/index.md b/docs/src/site/index.md index 7f04546cfc..cb08a2b44f 100644 --- a/docs/src/site/index.md +++ b/docs/src/site/index.md @@ -21,14 +21,15 @@ Cats is currently available for Scala 2.10 and 2.11. To get started with SBT, simply add the following to your build.sbt file: - libraryDependencies += "org.typelevel" %% "cats" % "0.5.0" + libraryDependencies += "org.typelevel" %% "cats" % "0.6.0" This will pull in all of Cats' modules. If you only require some functionality, you can pick-and-choose from amongst these modules (used in place of `"cats"`): * `cats-macros`: Macros used by Cats syntax (*required*). - * `cats-core`: Core type classes and functionality (*required*). + * `cats-kernel`: Small set of basic type classes (*required*). + * `cats-core`: Most core type classes and functionality (*required*). * `cats-laws`: Laws for testing type class instances. * `cats-free`: Free structures such as the free monad, and supporting type classes. From 3efe27fe252b420f2ff25c5780f9fccd9cdf6a92 Mon Sep 17 00:00:00 2001 From: Zainab Ali Date: Fri, 20 May 2016 20:12:11 +0100 Subject: [PATCH 156/185] Adding CoflatMap and tests to WriterT --- core/src/main/scala/cats/data/WriterT.scala | 24 +++++++++++++------ .../cats/laws/discipline/CoflatMapTests.scala | 15 ++++++++---- .../test/scala/cats/tests/WriterTTests.scala | 11 +++++++++ 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 2ee07cc5cd..1f7db51055 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -87,8 +87,7 @@ private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 implicit val L0: Monoid[L] = L } - implicit def writerTIdFunctor[L]: Functor[WriterT[Id, L, ?]] = - writerTFunctor[Id, L] + def writerTIdFunctor[L]: Functor[WriterT[Id, L, ?]] = writerTIdCoflatMap implicit def writerTIdFlatMap[L:Semigroup]: FlatMap[WriterT[Id, L, ?]] = writerTFlatMap[Id, L] @@ -111,7 +110,11 @@ private[data] sealed abstract class WriterTInstances1 extends WriterTInstances2 new WriterTMonoid[F, L, V] { implicit val F0: Monoid[F[(L, V)]] = W } + + implicit def writerTIdCoflatMap[L]: CoflatMap[WriterT[Id, L, ?]] = + writerTCoflatMap[Id, L] } + private[data] sealed abstract class WriterTInstances2 extends WriterTInstances3 { implicit def writerTMonadWriter[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadWriter[WriterT[F, L, ?], L] = new WriterTMonadWriter[F, L] { @@ -131,6 +134,7 @@ private[data] sealed abstract class WriterTInstances3 extends WriterTInstances4 implicit val F0: Alternative[F] = F implicit val L0: Monoid[L] = L } + } private[data] sealed abstract class WriterTInstances4 extends WriterTInstances5 { @@ -168,7 +172,10 @@ private[data] sealed abstract class WriterTInstances6 extends WriterTInstances7 } private[data] sealed abstract class WriterTInstances7 { - implicit def writerTFunctor[F[_], L](implicit F: Functor[F]): Functor[WriterT[F, L, ?]] = new WriterTFunctor[F, L] { + + def writerTFunctor[F[_], L](implicit F: Functor[F]): Functor[WriterT[F, L, ?]] = writerTCoflatMap + + implicit def writerTCoflatMap[F[_], L](implicit F: Functor[F]): WriterTCoflatMap[F, L] = new WriterTCoflatMap[F, L] { implicit val F0: Functor[F] = F } } @@ -259,12 +266,18 @@ private[data] sealed trait WriterTSemigroup[F[_], L, A] extends Semigroup[Writer WriterT(F0.combine(x.run, y.run)) } -private[data] sealed trait WriterTMonoid[F[_], L, A] extends Monoid[WriterT[F, L, A]] with WriterTSemigroup[F, L, A]{ +private[data] sealed trait WriterTMonoid[F[_], L, A] extends Monoid[WriterT[F, L, A]] with WriterTSemigroup[F, L, A] { override implicit def F0: Monoid[F[(L, A)]] def empty: WriterT[F, L, A] = WriterT(F0.empty) } +private[data] sealed trait WriterTCoflatMap[F[_], L] extends CoflatMap[WriterT[F, L, ?]] with WriterTFunctor[F, L] { + + def coflatMap[A, B](fa: WriterT[F, L, A])(f: WriterT[F, L, A] => B): WriterT[F, L, B] = fa.map(_ => f(fa)) +} + + trait WriterTFunctions { def putT[F[_], L, V](vf: F[V])(l: L)(implicit functorF: Functor[F]): WriterT[F, L, V] = WriterT(functorF.map(vf)(v => (l, v))) @@ -281,6 +294,3 @@ trait WriterTFunctions { def valueT[F[_], L, V](vf: F[V])(implicit functorF: Functor[F], monoidL: Monoid[L]): WriterT[F, L, V] = WriterT.putT[F, L, V](vf)(monoidL.empty) } - - - diff --git a/laws/src/main/scala/cats/laws/discipline/CoflatMapTests.scala b/laws/src/main/scala/cats/laws/discipline/CoflatMapTests.scala index 0abeff6ad9..887a2cb9d1 100644 --- a/laws/src/main/scala/cats/laws/discipline/CoflatMapTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/CoflatMapTests.scala @@ -7,18 +7,25 @@ import org.scalacheck.Prop import Prop._ import org.typelevel.discipline.Laws -trait CoflatMapTests[F[_]] extends Laws { +trait CoflatMapTests[F[_]] extends Laws with FunctorTests[F] { def laws: CoflatMapLaws[F] def coflatMap[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit ArbFA: Arbitrary[F[A]], EqFA: Eq[F[A]], - EqFC: Eq[F[C]] + EqFC: Eq[F[C]], + EqFFA: Eq[F[F[A]]], + EqFB: Eq[F[B]], + EqFFFA: Eq[F[F[F[A]]]] ): RuleSet = { new DefaultRuleSet( name = "coflatMap", - parent = None, - "coflatMap associativity" -> forAll(laws.coflatMapAssociativity[A, B, C] _)) + parent = Some(functor[A, B, C]), + "coflatMap associativity" -> forAll(laws.coflatMapAssociativity[A, B, C] _), + "coflatMap identity" -> forAll(laws.coflatMapIdentity[A, B] _), + "coflatten coherence" -> forAll(laws.coflattenCoherence[A, B] _), + "coflatten throughMap" -> forAll(laws.coflattenThroughMap[A] _) + ) } } diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index f6223e879f..d1bb73caa1 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -268,4 +268,15 @@ class WriterTTests extends CatsSuite { Semigroup[WriterT[Id, Int, Int]] } + + { + // F has a functor + implicit val F: Functor[ListWrapper] = ListWrapper.functor + + CoflatMap[WriterT[ListWrapper, Int, ?]] + checkAll("WriterT[Listwrapper, Int, ?]", CoflatMapTests[WriterT[ListWrapper, Int, ?]].coflatMap[Int, Int, Int]) + + // Id has a Functor + CoflatMap[WriterT[Id, Int, ?]] + } } From 6ab573c528843379b6f4a0c5b29a26b6e14c978d Mon Sep 17 00:00:00 2001 From: Zainab Ali Date: Fri, 20 May 2016 21:35:32 +0100 Subject: [PATCH 157/185] Added Serializable test to WriterT CoflatMap --- tests/src/test/scala/cats/tests/WriterTTests.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index d1bb73caa1..8ca9f789aa 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -270,11 +270,12 @@ class WriterTTests extends CatsSuite { } { - // F has a functor + // F has a Functor implicit val F: Functor[ListWrapper] = ListWrapper.functor CoflatMap[WriterT[ListWrapper, Int, ?]] checkAll("WriterT[Listwrapper, Int, ?]", CoflatMapTests[WriterT[ListWrapper, Int, ?]].coflatMap[Int, Int, Int]) + checkAll("WriterT[ListWrapper, Int, ?]", SerializableTests.serializable(CoflatMap[WriterT[ListWrapper, Int, ?]])) // Id has a Functor CoflatMap[WriterT[Id, Int, ?]] From 28cd7550cce08878541d25482157d0b3c5aab292 Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Fri, 20 May 2016 18:17:41 -0400 Subject: [PATCH 158/185] Process doc - add note about editing an existing release --- PROCESS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PROCESS.md b/PROCESS.md index 15db8b7e69..8394154520 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -87,7 +87,8 @@ does not account for conversations that occured on Github. Once the relevant documentation changes have been committed, new [release notes](https://github.com/typelevel/cats/releases) should be added. You can add a release by clicking the "Draft a new release" button -on that page. +on that page, or if the relevant release already exists, you can click +"Edit release". The website should then be updated via `sbt docs/ghpagesPushSite`. From b1cf1c0089f28acddee81bb05eb6ef7e4b86abec Mon Sep 17 00:00:00 2001 From: Cody Allen Date: Fri, 20 May 2016 18:19:58 -0400 Subject: [PATCH 159/185] Use pygments instead of rouge for syntax highlighting It appears that GitHub is already using rouge instead of pygments for the cats site, because it doesn't support pygments. This change is so I stop getting the following email every time I push the cats website: > You are attempting to use the 'pygments' highlighter, which is currently > unsupported on GitHub Pages. Your site will use 'rouge' for highlighting > instead. To suppress this warning, change the 'highlighter' value to > 'rouge' in your '_config.yml' and ensure the 'pygments' key is unset. --- docs/src/site/_config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/site/_config.yml b/docs/src/site/_config.yml index 79555d8ad3..26c3f77031 100644 --- a/docs/src/site/_config.yml +++ b/docs/src/site/_config.yml @@ -1,6 +1,6 @@ name: Cats Documentation markdown: redcarpet -highlighter: pygments +highlighter: rouge baseurl: /cats apidocs: /cats/api/ sources: https://github.com/typelevel/cats/blob/master/ From 128c49478fd1e486d7b5ab6941e0ba238318bfb8 Mon Sep 17 00:00:00 2001 From: Zainab Ali Date: Sat, 21 May 2016 00:11:01 +0100 Subject: [PATCH 160/185] Removing WriterT Functor instance in place of CoflatMap instance --- core/src/main/scala/cats/data/WriterT.scala | 10 ++++------ tests/src/test/scala/cats/tests/WriterTTests.scala | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 1f7db51055..81ee317e22 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -87,8 +87,6 @@ private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 implicit val L0: Monoid[L] = L } - def writerTIdFunctor[L]: Functor[WriterT[Id, L, ?]] = writerTIdCoflatMap - implicit def writerTIdFlatMap[L:Semigroup]: FlatMap[WriterT[Id, L, ?]] = writerTFlatMap[Id, L] @@ -111,7 +109,7 @@ private[data] sealed abstract class WriterTInstances1 extends WriterTInstances2 implicit val F0: Monoid[F[(L, V)]] = W } - implicit def writerTIdCoflatMap[L]: CoflatMap[WriterT[Id, L, ?]] = + implicit def writerTIdCoflatMap[L]: CoflatMap[WriterT[Id, L, ?]] = writerTCoflatMap[Id, L] } @@ -164,6 +162,7 @@ private[data] sealed abstract class WriterTInstances5 extends WriterTInstances6 } private[data] sealed abstract class WriterTInstances6 extends WriterTInstances7 { + implicit def writerTApply[F[_], L](implicit F: Apply[F], L: Semigroup[L]): Apply[WriterT[F, L, ?]] = new WriterTApply[F, L] { implicit val F0: Apply[F] = F @@ -173,9 +172,8 @@ private[data] sealed abstract class WriterTInstances6 extends WriterTInstances7 private[data] sealed abstract class WriterTInstances7 { - def writerTFunctor[F[_], L](implicit F: Functor[F]): Functor[WriterT[F, L, ?]] = writerTCoflatMap - - implicit def writerTCoflatMap[F[_], L](implicit F: Functor[F]): WriterTCoflatMap[F, L] = new WriterTCoflatMap[F, L] { + implicit def writerTCoflatMap[F[_], L](implicit F: Functor[F]): WriterTCoflatMap[F, L] = + new WriterTCoflatMap[F, L] { implicit val F0: Functor[F] = F } } diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index 8ca9f789aa..58dc04505d 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -101,7 +101,7 @@ class WriterTTests extends CatsSuite { checkAll("Bifunctor[WriterT[ListWrapper, ?, ?]]", SerializableTests.serializable(Bifunctor[WriterT[ListWrapper, ?, ?]])) } - implicit val iso = CartesianTests.Isomorphisms.invariant[WriterT[ListWrapper, ListWrapper[Int], ?]](WriterT.writerTFunctor(ListWrapper.functor)) + implicit val iso = CartesianTests.Isomorphisms.invariant[WriterT[ListWrapper, ListWrapper[Int], ?]](WriterT.writerTCoflatMap(ListWrapper.functor)) // We have varying instances available depending on `F` and `L`. // We also battle some inference issues with `Id`. From 82443c9f68f54c53a9836c89d3cb16e99d208946 Mon Sep 17 00:00:00 2001 From: Zainab Ali Date: Sat, 21 May 2016 15:53:52 +0100 Subject: [PATCH 161/185] Adding check for Functor in WriterT CoflatMap tests --- core/src/main/scala/cats/data/WriterT.scala | 4 ++-- tests/src/test/scala/cats/tests/WriterTTests.scala | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 81ee317e22..2c89f43dba 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -172,9 +172,9 @@ private[data] sealed abstract class WriterTInstances6 extends WriterTInstances7 private[data] sealed abstract class WriterTInstances7 { - implicit def writerTCoflatMap[F[_], L](implicit F: Functor[F]): WriterTCoflatMap[F, L] = + implicit def writerTCoflatMap[F[_], L](implicit F: Functor[F]): CoflatMap[WriterT[F, L, ?]] = new WriterTCoflatMap[F, L] { - implicit val F0: Functor[F] = F + implicit val F0: Functor[F] = F } } diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index 58dc04505d..55bff93d00 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -273,11 +273,13 @@ class WriterTTests extends CatsSuite { // F has a Functor implicit val F: Functor[ListWrapper] = ListWrapper.functor + Functor[WriterT[ListWrapper, Int, ?]] CoflatMap[WriterT[ListWrapper, Int, ?]] checkAll("WriterT[Listwrapper, Int, ?]", CoflatMapTests[WriterT[ListWrapper, Int, ?]].coflatMap[Int, Int, Int]) checkAll("WriterT[ListWrapper, Int, ?]", SerializableTests.serializable(CoflatMap[WriterT[ListWrapper, Int, ?]])) // Id has a Functor + Functor[WriterT[Id, Int, ?]] CoflatMap[WriterT[Id, Int, ?]] } } From 37e738012c22b9fe5c5ef64b1af681389892db47 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Sat, 21 May 2016 12:19:08 -0400 Subject: [PATCH 162/185] updated sbt to 0.13.11 --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 817bc38df8..43b8278c68 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.9 +sbt.version=0.13.11 From b106f1d11f6156f4aea8c3d3ff34fbfa923ae16a Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 20 May 2016 21:44:47 -0400 Subject: [PATCH 163/185] skip javadoc compilation for 2.10 --- build.sbt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 935d953264..4e7f648fff 100644 --- a/build.sbt +++ b/build.sbt @@ -82,7 +82,7 @@ lazy val commonJvmSettings = Seq( // JVM settings. https://github.com/tkawachi/sbt-doctest/issues/52 ) ++ catsDoctestSettings -lazy val catsSettings = buildSettings ++ commonSettings ++ publishSettings ++ scoverageSettings +lazy val catsSettings = buildSettings ++ commonSettings ++ publishSettings ++ scoverageSettings ++ skip210DocSettings lazy val scalacheckVersion = "1.12.5" @@ -106,6 +106,14 @@ def noDocProjects(sv: String): Seq[ProjectReference] = CrossVersion.partialVersi case _ => Nil } +lazy val skip210DocSettings = Seq( + sources in (Compile, doc) := ( + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, 10)) => Nil + case _ => (sources in (Compile, doc)).value + }) +) + lazy val docSettings = Seq( autoAPIMappings := true, unidocProjectFilter in (ScalaUnidoc, unidoc) := From 04bfb7bf90d0b58f23acb32c64ecca7f9a9740ea Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Sat, 21 May 2016 09:13:59 -0400 Subject: [PATCH 164/185] consolidate skip 210 doc settings --- build.sbt | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/build.sbt b/build.sbt index 4e7f648fff..e52b23a6fa 100644 --- a/build.sbt +++ b/build.sbt @@ -82,7 +82,7 @@ lazy val commonJvmSettings = Seq( // JVM settings. https://github.com/tkawachi/sbt-doctest/issues/52 ) ++ catsDoctestSettings -lazy val catsSettings = buildSettings ++ commonSettings ++ publishSettings ++ scoverageSettings ++ skip210DocSettings +lazy val catsSettings = buildSettings ++ commonSettings ++ publishSettings ++ scoverageSettings ++ javadocSettings lazy val scalacheckVersion = "1.12.5" @@ -95,29 +95,27 @@ lazy val testingDependencies = Seq( libraryDependencies += "org.typelevel" %%% "catalysts-macros" % "0.0.2" % "test", libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.0-M7" % "test") + /** - * Remove 2.10 projects from doc generation, as the macros used in the projects - * cause problems generating the documentation on scala 2.10. As the APIs for 2.10 - * and 2.11 are the same this has no effect on the resultant documentation, though - * it does mean that the scaladocs cannot be generated when the build is in 2.10 mode. - */ -def noDocProjects(sv: String): Seq[ProjectReference] = CrossVersion.partialVersion(sv) match { - case Some((2, 10)) => Seq[ProjectReference](coreJVM) - case _ => Nil + * Remove 2.10 projects from doc generation, as the macros used in the projects + * cause problems generating the documentation on scala 2.10. As the APIs for 2.10 + * and 2.11 are the same this has no effect on the resultant documentation, though + * it does mean that the scaladocs cannot be generated when the build is in 2.10 mode. + */ +def docsSourcesAndProjects(sv: String): (Boolean, Seq[ProjectReference]) = + CrossVersion.partialVersion(sv) match { + case Some((2, 10)) => (false, Nil) + case _ => (true, Seq(coreJVM, freeJVM)) } -lazy val skip210DocSettings = Seq( - sources in (Compile, doc) := ( - CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, 10)) => Nil - case _ => (sources in (Compile, doc)).value - }) +lazy val javadocSettings = Seq( + sources in (Compile, doc) := (if (docsSourcesAndProjects(scalaVersion.value)._1) (sources in (Compile, doc)).value else Nil) ) lazy val docSettings = Seq( autoAPIMappings := true, unidocProjectFilter in (ScalaUnidoc, unidoc) := - inProjects(coreJVM, freeJVM) -- inProjects(noDocProjects(scalaVersion.value): _*), + inProjects(docsSourcesAndProjects(scalaVersion.value)._2:_*), site.addMappingsToSiteDir(mappings in (ScalaUnidoc, packageDoc), "api"), site.addMappingsToSiteDir(tut, "_tut"), ghpagesNoJekyll := false, From b005e1774eae463e45b0ba281508a0320d9f531a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20G=C3=B3mez?= Date: Mon, 23 May 2016 16:56:25 +0200 Subject: [PATCH 165/185] Upgade tut-plugin --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 6c3ba461f7..0f5dbe47ce 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,7 +3,7 @@ addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.0") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.3") addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.8.1") -addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.0") +addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.2") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.3") addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.2.0") From 2d3eb7e2f8e4fb1aefd539d0db454dc2152c94f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20G=C3=B3mez?= Date: Mon, 23 May 2016 17:04:17 +0200 Subject: [PATCH 166/185] Change tut blocks to tut:book Closes #1055 --- docs/src/main/tut/applicative.md | 4 ++-- docs/src/main/tut/apply.md | 18 +++++++++--------- docs/src/main/tut/contravariant.md | 6 +++--- docs/src/main/tut/foldable.md | 8 ++++---- docs/src/main/tut/freeapplicative.md | 4 ++-- docs/src/main/tut/freemonad.md | 6 +++--- docs/src/main/tut/functor.md | 12 ++++++------ docs/src/main/tut/id.md | 8 ++++---- docs/src/main/tut/invariant.md | 2 +- docs/src/main/tut/kleisli.md | 2 +- docs/src/main/tut/monad.md | 6 +++--- docs/src/main/tut/monoid.md | 8 ++++---- docs/src/main/tut/semigroup.md | 8 ++++---- docs/src/main/tut/semigroupk.md | 8 ++++---- docs/src/main/tut/state.md | 12 ++++++------ docs/src/main/tut/traverse.md | 8 ++++---- docs/src/main/tut/typeclasses.md | 4 ++-- docs/src/main/tut/validated.md | 10 +++++----- docs/src/main/tut/xor.md | 16 ++++++++-------- 19 files changed, 75 insertions(+), 75 deletions(-) diff --git a/docs/src/main/tut/applicative.md b/docs/src/main/tut/applicative.md index 833c7acc89..9fca90c1d5 100644 --- a/docs/src/main/tut/applicative.md +++ b/docs/src/main/tut/applicative.md @@ -20,7 +20,7 @@ obvious. For `Option`, the `pure` operation wraps the value in `Some`. For `List`, the `pure` operation returns a single element `List`: -```tut +```tut:book import cats._ import cats.std.all._ @@ -34,7 +34,7 @@ you compose one `Applicative` with another, the resulting `pure` operation will lift the passed value into one context, and the result into the other context: -```tut +```tut:book (Applicative[List] compose Applicative[Option]).pure(1) ``` diff --git a/docs/src/main/tut/apply.md b/docs/src/main/tut/apply.md index 9a69e14a66..c2e007f0ef 100644 --- a/docs/src/main/tut/apply.md +++ b/docs/src/main/tut/apply.md @@ -40,7 +40,7 @@ implicit val listApply: Apply[List] = new Apply[List] { Since `Apply` extends `Functor`, we can use the `map` method from `Functor`: -```tut +```tut:book Apply[Option].map(Some(1))(intToString) Apply[Option].map(Some(1))(double) Apply[Option].map(None)(double) @@ -50,7 +50,7 @@ Apply[Option].map(None)(double) And like functors, `Apply` instances also compose: -```tut +```tut:book val listOpt = Apply[List] compose Apply[Option] val plusOne = (x:Int) => x + 1 listOpt.ap(List(Some(plusOne)))(List(Some(1), None, Some(3))) @@ -59,7 +59,7 @@ listOpt.ap(List(Some(plusOne)))(List(Some(1), None, Some(3))) ### ap The `ap` method is a method that `Functor` does not have: -```tut +```tut:book Apply[Option].ap(Some(intToString))(Some(1)) Apply[Option].ap(Some(double))(Some(1)) Apply[Option].ap(Some(double))(None) @@ -74,7 +74,7 @@ accept `N` arguments where `ap` accepts `1`: For example: -```tut +```tut:book val addArity2 = (a: Int, b: Int) => a + b Apply[Option].ap2(Some(addArity2))(Some(1), Some(2)) @@ -86,7 +86,7 @@ Note that if any of the arguments of this example is `None`, the final result is `None` as well. The effects of the context we are operating on are carried through the entire computation: -```tut +```tut:book Apply[Option].ap2(Some(addArity2))(Some(1), None) Apply[Option].ap4(None)(Some(1), Some(2), Some(3), Some(4)) ``` @@ -95,7 +95,7 @@ Apply[Option].ap4(None)(Some(1), Some(2), Some(3), Some(4)) Similarly, `mapN` functions are available: -```tut +```tut:book Apply[Option].map2(Some(1), Some(2))(addArity2) Apply[Option].map3(Some(1), Some(2), Some(3))(addArity3) @@ -105,7 +105,7 @@ Apply[Option].map3(Some(1), Some(2), Some(3))(addArity3) And `tupleN`: -```tut +```tut:book Apply[Option].tuple2(Some(1), Some(2)) Apply[Option].tuple3(Some(1), Some(2), Some(3)) @@ -118,7 +118,7 @@ functions (`apN`, `mapN` and `tupleN`). In order to use it, first import `cats.syntax.all._` or `cats.syntax.cartesian._`. Here we see that the following two functions, `f1` and `f2`, are equivalent: -```tut +```tut:book import cats.syntax.cartesian._ def f1(a: Option[Int], b: Option[Int], c: Option[Int]) = @@ -133,7 +133,7 @@ f2(Some(1), Some(2), Some(3)) All instances created by `|@|` have `map`, `ap`, and `tupled` methods of the appropriate arity: -```tut +```tut:book val option2 = Option(1) |@| Option(2) val option3 = option2 |@| Option.empty[Int] diff --git a/docs/src/main/tut/contravariant.md b/docs/src/main/tut/contravariant.md index 80fc363cb1..df1dea5dc2 100644 --- a/docs/src/main/tut/contravariant.md +++ b/docs/src/main/tut/contravariant.md @@ -40,7 +40,7 @@ If we want to show a `Salary` instance, we can just convert it to a `Money` inst Let's use `Show`'s `Contravariant`: -```tut +```tut:book implicit val showSalary: Show[Salary] = showMoney.contramap(_.size) Salary(Money(1000)).show @@ -52,7 +52,7 @@ Salary(Money(1000)).show `scala.math.Ordering` typeclass defines comparison operations, e.g. `compare`: -```tut +```tut:book Ordering.Int.compare(2, 1) Ordering.Int.compare(1, 2) ``` @@ -67,7 +67,7 @@ In fact, it is just `contramap`, defined in a slightly different way! We supply So let's use it in our advantage and get `Ordering[Money]` for free: -```tut +```tut:book // we need this for `<` to work import scala.math.Ordered._ diff --git a/docs/src/main/tut/foldable.md b/docs/src/main/tut/foldable.md index 81e4ea09fa..a990a635c0 100644 --- a/docs/src/main/tut/foldable.md +++ b/docs/src/main/tut/foldable.md @@ -32,7 +32,7 @@ import cats.implicits._ And examples. -```tut +```tut:book Foldable[List].fold(List("a", "b", "c")) Foldable[List].foldMap(List(1, 2, 4))(_.toString) Foldable[List].foldK(List(List(1,2,3), List(2,3,4))) @@ -95,7 +95,7 @@ Scala's standard library might expect. This will prevent operations which are lazy in their right hand argument to traverse the entire structure unnecessarily. For example, if you have: -```tut +```tut:book val allFalse = Stream.continually(false) ``` @@ -108,7 +108,7 @@ value. Using `foldRight` from the standard library *will* try to consider the entire stream, and thus will eventually cause a stack overflow: -```tut +```tut:book try { allFalse.foldRight(true)(_ && _) } catch { @@ -119,6 +119,6 @@ try { With the lazy `foldRight` on `Foldable`, the calculation terminates after looking at only one value: -```tut +```tut:book Foldable[Stream].foldRight(allFalse, Eval.True)((a,b) => if (a) b else Eval.now(false)).value ``` diff --git a/docs/src/main/tut/freeapplicative.md b/docs/src/main/tut/freeapplicative.md index 80a5605a36..238d44ded1 100644 --- a/docs/src/main/tut/freeapplicative.md +++ b/docs/src/main/tut/freeapplicative.md @@ -71,7 +71,7 @@ val compiler = } ``` -```tut +```tut:book val validator = prog.foldMap[FromString](compiler) validator("1234") validator("12345") @@ -142,7 +142,7 @@ def logValidation[A](validation: Validation[A]): List[String] = validation.foldMap[Log](logCompiler).getConst ``` -```tut +```tut:book logValidation(prog) logValidation(size(5) *> hasNumber *> size(10)) logValidation((hasNumber |@| size(3)).map(_ || _)) diff --git a/docs/src/main/tut/freemonad.md b/docs/src/main/tut/freemonad.md index e84d130f86..d8cf2a2848 100644 --- a/docs/src/main/tut/freemonad.md +++ b/docs/src/main/tut/freemonad.md @@ -252,7 +252,7 @@ under `Monad`). As `Id` is a `Monad`, we can use `foldMap`. To run your `Free` with previous `impureCompiler`: -```tut +```tut:book val result: Option[Int] = program.foldMap(impureCompiler) ``` @@ -291,7 +291,7 @@ val pureCompiler: KVStoreA ~> KVStoreState = new (KVStoreA ~> KVStoreState) { support for pattern matching is limited by the JVM's type erasure, but it's not too hard to get around.) -```tut +```tut:book val result: (Map[String, Any], Option[Int]) = program.foldMap(pureCompiler).run(Map.empty).value ``` @@ -403,7 +403,7 @@ Now if we run our program and type in "snuggles" when prompted, we see something import DataSource._, Interacts._ ``` -```tut +```tut:book val evaled: Unit = program.foldMap(interpreter) ``` diff --git a/docs/src/main/tut/functor.md b/docs/src/main/tut/functor.md index 1d31cd809e..5b8884edca 100644 --- a/docs/src/main/tut/functor.md +++ b/docs/src/main/tut/functor.md @@ -23,7 +23,7 @@ This method takes a function `A => B` and turns an `F[A]` into an method that exists on many classes in the Scala standard library, for example: -```tut +```tut:book Option(1).map(_ + 1) List(1,2,3).map(_ + 1) Vector(1,2,3).map(_.toString) @@ -72,7 +72,7 @@ Without kind-projector, we'd have to write this as something like `List` is a functor which applies the function to each element of the list: -```tut +```tut:book val len: String => Int = _.length Functor[List].map(List("qwer", "adsfg"))(len) ``` @@ -80,7 +80,7 @@ Functor[List].map(List("qwer", "adsfg"))(len) `Option` is a functor which only applies the function when the `Option` value is a `Some`: -```tut +```tut:book Functor[Option].map(Some("adsf"))(len) // Some(x) case: function is applied to x; result is wrapped in Some Functor[Option].map(None)(len) // None case: simply returns None (function is not applied) ``` @@ -91,7 +91,7 @@ Functor[Option].map(None)(len) // None case: simply returns None (function is no We can use `Functor` to "lift" a function from `A => B` to `F[A] => F[B]`: -```tut +```tut:book val lenOption: Option[String] => Option[Int] = Functor[Option].lift(len) lenOption(Some("abcd")) ``` @@ -101,7 +101,7 @@ lenOption(Some("abcd")) `Functor` provides an `fproduct` function which pairs a value with the result of applying a function to that value. -```tut +```tut:book val source = List("a", "aa", "b", "ccccc") Functor[List].fproduct(source)(len).toMap ``` @@ -111,7 +111,7 @@ Functor[List].fproduct(source)(len).toMap Functors compose! Given any functor `F[_]` and any functor `G[_]` we can create a new functor `F[G[_]]` by composing them: -```tut +```tut:book val listOpt = Functor[List] compose Functor[Option] listOpt.map(List(Some(1), None, Some(3)))(_ + 1) val optList = Functor[Option] compose Functor[List] diff --git a/docs/src/main/tut/id.md b/docs/src/main/tut/id.md index 3b48fbad67..05149ba0bc 100644 --- a/docs/src/main/tut/id.md +++ b/docs/src/main/tut/id.md @@ -21,7 +21,7 @@ That is to say that the type Id[A] is just a synonym for A. We can freely treat values of type `A` as values of type `Id[A]`, and vice-versa. -```tut +```tut:book import cats._ val x: Id[Int] = 1 @@ -34,7 +34,7 @@ method, which has type `A => Id[A]` just becomes the identity function. The `map` method from `Functor` just becomes function application: -```tut +```tut:book import cats.Functor val one: Int = 1 @@ -56,7 +56,7 @@ two functions the same, and, in fact, they can have the same implementation, meaning that for `Id`, `flatMap` is also just function application: -```tut +```tut:book import cats.Monad val one: Int = 1 @@ -66,7 +66,7 @@ Monad[Id].flatMap(one)(_ + 1) And that similarly, coflatMap is just function application: -```tut +```tut:book import cats.Comonad Comonad[Id].coflatMap(one)(_ + 1) diff --git a/docs/src/main/tut/invariant.md b/docs/src/main/tut/invariant.md index eb1d6299f7..371a50350a 100644 --- a/docs/src/main/tut/invariant.md +++ b/docs/src/main/tut/invariant.md @@ -102,6 +102,6 @@ val today: Date = longToDate(1449088684104l) val timeLeft: Date = longToDate(1900918893l) ``` -```tut +```tut:book today |+| timeLeft ``` diff --git a/docs/src/main/tut/kleisli.md b/docs/src/main/tut/kleisli.md index 3b03916e0f..8505b7513b 100644 --- a/docs/src/main/tut/kleisli.md +++ b/docs/src/main/tut/kleisli.md @@ -36,7 +36,7 @@ val twiceAsManyCats: Int => String = Thus. -```tut +```tut:book twiceAsManyCats(1) // "2 cats" ``` diff --git a/docs/src/main/tut/monad.md b/docs/src/main/tut/monad.md index 36ecf30cad..c41a7d150e 100644 --- a/docs/src/main/tut/monad.md +++ b/docs/src/main/tut/monad.md @@ -15,7 +15,7 @@ that we have a single context (ie. `F[A]`). The name `flatten` should remind you of the functions of the same name on many classes in the standard library. -```tut +```tut:book Option(Option(1)).flatten Option(None).flatten List(List(1),List(2,3)).flatten @@ -63,7 +63,7 @@ Part of the reason for this is that name `flatMap` has special significance in scala, as for-comprehensions rely on this method to chain together operations in a monadic context. -```tut +```tut:book import scala.reflect.runtime.universe universe.reify( @@ -80,7 +80,7 @@ universe.reify( the results of earlier ones. This is embodied in `ifM`, which lifts an `if` statement into the monadic context. -```tut +```tut:book Monad[List].ifM(List(true, false, true))(List(1, 2), List(3, 4)) ``` diff --git a/docs/src/main/tut/monoid.md b/docs/src/main/tut/monoid.md index b31f6e6662..6acbe6e48f 100644 --- a/docs/src/main/tut/monoid.md +++ b/docs/src/main/tut/monoid.md @@ -34,7 +34,7 @@ import cats.implicits._ Examples. -```tut +```tut:book Monoid[String].empty Monoid[String].combineAll(List("a", "b", "c")) Monoid[String].combineAll(List()) @@ -44,7 +44,7 @@ The advantage of using these type class provided methods, rather than the specific ones for each type, is that we can compose monoids to allow us to operate on more complex types, e.g. -```tut +```tut:book Monoid[Map[String,Int]].combineAll(List(Map("a" -> 1, "b" -> 2), Map("a" -> 3))) Monoid[Map[String,Int]].combineAll(List()) ``` @@ -53,7 +53,7 @@ This is also true if we define our own instances. As an example, let's use [`Foldable`](foldable.html)'s `foldMap`, which maps over values accumulating the results, using the available `Monoid` for the type mapped onto. -```tut +```tut:book val l = List(1, 2, 3, 4, 5) l.foldMap(identity) l.foldMap(i => i.toString) @@ -64,7 +64,7 @@ with a function that produces a tuple, cats also provides a `Monoid` for a tuple that will be valid for any tuple where the types it contains also have a `Monoid` available, thus. -```tut +```tut:book l.foldMap(i => (i, i.toString)) // do both of the above in one pass, hurrah! ``` diff --git a/docs/src/main/tut/semigroup.md b/docs/src/main/tut/semigroup.md index c1045ef563..79d153acf9 100644 --- a/docs/src/main/tut/semigroup.md +++ b/docs/src/main/tut/semigroup.md @@ -37,7 +37,7 @@ import cats.implicits._ Examples. -```tut +```tut:book Semigroup[Int].combine(1, 2) Semigroup[List[Int]].combine(List(1,2,3), List(4,5,6)) Semigroup[Option[Int]].combine(Option(1), Option(2)) @@ -50,14 +50,14 @@ which allow for such combining, e.g. `++` on List, but the value of having a `Semigroup` type class available is that these compose, so for instance, we can say -```tut +```tut:book Map("foo" -> Map("bar" -> 5)).combine(Map("foo" -> Map("bar" -> 6), "baz" -> Map())) Map("foo" -> List(1, 2)).combine(Map("foo" -> List(3,4), "bar" -> List(42))) ``` which is far more likely to be useful than -```tut +```tut:book Map("foo" -> Map("bar" -> 5)) ++ Map("foo" -> Map("bar" -> 6), "baz" -> Map()) Map("foo" -> List(1, 2)) ++ Map("foo" -> List(3,4), "bar" -> List(42)) ``` @@ -78,7 +78,7 @@ val n: Option[Int] = None Thus. -```tut +```tut:book one |+| two n |+| two n |+| n diff --git a/docs/src/main/tut/semigroupk.md b/docs/src/main/tut/semigroupk.md index 1085c6ee56..2cd8cb1f02 100644 --- a/docs/src/main/tut/semigroupk.md +++ b/docs/src/main/tut/semigroupk.md @@ -44,7 +44,7 @@ import cats.std.all._ Examples. -```tut +```tut:book Semigroup[Int].combine(1, 2) Semigroup[List[Int]].combine(List(1,2,3), List(4,5,6)) Semigroup[Option[Int]].combine(Option(1), Option(2)) @@ -67,7 +67,7 @@ and, in fact, the `K` in `SemigroupK` stands for `Kind`. For `List`, the `Semigroup` instance's `combine` operation and the `SemigroupK` instance's `combineK` operation are both list concatenation: -```tut +```tut:book SemigroupK[List].combineK(List(1,2,3), List(4,5,6)) == Semigroup[List[Int]].combine(List(1,2,3), List(4,5,6)) ``` @@ -84,7 +84,7 @@ two of them. Therefore, in the case of `Option` the `SemigroupK[Option].combineK` method has no choice but to use the `orElse` method of Option: -```tut +```tut:book Semigroup[Option[Int]].combine(Some(1), Some(2)) SemigroupK[Option].combineK(Some(1), Some(2)) SemigroupK[Option].combineK(Some(1), None) @@ -108,7 +108,7 @@ val n: Option[Int] = None Thus. -```tut +```tut:book one |+| two one <+> two n |+| two diff --git a/docs/src/main/tut/state.md b/docs/src/main/tut/state.md index 980ccc5cd4..e60ff301b2 100644 --- a/docs/src/main/tut/state.md +++ b/docs/src/main/tut/state.md @@ -41,7 +41,7 @@ def createRobot(): Robot = { } ``` -```tut +```tut:book val robot = createRobot() ``` @@ -62,7 +62,7 @@ def createRobot(): Robot = { } ``` -```tut +```tut:book val robot = createRobot() ``` @@ -114,7 +114,7 @@ def createRobot(seed: Seed): Robot = { val initialSeed = Seed(13L) ``` -```tut +```tut:book val robot = createRobot(initialSeed) ``` @@ -160,13 +160,13 @@ val createRobot: State[Seed, Robot] = At this point, we have not yet created a robot; we have written instructions for creating a robot. We need to pass in an initial seed value, and then we can call `value` to actually create the robot: -```tut +```tut:book val (finalState, robot) = createRobot.run(initialSeed).value ``` If we only care about the robot and not the final state, then we can use `runA`: -```tut +```tut:book val robot = createRobot.runA(initialSeed).value ``` @@ -187,7 +187,7 @@ val createRobot: State[Seed, Robot] = { } ``` -```tut +```tut:book val robot = createRobot.runA(initialSeed).value ``` diff --git a/docs/src/main/tut/traverse.md b/docs/src/main/tut/traverse.md index 9281a90b21..22373a33e9 100644 --- a/docs/src/main/tut/traverse.md +++ b/docs/src/main/tut/traverse.md @@ -41,7 +41,7 @@ hold true in this case? Given just `User => Future[Profile]`, what should we do if we want to fetch profiles for a `List[User]`? We could try familiar combinators like `map`. -```tut +```tut:book def profilesFor(users: List[User]) = users.map(userInfo) ``` @@ -104,7 +104,7 @@ def parseIntValidated(s: String): ValidatedNel[NumberFormatException, Int] = Examples. -```tut +```tut:book val x1 = List("1", "2", "3").traverseU(parseIntXor) val x2 = List("1", "abc", "3").traverseU(parseIntXor) val x3 = List("1", "abc", "def").traverseU(parseIntXor) @@ -187,7 +187,7 @@ Sometimes you may find yourself with a collection of data, each of which is alre for instance a `List[Option[A]]`. To make this easier to work with, you want a `Option[List[A]]`. Given `Option` has an `Applicative` instance, we can traverse over the list with the identity function. -```tut +```tut:book import cats.std.option._ val l1 = List(Option(1), Option(2), Option(3)).traverse(identity) val l2 = List(Option(1), None, Option(3)).traverse(identity) @@ -195,7 +195,7 @@ val l2 = List(Option(1), None, Option(3)).traverse(identity) `Traverse` provides a convenience method `sequence` that does exactly this. -```tut +```tut:book val l1 = List(Option(1), Option(2), Option(3)).sequence val l2 = List(Option(1), None, Option(3)).sequence ``` diff --git a/docs/src/main/tut/typeclasses.md b/docs/src/main/tut/typeclasses.md index 5084b1c5b2..a555c3df9a 100644 --- a/docs/src/main/tut/typeclasses.md +++ b/docs/src/main/tut/typeclasses.md @@ -38,7 +38,7 @@ implicit val stringShow = new Show[String] { and now our call to Log succeeds -```tut +```tut:book log("a string") ``` @@ -67,7 +67,7 @@ implicit def optionShow[A](implicit sa: Show[A]) = new Show[Option[A]] { Now we can call our log function with a `Option[String]` or a `Option[Option[String]]`: -```tut +```tut:book log(Option(Option("hello"))) ``` diff --git a/docs/src/main/tut/validated.md b/docs/src/main/tut/validated.md index ea26d16b8e..e4ba7ead44 100644 --- a/docs/src/main/tut/validated.md +++ b/docs/src/main/tut/validated.md @@ -162,7 +162,7 @@ implicit val readInt: Read[Int] = Read.intRead Any and all errors are reported! -```tut +```tut:book val v1 = parallelValidate(config.parse[String]("url").toValidatedNel, config.parse[Int]("port").toValidatedNel)(ConnectionParams.apply) @@ -224,7 +224,7 @@ case class Person(name: String, age: Int, address: Address) Thus. -```tut +```tut:book val personFromConfig: ValidatedNel[ConfigError, Person] = Apply[ValidatedNel[ConfigError, ?]].map4(config.parse[String]("name").toValidatedNel, config.parse[Int]("age").toValidatedNel, @@ -271,7 +271,7 @@ trait Monad[F[_]] { However, the `ap` behavior defined in terms of `flatMap` does not behave the same as that of our `ap` defined above. Observe: -```tut +```tut:book val v = validatedMonad.tuple2(Validated.invalidNel[String, Int]("oops"), Validated.invalidNel[String, Double]("uh oh")) ``` @@ -294,7 +294,7 @@ If you do want error accumulation but occasionally run into places where you seq ### `andThen` The `andThen` method is similar to `flatMap` (such as `Xor.flatMap`). In the cause of success, it passes the valid value into a function that returns a new `Validated` instance. -```tut +```tut:book val houseNumber = config.parse[Int]("house_number").andThen{ n => if (n >= 0) Validated.valid(n) else Validated.invalid(ParseError("house_number")) @@ -315,7 +315,7 @@ def positive(field: String, i: Int): ConfigError Xor Int = { Thus. -```tut +```tut:book val houseNumber = config.parse[Int]("house_number").withXor{ xor: ConfigError Xor Int => xor.flatMap{ i => positive("house_number", i) diff --git a/docs/src/main/tut/xor.md b/docs/src/main/tut/xor.md index 3873a8f69f..6944f4d673 100644 --- a/docs/src/main/tut/xor.md +++ b/docs/src/main/tut/xor.md @@ -64,7 +64,7 @@ This can be accomplished by using the `Either#right` method, which returns a `Ri instance. `RightProjection` does have `flatMap` and `map` on it, which acts on the right side and ignores the left - this property is referred to as "right-bias." -```tut +```tut:book val e1: Either[String, Int] = Right(5) e1.right.map(_ + 1) @@ -80,7 +80,7 @@ the right side is most often chosen. This is the primary difference between `Xor `Xor` is right-biased. `Xor` also has some more convenient methods on it, but the most crucial one is the right-biased being built-in. -```tut +```tut:book import cats.data.Xor val xor1: Xor[String, Int] = Xor.right(5) @@ -163,7 +163,7 @@ With the composite function that we actually care about, we can pass in strings match on the exception. Because `Xor` is a sealed type (often referred to as an algebraic data type, or ADT), the compiler will complain if we do not check both the `Left` and `Right` case. -```tut +```tut:book magic("123") match { case Xor.Left(_: NumberFormatException) => println("not a number!") case Xor.Left(_: IllegalArgumentException) => println("can't take reciprocal of 0!") @@ -209,7 +209,7 @@ exception classes as error values, we use one of the enumerated cases. Now when match, we get much nicer matching. Moreover, since `Error` is `sealed`, no outside code can add additional subtypes which we might fail to handle. -```tut +```tut:book import XorStyle._ magic("123") match { @@ -337,7 +337,7 @@ def awesome = There will inevitably come a time when your nice `Xor` code will have to interact with exception-throwing code. Handling such situations is easy enough. -```tut +```tut:book val xor: Xor[NumberFormatException, Int] = try { Xor.right("abc".toInt) @@ -350,21 +350,21 @@ However, this can get tedious quickly. `Xor` provides a `catchOnly` method on it that allows you to pass it a function, along with the type of exception you want to catch, and does the above for you. -```tut +```tut:book val xor: Xor[NumberFormatException, Int] = Xor.catchOnly[NumberFormatException]("abc".toInt) ``` If you want to catch all (non-fatal) throwables, you can use `catchNonFatal`. -```tut +```tut:book val xor: Xor[Throwable, Int] = Xor.catchNonFatal("abc".toInt) ``` ## Additional syntax -```tut +```tut:book import cats.syntax.xor._ val xor3: Xor[String, Int] = 7.right[String] From b7a191944dcef442c032625bf362875d4c25aa8f Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Mon, 23 May 2016 15:11:54 -1000 Subject: [PATCH 167/185] Add Try instances --- core/src/main/scala/cats/std/all.scala | 1 + core/src/main/scala/cats/std/package.scala | 1 + core/src/main/scala/cats/std/try.scala | 105 ++++++++++++++++++ .../src/test/scala/cats/tests/TryTests.scala | 77 +++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 core/src/main/scala/cats/std/try.scala create mode 100644 tests/src/test/scala/cats/tests/TryTests.scala diff --git a/core/src/main/scala/cats/std/all.scala b/core/src/main/scala/cats/std/all.scala index 0060a5f513..27c80e9d90 100644 --- a/core/src/main/scala/cats/std/all.scala +++ b/core/src/main/scala/cats/std/all.scala @@ -15,4 +15,5 @@ trait AllInstances with BigIntInstances with BigDecimalInstances with FutureInstances + with TryInstances with TupleInstances diff --git a/core/src/main/scala/cats/std/package.scala b/core/src/main/scala/cats/std/package.scala index 9b31e87b51..f49a7fdce7 100644 --- a/core/src/main/scala/cats/std/package.scala +++ b/core/src/main/scala/cats/std/package.scala @@ -28,5 +28,6 @@ package object std { object bigInt extends BigIntInstances object bigDecimal extends BigDecimalInstances + object `try` extends TryInstances object tuple extends TupleInstances } diff --git a/core/src/main/scala/cats/std/try.scala b/core/src/main/scala/cats/std/try.scala new file mode 100644 index 0000000000..7b5789d528 --- /dev/null +++ b/core/src/main/scala/cats/std/try.scala @@ -0,0 +1,105 @@ +package cats +package std + +import cats.syntax.all._ +import cats.data.Xor + +import scala.util.control.NonFatal +import scala.util.{Failure, Success, Try} + +trait TryInstances extends TryInstances1 { + + implicit def tryInstance: MonadError[Try, Throwable] with CoflatMap[Try] = + new TryCoflatMap with MonadError[Try, Throwable]{ + def pure[A](x: A): Try[A] = Success(x) + + override def pureEval[A](x: Eval[A]): Try[A] = x match { + case Now(x) => Success(x) + case _ => Try(x.value) + } + + override def map2[A, B, Z](ta: Try[A], tb: Try[B])(f: (A, B) => Z): Try[Z] = + ta.flatMap(a => tb.map(b => f(a, b))) + + override def map2Eval[A, B, Z](ta: Try[A], tb: Eval[Try[B]])(f: (A, B) => Z): Eval[Try[Z]] = + ta match { + case f@Failure(_) => Now(f.asInstanceOf[Try[Z]]) + case Success(a) => tb.map(_.map(f(a, _))) + } + + def flatMap[A, B](ta: Try[A])(f: A => Try[B]): Try[B] = ta.flatMap(f) + + def handleErrorWith[A](ta: Try[A])(f: Throwable => Try[A]): Try[A] = + ta.recoverWith { case t => f(t) } + + def raiseError[A](e: Throwable): Try[A] = Failure(e) + override def handleError[A](ta: Try[A])(f: Throwable => A): Try[A] = + ta.recover { case t => f(t) } + + override def attempt[A](ta: Try[A]): Try[Throwable Xor A] = + (ta map Xor.right) recover { case NonFatal(t) => Xor.left(t) } + + override def recover[A](ta: Try[A])(pf: PartialFunction[Throwable, A]): Try[A] = + ta.recover(pf) + + override def recoverWith[A](ta: Try[A])(pf: PartialFunction[Throwable, Try[A]]): Try[A] = ta.recoverWith(pf) + + override def map[A, B](ta: Try[A])(f: A => B): Try[B] = ta.map(f) + } + + implicit def tryGroup[A: Group]: Group[Try[A]] = + new TryGroup[A] + + implicit def showTry[A](implicit A: Show[A]): Show[Try[A]] = + new Show[Try[A]] { + def show(fa: Try[A]): String = fa match { + case Success(a) => s"Success(${A.show(a)})" + case Failure(e) => s"Failure($e)" + } + } + implicit def eqTry[A](implicit A: Eq[A]): Eq[Try[A]] = + new Eq[Try[A]] { + def eqv(x: Try[A], y: Try[A]): Boolean = (x, y) match { + case (Success(a), Success(b)) => A.eqv(a, b) + case (Failure(_), Failure(_)) => true // all failures are equivalent + case _ => false + } + } +} + +private[std] sealed trait TryInstances1 extends TryInstances2 { + implicit def tryMonoid[A: Monoid]: Monoid[Try[A]] = + new TryMonoid[A] +} + +private[std] sealed trait TryInstances2 { + implicit def trySemigroup[A: Semigroup]: Semigroup[Try[A]] = + new TrySemigroup[A] +} + +private[cats] abstract class TryCoflatMap extends CoflatMap[Try] { + def map[A, B](ta: Try[A])(f: A => B): Try[B] = ta.map(f) + def coflatMap[A, B](ta: Try[A])(f: Try[A] => B): Try[B] = Try(f(ta)) +} + +private[cats] class TrySemigroup[A: Semigroup] extends Semigroup[Try[A]] { + def combine(fx: Try[A], fy: Try[A]): Try[A] = + for { + x <- fx + y <- fy + } yield x |+| y +} + +private[cats] class TryMonoid[A](implicit A: Monoid[A]) extends TrySemigroup[A] with Monoid[Try[A]] { + def empty: Try[A] = Success(A.empty) +} + +private[cats] class TryGroup[A](implicit A: Group[A]) extends TryMonoid[A] with Group[Try[A]] { + def inverse(fx: Try[A]): Try[A] = + fx.map(_.inverse) + override def remove(fx: Try[A], fy: Try[A]): Try[A] = + for { + x <- fx + y <- fy + } yield x |-| y +} diff --git a/tests/src/test/scala/cats/tests/TryTests.scala b/tests/src/test/scala/cats/tests/TryTests.scala new file mode 100644 index 0000000000..0ccc5d4028 --- /dev/null +++ b/tests/src/test/scala/cats/tests/TryTests.scala @@ -0,0 +1,77 @@ +package cats +package tests + +import cats.laws.{ApplicativeLaws, CoflatMapLaws, FlatMapLaws, MonadLaws} +import cats.laws.discipline._ + +class TryTests extends CatsSuite { + checkAll("Try[Int]", CartesianTests[Try].cartesian[Int, Int, Int]) + checkAll("Cartesian[Try]", SerializableTests.serializable(Cartesian[Try])) + + checkAll("Try[Int]", CoflatMapTests[Try].coflatMap[Int, Int, Int]) + checkAll("CoflatMap[Try]", SerializableTests.serializable(CoflatMap[Try])) + + checkAll("Try with Unit", MonadErrorTests[Try, Unit].monadError[Int, Int, Int]) + checkAll("MonadError[Try, Unit]", SerializableTests.serializable(MonadError[Try, Unit])) + + test("show") { + forAll { fs: Try[String] => + fs.show should === (fs.toString) + } + } + + // The following tests check laws which are a different formulation of + // laws that are checked. Since these laws are more or less duplicates of + // existing laws, we don't check them for all types that have the relevant + // instances. + + test("Kleisli associativity") { + forAll { (l: Long, + f: Long => Try[Int], + g: Int => Try[Char], + h: Char => Try[String]) => + val isEq = FlatMapLaws[Try].kleisliAssociativity(f, g, h, l) + isEq.lhs should === (isEq.rhs) + } + } + + test("Cokleisli associativity") { + forAll { (l: Try[Long], + f: Try[Long] => Int, + g: Try[Int] => Char, + h: Try[Char] => String) => + val isEq = CoflatMapLaws[Try].cokleisliAssociativity(f, g, h, l) + isEq.lhs should === (isEq.rhs) + } + } + + test("applicative composition") { + forAll { (fa: Try[Int], + fab: Try[Int => Long], + fbc: Try[Long => Char]) => + val isEq = ApplicativeLaws[Try].applicativeComposition(fa, fab, fbc) + isEq.lhs should === (isEq.rhs) + } + } + + val monadLaws = MonadLaws[Try] + + test("Kleisli left identity") { + forAll { (a: Int, f: Int => Try[Long]) => + val isEq = monadLaws.kleisliLeftIdentity(a, f) + isEq.lhs should === (isEq.rhs) + } + } + + test("Kleisli right identity") { + forAll { (a: Int, f: Int => Try[Long]) => + val isEq = monadLaws.kleisliRightIdentity(a, f) + isEq.lhs should === (isEq.rhs) + } + } + + test("map2Eval is lazy") { + val bomb: Eval[Try[Int]] = Later(sys.error("boom")) + Try(sys.error("boom0")).map2Eval(bomb)(_ + _).value should === (Failure(new Exception)) + } +} From 71c0d70eba472195eca5cf496b4a13c03fbe7fb9 Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Mon, 23 May 2016 15:26:34 -1000 Subject: [PATCH 168/185] add a product implementation --- core/src/main/scala/cats/std/try.scala | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/std/try.scala b/core/src/main/scala/cats/std/try.scala index 7b5789d528..b69154cf1c 100644 --- a/core/src/main/scala/cats/std/try.scala +++ b/core/src/main/scala/cats/std/try.scala @@ -18,12 +18,21 @@ trait TryInstances extends TryInstances1 { case _ => Try(x.value) } - override def map2[A, B, Z](ta: Try[A], tb: Try[B])(f: (A, B) => Z): Try[Z] = - ta.flatMap(a => tb.map(b => f(a, b))) + override def product[A, B](ta: Try[A], tb: Try[B]): Try[(A, B)] = (ta, tb) match { + case (Success(a), Success(b)) => Success((a, b)) + case (f: Failure[_], _) => f.asInstanceOf[Try[(A, B)]] + case (_, f: Failure[_]) => f.asInstanceOf[Try[(A, B)]] + } + + override def map2[A, B, Z](ta: Try[A], tb: Try[B])(f: (A, B) => Z): Try[Z] = (ta, tb) match { + case (Success(a), Success(b)) => Try(f(a, b)) + case (f: Failure[_], _) => f.asInstanceOf[Try[Z]] + case (_, f: Failure[_]) => f.asInstanceOf[Try[Z]] + } override def map2Eval[A, B, Z](ta: Try[A], tb: Eval[Try[B]])(f: (A, B) => Z): Eval[Try[Z]] = ta match { - case f@Failure(_) => Now(f.asInstanceOf[Try[Z]]) + case f: Failure[_] => Now(f.asInstanceOf[Try[Z]]) case Success(a) => tb.map(_.map(f(a, _))) } From b2193de4a0708c4106361bb1a16cd7359995ea71 Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Mon, 23 May 2016 15:31:48 -1000 Subject: [PATCH 169/185] actually test map2Eval laziness --- tests/src/test/scala/cats/tests/TryTests.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/src/test/scala/cats/tests/TryTests.scala b/tests/src/test/scala/cats/tests/TryTests.scala index 0ccc5d4028..46370b4a9c 100644 --- a/tests/src/test/scala/cats/tests/TryTests.scala +++ b/tests/src/test/scala/cats/tests/TryTests.scala @@ -71,7 +71,9 @@ class TryTests extends CatsSuite { } test("map2Eval is lazy") { - val bomb: Eval[Try[Int]] = Later(sys.error("boom")) - Try(sys.error("boom0")).map2Eval(bomb)(_ + _).value should === (Failure(new Exception)) + var evals = 0 + val bomb: Eval[Try[Int]] = Later { evals += 1; Success(1) } + Try(sys.error("boom0")).map2Eval(bomb)(_ + _).value + evals should === (0) } } From 94123926004067a90f5c9aa76524678698519e98 Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Mon, 23 May 2016 16:00:19 -1000 Subject: [PATCH 170/185] fix the tests --- tests/src/test/scala/cats/tests/TryTests.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/src/test/scala/cats/tests/TryTests.scala b/tests/src/test/scala/cats/tests/TryTests.scala index 46370b4a9c..e280f20aaf 100644 --- a/tests/src/test/scala/cats/tests/TryTests.scala +++ b/tests/src/test/scala/cats/tests/TryTests.scala @@ -4,15 +4,19 @@ package tests import cats.laws.{ApplicativeLaws, CoflatMapLaws, FlatMapLaws, MonadLaws} import cats.laws.discipline._ +import scala.util.{Success, Try} + class TryTests extends CatsSuite { + implicit val eqThrow: Eq[Throwable] = Eq.allEqual + checkAll("Try[Int]", CartesianTests[Try].cartesian[Int, Int, Int]) checkAll("Cartesian[Try]", SerializableTests.serializable(Cartesian[Try])) checkAll("Try[Int]", CoflatMapTests[Try].coflatMap[Int, Int, Int]) checkAll("CoflatMap[Try]", SerializableTests.serializable(CoflatMap[Try])) - checkAll("Try with Unit", MonadErrorTests[Try, Unit].monadError[Int, Int, Int]) - checkAll("MonadError[Try, Unit]", SerializableTests.serializable(MonadError[Try, Unit])) + checkAll("Try with Throwable", MonadErrorTests[Try, Throwable].monadError[Int, Int, Int]) + checkAll("MonadError[Try, Throwable]", SerializableTests.serializable(MonadError[Try, Throwable])) test("show") { forAll { fs: Try[String] => @@ -73,7 +77,7 @@ class TryTests extends CatsSuite { test("map2Eval is lazy") { var evals = 0 val bomb: Eval[Try[Int]] = Later { evals += 1; Success(1) } - Try(sys.error("boom0")).map2Eval(bomb)(_ + _).value + Try[Int](sys.error("boom0")).map2Eval(bomb)(_ + _).value evals should === (0) } } From 42cb7bac9a52337fe1346548ee3a4295140193ae Mon Sep 17 00:00:00 2001 From: Michael Pilquist Date: Tue, 24 May 2016 21:42:06 -0400 Subject: [PATCH 171/185] Fixed a bug in the Order and PartialOrder instances for Tuple2+ where only the first element was used in comparisons --- project/KernelBoiler.scala | 4 ++-- tests/src/test/scala/cats/tests/TupleTests.scala | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/project/KernelBoiler.scala b/project/KernelBoiler.scala index 69a46537fe..bb5553ccd8 100644 --- a/project/KernelBoiler.scala +++ b/project/KernelBoiler.scala @@ -147,13 +147,13 @@ object KernelBoiler { - implicit def tuple${arity}Order[${`A..N`}](implicit ${constraints("Order")}): Order[${`(A..N)`}] = - new Order[${`(A..N)`}] { - def compare(x: ${`(A..N)`}, y: ${`(A..N)`}): Int = - - ${binMethod("compare").find(_ != 0).getOrElse(0)} + - ${binMethod("compare").mkString("Array(", ", ", ")")}.find(_ != 0).getOrElse(0) - } - - implicit def tuple${arity}PartialOrder[${`A..N`}](implicit ${constraints("PartialOrder")}): PartialOrder[${`(A..N)`}] = - new PartialOrder[${`(A..N)`}] { - def partialCompare(x: ${`(A..N)`}, y: ${`(A..N)`}): Double = - - ${binMethod("partialCompare").find(_ != 0.0).getOrElse(0.0)} + - ${binMethod("partialCompare").mkString("Array(", ", ", ")")}.find(_ != 0.0).getOrElse(0.0) - } - - implicit def tuple${arity}Semigroup[${`A..N`}](implicit ${constraints("Semigroup")}): Semigroup[${`(A..N)`}] = diff --git a/tests/src/test/scala/cats/tests/TupleTests.scala b/tests/src/test/scala/cats/tests/TupleTests.scala index fdde8f5545..8dab6e68af 100644 --- a/tests/src/test/scala/cats/tests/TupleTests.scala +++ b/tests/src/test/scala/cats/tests/TupleTests.scala @@ -7,6 +7,19 @@ class TupleTests extends CatsSuite { checkAll("Tuple2", BitraverseTests[Tuple2].bitraverse[Option, Int, Int, Int, String, String, String]) checkAll("Bitraverse[Tuple2]", SerializableTests.serializable(Bitraverse[Tuple2])) + test("eqv") { + val eq = Eq[(Int, Long)] + forAll { t: (Int, Long) => eq.eqv(t, t) should === (true) } + forAll { t: (Int, Long) => eq.eqv(t, t._1 -> (t._2 + 1)) should === (false) } + } + + test("order") { + forAll { t: (Int, Int) => + val u = t.swap + Order[(Int, Int)].compare(t, u) should === (scala.math.Ordering[(Int, Int)].compare(t, u)) + } + } + test("show") { (1, 2).show should === ("(1,2)") From 78ff38a34ab6bce165b42675d46811e2d8e36dbc Mon Sep 17 00:00:00 2001 From: Julien Truffaut Date: Thu, 26 May 2016 12:53:54 +0100 Subject: [PATCH 172/185] fix IdT --- core/src/main/scala/cats/data/IdT.scala | 2 +- tests/src/test/scala/cats/tests/IdTTests.scala | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/data/IdT.scala b/core/src/main/scala/cats/data/IdT.scala index bed9c19440..97cb261960 100644 --- a/core/src/main/scala/cats/data/IdT.scala +++ b/core/src/main/scala/cats/data/IdT.scala @@ -25,7 +25,7 @@ final case class IdT[F[_], A](value: F[A]) { G.map(F.traverse(value)(f))(IdT(_)) def ap[B](f: IdT[F, A => B])(implicit F: Apply[F]): IdT[F, B] = - IdT(F.ap(value)(f.value)) + IdT(F.ap(f.value)(value)) } diff --git a/tests/src/test/scala/cats/tests/IdTTests.scala b/tests/src/test/scala/cats/tests/IdTTests.scala index 393bf66f15..a748178d9f 100644 --- a/tests/src/test/scala/cats/tests/IdTTests.scala +++ b/tests/src/test/scala/cats/tests/IdTTests.scala @@ -2,11 +2,13 @@ package cats.tests import cats.{Foldable, Functor, Monad, Traverse} import cats.data.IdT -import cats.laws.discipline.{FoldableTests, FunctorTests, MonadTests, SerializableTests, TraverseTests} +import cats.laws.discipline.{CartesianTests, FoldableTests, FunctorTests, MonadTests, SerializableTests, TraverseTests} import cats.laws.discipline.arbitrary._ class IdTTests extends CatsSuite { + implicit val iso = CartesianTests.Isomorphisms.invariant[IdT[List, ?]] + checkAll("IdT[Functor, Int]", FunctorTests[IdT[List, ?]].functor[Int, Int, Int]) checkAll("Functor[IdT[List, ?]]", SerializableTests.serializable(Functor[IdT[List, ?]])) From 241140ee930ae5d416fe17846674efe799e8224c Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Thu, 26 May 2016 10:31:05 -1000 Subject: [PATCH 173/185] update, remove bad groups --- core/src/main/scala/cats/std/future.scala | 10 ---------- core/src/main/scala/cats/std/package.scala | 2 +- core/src/main/scala/cats/std/try.scala | 22 +++++++--------------- 3 files changed, 8 insertions(+), 26 deletions(-) diff --git a/core/src/main/scala/cats/std/future.scala b/core/src/main/scala/cats/std/future.scala index e1453870d6..c18f7a0fff 100644 --- a/core/src/main/scala/cats/std/future.scala +++ b/core/src/main/scala/cats/std/future.scala @@ -34,9 +34,6 @@ trait FutureInstances extends FutureInstances1 { override def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f) } - - implicit def futureGroup[A: Group](implicit ec: ExecutionContext): Group[Future[A]] = - new FutureGroup[A] } private[std] sealed trait FutureInstances1 extends FutureInstances2 { @@ -63,10 +60,3 @@ private[cats] class FutureMonoid[A](implicit A: Monoid[A], ec: ExecutionContext) def empty: Future[A] = Future.successful(A.empty) } - -private[cats] class FutureGroup[A](implicit A: Group[A], ec: ExecutionContext) extends FutureMonoid[A] with Group[Future[A]] { - def inverse(fx: Future[A]): Future[A] = - fx.map(_.inverse) - override def remove(fx: Future[A], fy: Future[A]): Future[A] = - (fx zip fy).map { case (x, y) => x |-| y } -} diff --git a/core/src/main/scala/cats/std/package.scala b/core/src/main/scala/cats/std/package.scala index f49a7fdce7..2bc6a91fca 100644 --- a/core/src/main/scala/cats/std/package.scala +++ b/core/src/main/scala/cats/std/package.scala @@ -28,6 +28,6 @@ package object std { object bigInt extends BigIntInstances object bigDecimal extends BigDecimalInstances - object `try` extends TryInstances + object try_ extends TryInstances object tuple extends TupleInstances } diff --git a/core/src/main/scala/cats/std/try.scala b/core/src/main/scala/cats/std/try.scala index b69154cf1c..b8871e881d 100644 --- a/core/src/main/scala/cats/std/try.scala +++ b/core/src/main/scala/cats/std/try.scala @@ -56,9 +56,6 @@ trait TryInstances extends TryInstances1 { override def map[A, B](ta: Try[A])(f: A => B): Try[B] = ta.map(f) } - implicit def tryGroup[A: Group]: Group[Try[A]] = - new TryGroup[A] - implicit def showTry[A](implicit A: Show[A]): Show[Try[A]] = new Show[Try[A]] { def show(fa: Try[A]): String = fa match { @@ -66,11 +63,16 @@ trait TryInstances extends TryInstances1 { case Failure(e) => s"Failure($e)" } } - implicit def eqTry[A](implicit A: Eq[A]): Eq[Try[A]] = + /** + * you may with to do equality by making `implicit val eqT: Eq[Throwable] = Eq.allEqual` + * doing a fine grained equality on Throwable can make the code very execution + * order dependent + */ + implicit def eqTry[A, T](implicit A: Eq[A], T: Eq[Throwable]): Eq[Try[A]] = new Eq[Try[A]] { def eqv(x: Try[A], y: Try[A]): Boolean = (x, y) match { case (Success(a), Success(b)) => A.eqv(a, b) - case (Failure(_), Failure(_)) => true // all failures are equivalent + case (Failure(a), Failure(b)) => T.eqv(a, b) case _ => false } } @@ -102,13 +104,3 @@ private[cats] class TrySemigroup[A: Semigroup] extends Semigroup[Try[A]] { private[cats] class TryMonoid[A](implicit A: Monoid[A]) extends TrySemigroup[A] with Monoid[Try[A]] { def empty: Try[A] = Success(A.empty) } - -private[cats] class TryGroup[A](implicit A: Group[A]) extends TryMonoid[A] with Group[Try[A]] { - def inverse(fx: Try[A]): Try[A] = - fx.map(_.inverse) - override def remove(fx: Try[A], fy: Try[A]): Try[A] = - for { - x <- fx - y <- fy - } yield x |-| y -} From 75b53e4061035553a291376d119322270797c487 Mon Sep 17 00:00:00 2001 From: Oscar Boykin Date: Thu, 26 May 2016 15:23:12 -1000 Subject: [PATCH 174/185] fix typo --- core/src/main/scala/cats/std/try.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/std/try.scala b/core/src/main/scala/cats/std/try.scala index b8871e881d..a42b015718 100644 --- a/core/src/main/scala/cats/std/try.scala +++ b/core/src/main/scala/cats/std/try.scala @@ -64,7 +64,7 @@ trait TryInstances extends TryInstances1 { } } /** - * you may with to do equality by making `implicit val eqT: Eq[Throwable] = Eq.allEqual` + * you may wish to do equality by making `implicit val eqT: Eq[Throwable] = Eq.allEqual` * doing a fine grained equality on Throwable can make the code very execution * order dependent */ From 65a506c2931b0fe7d79b69e6dfde7cf826a0b126 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 27 May 2016 09:39:57 -0400 Subject: [PATCH 175/185] renamed implicit vals in std --- core/src/main/scala/cats/std/anyval.scala | 18 +++++++++--------- core/src/main/scala/cats/std/bigDecimal.scala | 2 +- core/src/main/scala/cats/std/bigInt.scala | 2 +- core/src/main/scala/cats/std/either.scala | 12 ++++++------ core/src/main/scala/cats/std/function.scala | 16 ++++++++-------- core/src/main/scala/cats/std/future.scala | 8 ++++---- core/src/main/scala/cats/std/list.scala | 4 ++-- core/src/main/scala/cats/std/map.scala | 4 ++-- core/src/main/scala/cats/std/option.scala | 4 ++-- core/src/main/scala/cats/std/set.scala | 4 ++-- core/src/main/scala/cats/std/stream.scala | 4 ++-- core/src/main/scala/cats/std/string.scala | 2 +- core/src/main/scala/cats/std/tuple.scala | 4 ++-- core/src/main/scala/cats/std/vector.scala | 4 ++-- 14 files changed, 44 insertions(+), 44 deletions(-) diff --git a/core/src/main/scala/cats/std/anyval.scala b/core/src/main/scala/cats/std/anyval.scala index c9d1109a18..5bd6b357ad 100644 --- a/core/src/main/scala/cats/std/anyval.scala +++ b/core/src/main/scala/cats/std/anyval.scala @@ -14,37 +14,37 @@ trait AnyValInstances with TupleInstances trait IntInstances extends cats.kernel.std.IntInstances { - implicit val intShow: Show[Int] = Show.fromToString[Int] + implicit val catsShowForcatsForInt: Show[Int] = Show.fromToString[Int] } trait ByteInstances extends cats.kernel.std.ByteInstances { - implicit val byteShow: Show[Byte] = Show.fromToString[Byte] + implicit val catsShowForByte: Show[Byte] = Show.fromToString[Byte] } trait CharInstances extends cats.kernel.std.CharInstances { - implicit val charShow: Show[Char] = Show.fromToString[Char] + implicit val catsShowForChar: Show[Char] = Show.fromToString[Char] } trait ShortInstances extends cats.kernel.std.ShortInstances { - implicit val shortShow: Show[Short] = Show.fromToString[Short] + implicit val catsShowForShort: Show[Short] = Show.fromToString[Short] } trait LongInstances extends cats.kernel.std.LongInstances { - implicit val longShow: Show[Long] = Show.fromToString[Long] + implicit val catsShowForLong: Show[Long] = Show.fromToString[Long] } trait FloatInstances extends cats.kernel.std.FloatInstances { - implicit val floatShow: Show[Float] = Show.fromToString[Float] + implicit val catsShowForFloat: Show[Float] = Show.fromToString[Float] } trait DoubleInstances extends cats.kernel.std.DoubleInstances { - implicit val doubleShow: Show[Double] = Show.fromToString[Double] + implicit val catsShowForDouble: Show[Double] = Show.fromToString[Double] } trait BooleanInstances extends cats.kernel.std.BooleanInstances { - implicit val booleanShow: Show[Boolean] = Show.fromToString[Boolean] + implicit val catsShowForBoolean: Show[Boolean] = Show.fromToString[Boolean] } trait UnitInstances extends cats.kernel.std.UnitInstances { - implicit val unitShow: Show[Unit] = Show.fromToString[Unit] + implicit val catsShowForUnit: Show[Unit] = Show.fromToString[Unit] } diff --git a/core/src/main/scala/cats/std/bigDecimal.scala b/core/src/main/scala/cats/std/bigDecimal.scala index f3aeac15c3..b61b82798c 100644 --- a/core/src/main/scala/cats/std/bigDecimal.scala +++ b/core/src/main/scala/cats/std/bigDecimal.scala @@ -2,6 +2,6 @@ package cats package std trait BigDecimalInstances { - implicit val bigDecimalShow: Show[BigDecimal] = + implicit val catsShowForBigDecimal: Show[BigDecimal] = Show.fromToString[BigDecimal] } diff --git a/core/src/main/scala/cats/std/bigInt.scala b/core/src/main/scala/cats/std/bigInt.scala index 0c663f734e..4af909c3aa 100644 --- a/core/src/main/scala/cats/std/bigInt.scala +++ b/core/src/main/scala/cats/std/bigInt.scala @@ -2,6 +2,6 @@ package cats package std trait BigIntInstances extends cats.kernel.std.BigIntInstances { - implicit val bigIntShow: Show[BigInt] = + implicit val catsShowForBigInt: Show[BigInt] = Show.fromToString[BigInt] } diff --git a/core/src/main/scala/cats/std/either.scala b/core/src/main/scala/cats/std/either.scala index 567ee29aec..054df7fb62 100644 --- a/core/src/main/scala/cats/std/either.scala +++ b/core/src/main/scala/cats/std/either.scala @@ -2,7 +2,7 @@ package cats package std trait EitherInstances extends EitherInstances1 { - implicit val eitherBitraverse: Bitraverse[Either] = + implicit val catsBitraverseForEither: Bitraverse[Either] = new Bitraverse[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 { @@ -23,7 +23,7 @@ trait EitherInstances extends EitherInstances1 { } } - implicit def eitherInstances[A]: Monad[Either[A, ?]] with Traverse[Either[A, ?]] = + implicit def catsMonadForEither[A]: Monad[Either[A, ?]] with Traverse[Either[A, ?]] = new Monad[Either[A, ?]] with Traverse[Either[A, ?]] { def pure[B](b: B): Either[A, B] = Right(b) @@ -55,14 +55,14 @@ trait EitherInstances extends EitherInstances1 { fa.fold(_ => lc, b => f(b, lc)) } - implicit def eitherOrder[A, B](implicit A: Order[A], B: Order[B]): Order[Either[A, B]] = new Order[Either[A, B]] { + implicit def catsOrderForEither[A, B](implicit A: Order[A], B: Order[B]): Order[Either[A, B]] = new Order[Either[A, B]] { def compare(x: Either[A, B], y: Either[A, B]): Int = x.fold( a => y.fold(A.compare(a, _), _ => -1), b => y.fold(_ => 1, B.compare(b, _)) ) } - implicit def eitherShow[A, B](implicit A: Show[A], B: Show[B]): Show[Either[A, B]] = + implicit def catsShowForEither[A, B](implicit A: Show[A], B: Show[B]): Show[Either[A, B]] = new Show[Either[A, B]] { def show(f: Either[A, B]): String = f.fold( a => s"Left(${A.show(a)})", @@ -72,7 +72,7 @@ trait EitherInstances extends EitherInstances1 { } private[std] sealed trait EitherInstances1 extends EitherInstances2 { - implicit def eitherPartialOrder[A, B](implicit A: PartialOrder[A], B: PartialOrder[B]): PartialOrder[Either[A, B]] = + implicit def catsPartialOrderForEither[A, B](implicit A: PartialOrder[A], B: PartialOrder[B]): PartialOrder[Either[A, B]] = new PartialOrder[Either[A, B]] { def partialCompare(x: Either[A, B], y: Either[A, B]): Double = x.fold( a => y.fold(A.partialCompare(a, _), _ => -1), @@ -82,7 +82,7 @@ private[std] sealed trait EitherInstances1 extends EitherInstances2 { } private[std] sealed trait EitherInstances2 { - implicit def eitherEq[A, B](implicit A: Eq[A], B: Eq[B]): Eq[Either[A, B]] = new Eq[Either[A, B]] { + implicit def catsEqForEither[A, B](implicit A: Eq[A], B: Eq[B]): Eq[Either[A, B]] = new Eq[Either[A, B]] { def eqv(x: Either[A, B], y: Either[A, B]): Boolean = x.fold( a => y.fold(A.eqv(a, _), _ => false), b => y.fold(_ => false, B.eqv(b, _)) diff --git a/core/src/main/scala/cats/std/function.scala b/core/src/main/scala/cats/std/function.scala index f19d20a816..2face19c59 100644 --- a/core/src/main/scala/cats/std/function.scala +++ b/core/src/main/scala/cats/std/function.scala @@ -7,7 +7,7 @@ import cats.functor.Contravariant private[std] sealed trait Function0Instances { - implicit val function0Instance: Bimonad[Function0] = + implicit val catsBimonadForFunction0: Bimonad[Function0] = new Bimonad[Function0] { def extract[A](x: () => A): A = x() @@ -27,13 +27,13 @@ private[std] sealed trait Function0Instances { } private[std] sealed trait Function1Instances extends Function1Instances0 { - implicit def function1Contravariant[R]: Contravariant[? => R] = + implicit def catsContravariantForFunction1[R]: Contravariant[? => R] = new Contravariant[? => R] { def contramap[T1, T0](fa: T1 => R)(f: T0 => T1): T0 => R = fa.compose(f) } - implicit def function1Covariant[T1]: MonadReader[T1 => ?, T1] = + implicit def catsMonadReaderForFunction1[T1]: MonadReader[T1 => ?, T1] = new MonadReader[T1 => ?, T1] { def pure[R](r: R): T1 => R = _ => r @@ -48,7 +48,7 @@ private[std] sealed trait Function1Instances extends Function1Instances0 { f.compose(fa) } - implicit val function1Instance: Choice[Function1] with Arrow[Function1] = + implicit val catsChoiceForFunction1: Choice[Function1] with Arrow[Function1] = new Choice[Function1] with Arrow[Function1] { def choice[A, B, C](f: A => C, g: B => C): Xor[A, B] => C = _ match { @@ -71,18 +71,18 @@ private[std] sealed trait Function1Instances extends Function1Instances0 { def compose[A, B, C](f: B => C, g: A => B): A => C = f.compose(g) } - implicit def function1Monoid[A,B](implicit M: Monoid[B]): Monoid[A => B] = + implicit def catsMonoidForFunction1[A,B](implicit M: Monoid[B]): Monoid[A => B] = new Function1Monoid[A, B] { def B: Monoid[B] = M } - implicit val function1MonoidK: MonoidK[Lambda[A => A => A]] = + implicit val catsMonoidKForFunction1: MonoidK[Lambda[A => A => A]] = new Function1MonoidK {} } private[std] sealed trait Function1Instances0 { - implicit def function1Semigroup[A,B](implicit S: Semigroup[B]): Semigroup[A => B] = + implicit def catsSemigroupForFunction1[A,B](implicit S: Semigroup[B]): Semigroup[A => B] = new Function1Semigroup[A, B] { def B: Semigroup[B] = S } - implicit val function1SemigroupK: SemigroupK[Lambda[A => A => A]] = + implicit val catsSemigroupKForFunction1: SemigroupK[Lambda[A => A => A]] = new Function1SemigroupK {} } diff --git a/core/src/main/scala/cats/std/future.scala b/core/src/main/scala/cats/std/future.scala index e1453870d6..f9b255a4ff 100644 --- a/core/src/main/scala/cats/std/future.scala +++ b/core/src/main/scala/cats/std/future.scala @@ -9,7 +9,7 @@ import scala.concurrent.{ExecutionContext, Future} trait FutureInstances extends FutureInstances1 { - implicit def futureInstance(implicit ec: ExecutionContext): MonadError[Future, Throwable] with CoflatMap[Future] = + implicit def catsMonadErrorCoFlatMapForFuture(implicit ec: ExecutionContext): MonadError[Future, Throwable] with CoflatMap[Future] = new FutureCoflatMap with MonadError[Future, Throwable]{ def pure[A](x: A): Future[A] = Future.successful(x) @@ -35,17 +35,17 @@ trait FutureInstances extends FutureInstances1 { override def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f) } - implicit def futureGroup[A: Group](implicit ec: ExecutionContext): Group[Future[A]] = + implicit def catsGroupForFuture[A: Group](implicit ec: ExecutionContext): Group[Future[A]] = new FutureGroup[A] } private[std] sealed trait FutureInstances1 extends FutureInstances2 { - implicit def futureMonoid[A: Monoid](implicit ec: ExecutionContext): Monoid[Future[A]] = + implicit def catsMonoidForFuture[A: Monoid](implicit ec: ExecutionContext): Monoid[Future[A]] = new FutureMonoid[A] } private[std] sealed trait FutureInstances2 { - implicit def futureSemigroup[A: Semigroup](implicit ec: ExecutionContext): Semigroup[Future[A]] = + implicit def catsSemigroupForFuture[A: Semigroup](implicit ec: ExecutionContext): Semigroup[Future[A]] = new FutureSemigroup[A] } diff --git a/core/src/main/scala/cats/std/list.scala b/core/src/main/scala/cats/std/list.scala index 85a5231740..885a93e4fe 100644 --- a/core/src/main/scala/cats/std/list.scala +++ b/core/src/main/scala/cats/std/list.scala @@ -8,7 +8,7 @@ import scala.collection.mutable.ListBuffer trait ListInstances extends cats.kernel.std.ListInstances { - implicit val listInstance: Traverse[List] with MonadCombine[List] with CoflatMap[List] = + implicit val catsTraverseMonadCombineCoflatMapForList: Traverse[List] with MonadCombine[List] with CoflatMap[List] = new Traverse[List] with MonadCombine[List] with CoflatMap[List] { def empty[A]: List[A] = Nil @@ -61,7 +61,7 @@ trait ListInstances extends cats.kernel.std.ListInstances { override def isEmpty[A](fa: List[A]): Boolean = fa.isEmpty } - implicit def listShow[A:Show]: Show[List[A]] = + implicit def catsShowForList[A:Show]: Show[List[A]] = new Show[List[A]] { def show(fa: List[A]): String = fa.map(_.show).mkString("List(", ", ", ")") } diff --git a/core/src/main/scala/cats/std/map.scala b/core/src/main/scala/cats/std/map.scala index 3b6d4d4a2b..00a4b4323e 100644 --- a/core/src/main/scala/cats/std/map.scala +++ b/core/src/main/scala/cats/std/map.scala @@ -3,7 +3,7 @@ package std trait MapInstances extends cats.kernel.std.MapInstances { - implicit def MapShow[A, B](implicit showA: Show[A], showB: Show[B]): Show[Map[A, B]] = + implicit def catsShowForMap[A, B](implicit showA: Show[A], showB: Show[B]): Show[Map[A, B]] = Show.show[Map[A, B]] { m => val body = m.map { case (a, b) => s"${showA.show(a)} -> ${showB.show(b)})" @@ -11,7 +11,7 @@ trait MapInstances extends cats.kernel.std.MapInstances { s"Map($body)" } - implicit def mapInstance[K]: Traverse[Map[K, ?]] with FlatMap[Map[K, ?]] = + implicit def catsTraverseFlatMapForMap[K]: Traverse[Map[K, ?]] with FlatMap[Map[K, ?]] = new Traverse[Map[K, ?]] with FlatMap[Map[K, ?]] { def traverse[G[_], A, B](fa: Map[K, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[Map[K, B]] = { diff --git a/core/src/main/scala/cats/std/option.scala b/core/src/main/scala/cats/std/option.scala index 7ffef7c4bb..c6b4e435c3 100644 --- a/core/src/main/scala/cats/std/option.scala +++ b/core/src/main/scala/cats/std/option.scala @@ -3,7 +3,7 @@ package std trait OptionInstances extends cats.kernel.std.OptionInstances { - implicit val optionInstance: Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] = + implicit val catsTraverseMonadErrorMonadCombineCoflatMapAlternativeForOption: Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] = new Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] { def empty[A]: Option[A] = None @@ -62,7 +62,7 @@ trait OptionInstances extends cats.kernel.std.OptionInstances { fa.isEmpty } - implicit def showOption[A](implicit A: Show[A]): Show[Option[A]] = + implicit def catsShowForOption[A](implicit A: Show[A]): Show[Option[A]] = new Show[Option[A]] { def show(fa: Option[A]): String = fa match { case Some(a) => s"Some(${A.show(a)})" diff --git a/core/src/main/scala/cats/std/set.scala b/core/src/main/scala/cats/std/set.scala index 7ac35ef22e..79dd1e8d9f 100644 --- a/core/src/main/scala/cats/std/set.scala +++ b/core/src/main/scala/cats/std/set.scala @@ -5,7 +5,7 @@ import cats.syntax.show._ trait SetInstances extends cats.kernel.std.SetInstances { - implicit val setInstance: Foldable[Set] with MonoidK[Set] = + implicit val catsFoldableMonoidKForSet: Foldable[Set] with MonoidK[Set] = new Foldable[Set] with MonoidK[Set] { def empty[A]: Set[A] = Set.empty[A] @@ -27,7 +27,7 @@ trait SetInstances extends cats.kernel.std.SetInstances { override def isEmpty[A](fa: Set[A]): Boolean = fa.isEmpty } - implicit def setShow[A:Show]: Show[Set[A]] = new Show[Set[A]] { + implicit def catsShowForSet[A:Show]: Show[Set[A]] = new Show[Set[A]] { def show(fa: Set[A]): String = fa.toIterator.map(_.show).mkString("Set(", ", ", ")") } diff --git a/core/src/main/scala/cats/std/stream.scala b/core/src/main/scala/cats/std/stream.scala index da26f238ba..3f694a7d3e 100644 --- a/core/src/main/scala/cats/std/stream.scala +++ b/core/src/main/scala/cats/std/stream.scala @@ -4,7 +4,7 @@ package std import cats.syntax.show._ trait StreamInstances extends cats.kernel.std.StreamInstances { - implicit val streamInstance: Traverse[Stream] with MonadCombine[Stream] with CoflatMap[Stream] = + implicit val catsTraverseMonadCombineCoflatMapForStream: Traverse[Stream] with MonadCombine[Stream] with CoflatMap[Stream] = new Traverse[Stream] with MonadCombine[Stream] with CoflatMap[Stream] { def empty[A]: Stream[A] = Stream.Empty @@ -55,7 +55,7 @@ trait StreamInstances extends cats.kernel.std.StreamInstances { override def isEmpty[A](fa: Stream[A]): Boolean = fa.isEmpty } - implicit def streamShow[A: Show]: Show[Stream[A]] = + implicit def catsShowForStream[A: Show]: Show[Stream[A]] = new Show[Stream[A]] { def show(fa: Stream[A]): String = if(fa.isEmpty) "Stream()" else s"Stream(${fa.head.show}, ?)" } diff --git a/core/src/main/scala/cats/std/string.scala b/core/src/main/scala/cats/std/string.scala index ca96850460..c947774ccf 100644 --- a/core/src/main/scala/cats/std/string.scala +++ b/core/src/main/scala/cats/std/string.scala @@ -2,6 +2,6 @@ package cats package std trait StringInstances extends cats.kernel.std.StringInstances { - implicit val stringShow: Show[String] = + implicit val catsShowForString: Show[String] = Show.fromToString[String] } diff --git a/core/src/main/scala/cats/std/tuple.scala b/core/src/main/scala/cats/std/tuple.scala index a79d702948..2314f7b709 100644 --- a/core/src/main/scala/cats/std/tuple.scala +++ b/core/src/main/scala/cats/std/tuple.scala @@ -4,7 +4,7 @@ package std trait TupleInstances extends Tuple2Instances with cats.kernel.std.TupleInstances sealed trait Tuple2Instances { - implicit val tuple2Bitraverse: Bitraverse[Tuple2] = + implicit val catsBitraverseForTuple2: Bitraverse[Tuple2] = new Bitraverse[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)) @@ -16,7 +16,7 @@ sealed trait Tuple2Instances { g(fab._2, f(fab._1, c)) } - implicit def tuple2Show[A, B](implicit aShow: Show[A], bShow: Show[B]): Show[(A, B)] = new Show[(A, B)] { + implicit def catsShowForTuple2[A, B](implicit aShow: Show[A], bShow: Show[B]): Show[(A, B)] = new Show[(A, B)] { override def show(f: (A, B)): String = { s"(${aShow.show(f._1)},${bShow.show(f._2)})" } diff --git a/core/src/main/scala/cats/std/vector.scala b/core/src/main/scala/cats/std/vector.scala index fd6591127a..f67b3c8edc 100644 --- a/core/src/main/scala/cats/std/vector.scala +++ b/core/src/main/scala/cats/std/vector.scala @@ -8,7 +8,7 @@ import scala.collection.+: import scala.collection.immutable.VectorBuilder trait VectorInstances extends cats.kernel.std.VectorInstances { - implicit val vectorInstance: Traverse[Vector] with MonadCombine[Vector] with CoflatMap[Vector] = + implicit val catsTraverseMonadCombineCoflatMapForVector: Traverse[Vector] with MonadCombine[Vector] with CoflatMap[Vector] = new Traverse[Vector] with MonadCombine[Vector] with CoflatMap[Vector] { def empty[A]: Vector[A] = Vector.empty[A] @@ -55,7 +55,7 @@ trait VectorInstances extends cats.kernel.std.VectorInstances { override def isEmpty[A](fa: Vector[A]): Boolean = fa.isEmpty } - implicit def vectorShow[A:Show]: Show[Vector[A]] = + implicit def catsShowForVector[A:Show]: Show[Vector[A]] = new Show[Vector[A]] { def show(fa: Vector[A]): String = fa.map(_.show).mkString("Vector(", ", ", ")") } From 67a846e94666a68560ad0814229cc602b0522658 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 27 May 2016 09:48:24 -0400 Subject: [PATCH 176/185] fixed typo --- core/src/main/scala/cats/std/anyval.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/std/anyval.scala b/core/src/main/scala/cats/std/anyval.scala index 5bd6b357ad..404e399249 100644 --- a/core/src/main/scala/cats/std/anyval.scala +++ b/core/src/main/scala/cats/std/anyval.scala @@ -14,7 +14,7 @@ trait AnyValInstances with TupleInstances trait IntInstances extends cats.kernel.std.IntInstances { - implicit val catsShowForcatsForInt: Show[Int] = Show.fromToString[Int] + implicit val catsShowForInt: Show[Int] = Show.fromToString[Int] } trait ByteInstances extends cats.kernel.std.ByteInstances { From 4b3d6f389270132804ca843ad9da4d543fcb0a6d Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 27 May 2016 09:57:25 -0400 Subject: [PATCH 177/185] added some missing typecalsses back to implicit val names --- core/src/main/scala/cats/std/either.scala | 2 +- core/src/main/scala/cats/std/function.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/std/either.scala b/core/src/main/scala/cats/std/either.scala index 054df7fb62..9fde66c71a 100644 --- a/core/src/main/scala/cats/std/either.scala +++ b/core/src/main/scala/cats/std/either.scala @@ -23,7 +23,7 @@ trait EitherInstances extends EitherInstances1 { } } - implicit def catsMonadForEither[A]: Monad[Either[A, ?]] with Traverse[Either[A, ?]] = + implicit def catsMonadTraverseForEither[A]: Monad[Either[A, ?]] with Traverse[Either[A, ?]] = new Monad[Either[A, ?]] with Traverse[Either[A, ?]] { def pure[B](b: B): Either[A, B] = Right(b) diff --git a/core/src/main/scala/cats/std/function.scala b/core/src/main/scala/cats/std/function.scala index 2face19c59..fae6aa4df5 100644 --- a/core/src/main/scala/cats/std/function.scala +++ b/core/src/main/scala/cats/std/function.scala @@ -48,7 +48,7 @@ private[std] sealed trait Function1Instances extends Function1Instances0 { f.compose(fa) } - implicit val catsChoiceForFunction1: Choice[Function1] with Arrow[Function1] = + implicit val catsChoiceArrowForFunction1: Choice[Function1] with Arrow[Function1] = new Choice[Function1] with Arrow[Function1] { def choice[A, B, C](f: A => C, g: B => C): Xor[A, B] => C = _ match { From 495bff7d0b908d51325761f6f496d92c5d66e901 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 27 May 2016 11:47:36 -0400 Subject: [PATCH 178/185] use full package name for implicit val def names --- core/src/main/scala/cats/std/anyval.scala | 18 +++++++++--------- core/src/main/scala/cats/std/bigDecimal.scala | 2 +- core/src/main/scala/cats/std/bigInt.scala | 2 +- core/src/main/scala/cats/std/either.scala | 12 ++++++------ core/src/main/scala/cats/std/function.scala | 18 +++++++++--------- core/src/main/scala/cats/std/future.scala | 8 ++++---- core/src/main/scala/cats/std/list.scala | 4 ++-- core/src/main/scala/cats/std/map.scala | 4 ++-- core/src/main/scala/cats/std/option.scala | 4 ++-- core/src/main/scala/cats/std/set.scala | 4 ++-- core/src/main/scala/cats/std/stream.scala | 4 ++-- core/src/main/scala/cats/std/string.scala | 2 +- core/src/main/scala/cats/std/tuple.scala | 4 ++-- core/src/main/scala/cats/std/vector.scala | 4 ++-- free/src/main/scala/cats/free/Trampoline.scala | 2 +- .../test/scala/cats/tests/EitherTests.scala | 4 ++-- .../test/scala/cats/tests/FunctionTests.scala | 6 +++--- tests/src/test/scala/cats/tests/SetTests.scala | 3 ++- 18 files changed, 53 insertions(+), 52 deletions(-) diff --git a/core/src/main/scala/cats/std/anyval.scala b/core/src/main/scala/cats/std/anyval.scala index 404e399249..98a09b7f87 100644 --- a/core/src/main/scala/cats/std/anyval.scala +++ b/core/src/main/scala/cats/std/anyval.scala @@ -14,37 +14,37 @@ trait AnyValInstances with TupleInstances trait IntInstances extends cats.kernel.std.IntInstances { - implicit val catsShowForInt: Show[Int] = Show.fromToString[Int] + implicit val catsStdShowForInt: Show[Int] = Show.fromToString[Int] } trait ByteInstances extends cats.kernel.std.ByteInstances { - implicit val catsShowForByte: Show[Byte] = Show.fromToString[Byte] + implicit val catsStdShowForByte: Show[Byte] = Show.fromToString[Byte] } trait CharInstances extends cats.kernel.std.CharInstances { - implicit val catsShowForChar: Show[Char] = Show.fromToString[Char] + implicit val catsStdShowForChar: Show[Char] = Show.fromToString[Char] } trait ShortInstances extends cats.kernel.std.ShortInstances { - implicit val catsShowForShort: Show[Short] = Show.fromToString[Short] + implicit val catsStdShowForShort: Show[Short] = Show.fromToString[Short] } trait LongInstances extends cats.kernel.std.LongInstances { - implicit val catsShowForLong: Show[Long] = Show.fromToString[Long] + implicit val catsStdShowForLong: Show[Long] = Show.fromToString[Long] } trait FloatInstances extends cats.kernel.std.FloatInstances { - implicit val catsShowForFloat: Show[Float] = Show.fromToString[Float] + implicit val catsStdShowForFloat: Show[Float] = Show.fromToString[Float] } trait DoubleInstances extends cats.kernel.std.DoubleInstances { - implicit val catsShowForDouble: Show[Double] = Show.fromToString[Double] + implicit val catsStdShowForDouble: Show[Double] = Show.fromToString[Double] } trait BooleanInstances extends cats.kernel.std.BooleanInstances { - implicit val catsShowForBoolean: Show[Boolean] = Show.fromToString[Boolean] + implicit val catsStdShowForBoolean: Show[Boolean] = Show.fromToString[Boolean] } trait UnitInstances extends cats.kernel.std.UnitInstances { - implicit val catsShowForUnit: Show[Unit] = Show.fromToString[Unit] + implicit val catsStdShowForUnit: Show[Unit] = Show.fromToString[Unit] } diff --git a/core/src/main/scala/cats/std/bigDecimal.scala b/core/src/main/scala/cats/std/bigDecimal.scala index b61b82798c..572bcf24ee 100644 --- a/core/src/main/scala/cats/std/bigDecimal.scala +++ b/core/src/main/scala/cats/std/bigDecimal.scala @@ -2,6 +2,6 @@ package cats package std trait BigDecimalInstances { - implicit val catsShowForBigDecimal: Show[BigDecimal] = + implicit val catsStdShowForBigDecimal: Show[BigDecimal] = Show.fromToString[BigDecimal] } diff --git a/core/src/main/scala/cats/std/bigInt.scala b/core/src/main/scala/cats/std/bigInt.scala index 4af909c3aa..03d680eec9 100644 --- a/core/src/main/scala/cats/std/bigInt.scala +++ b/core/src/main/scala/cats/std/bigInt.scala @@ -2,6 +2,6 @@ package cats package std trait BigIntInstances extends cats.kernel.std.BigIntInstances { - implicit val catsShowForBigInt: Show[BigInt] = + implicit val catsStdShowForBigInt: Show[BigInt] = Show.fromToString[BigInt] } diff --git a/core/src/main/scala/cats/std/either.scala b/core/src/main/scala/cats/std/either.scala index 9fde66c71a..43962597bf 100644 --- a/core/src/main/scala/cats/std/either.scala +++ b/core/src/main/scala/cats/std/either.scala @@ -2,7 +2,7 @@ package cats package std trait EitherInstances extends EitherInstances1 { - implicit val catsBitraverseForEither: Bitraverse[Either] = + implicit val catsStdBitraverseForEither: Bitraverse[Either] = new Bitraverse[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 { @@ -23,7 +23,7 @@ trait EitherInstances extends EitherInstances1 { } } - implicit def catsMonadTraverseForEither[A]: Monad[Either[A, ?]] with Traverse[Either[A, ?]] = + implicit def catsStdInstancesForEither[A]: Monad[Either[A, ?]] with Traverse[Either[A, ?]] = new Monad[Either[A, ?]] with Traverse[Either[A, ?]] { def pure[B](b: B): Either[A, B] = Right(b) @@ -55,14 +55,14 @@ trait EitherInstances extends EitherInstances1 { fa.fold(_ => lc, b => f(b, lc)) } - implicit def catsOrderForEither[A, B](implicit A: Order[A], B: Order[B]): Order[Either[A, B]] = new Order[Either[A, B]] { + implicit def catsStdOrderForEither[A, B](implicit A: Order[A], B: Order[B]): Order[Either[A, B]] = new Order[Either[A, B]] { def compare(x: Either[A, B], y: Either[A, B]): Int = x.fold( a => y.fold(A.compare(a, _), _ => -1), b => y.fold(_ => 1, B.compare(b, _)) ) } - implicit def catsShowForEither[A, B](implicit A: Show[A], B: Show[B]): Show[Either[A, B]] = + implicit def catsStdShowForEither[A, B](implicit A: Show[A], B: Show[B]): Show[Either[A, B]] = new Show[Either[A, B]] { def show(f: Either[A, B]): String = f.fold( a => s"Left(${A.show(a)})", @@ -72,7 +72,7 @@ trait EitherInstances extends EitherInstances1 { } private[std] sealed trait EitherInstances1 extends EitherInstances2 { - implicit def catsPartialOrderForEither[A, B](implicit A: PartialOrder[A], B: PartialOrder[B]): PartialOrder[Either[A, B]] = + implicit def catsStdPartialOrderForEither[A, B](implicit A: PartialOrder[A], B: PartialOrder[B]): PartialOrder[Either[A, B]] = new PartialOrder[Either[A, B]] { def partialCompare(x: Either[A, B], y: Either[A, B]): Double = x.fold( a => y.fold(A.partialCompare(a, _), _ => -1), @@ -82,7 +82,7 @@ private[std] sealed trait EitherInstances1 extends EitherInstances2 { } private[std] sealed trait EitherInstances2 { - implicit def catsEqForEither[A, B](implicit A: Eq[A], B: Eq[B]): Eq[Either[A, B]] = new Eq[Either[A, B]] { + implicit def catsStdEqForEither[A, B](implicit A: Eq[A], B: Eq[B]): Eq[Either[A, B]] = new Eq[Either[A, B]] { def eqv(x: Either[A, B], y: Either[A, B]): Boolean = x.fold( a => y.fold(A.eqv(a, _), _ => false), b => y.fold(_ => false, B.eqv(b, _)) diff --git a/core/src/main/scala/cats/std/function.scala b/core/src/main/scala/cats/std/function.scala index fae6aa4df5..5d29283179 100644 --- a/core/src/main/scala/cats/std/function.scala +++ b/core/src/main/scala/cats/std/function.scala @@ -7,7 +7,7 @@ import cats.functor.Contravariant private[std] sealed trait Function0Instances { - implicit val catsBimonadForFunction0: Bimonad[Function0] = + implicit val catsStdBimonadForFunction0: Bimonad[Function0] = new Bimonad[Function0] { def extract[A](x: () => A): A = x() @@ -20,20 +20,20 @@ private[std] sealed trait Function0Instances { () => f(fa())() } - implicit def eqFunction0[A](implicit A: Eq[A]): Eq[() => A] = + implicit def catsStdEqForFunction0[A](implicit A: Eq[A]): Eq[() => A] = new Eq[() => A] { def eqv(x: () => A, y: () => A): Boolean = A.eqv(x(), y()) } } private[std] sealed trait Function1Instances extends Function1Instances0 { - implicit def catsContravariantForFunction1[R]: Contravariant[? => R] = + implicit def catsStdContravariantForFunction1[R]: Contravariant[? => R] = new Contravariant[? => R] { def contramap[T1, T0](fa: T1 => R)(f: T0 => T1): T0 => R = fa.compose(f) } - implicit def catsMonadReaderForFunction1[T1]: MonadReader[T1 => ?, T1] = + implicit def catsStdMonadReaderForFunction1[T1]: MonadReader[T1 => ?, T1] = new MonadReader[T1 => ?, T1] { def pure[R](r: R): T1 => R = _ => r @@ -48,7 +48,7 @@ private[std] sealed trait Function1Instances extends Function1Instances0 { f.compose(fa) } - implicit val catsChoiceArrowForFunction1: Choice[Function1] with Arrow[Function1] = + implicit val catsStdInstancesForFunction1: Choice[Function1] with Arrow[Function1] = new Choice[Function1] with Arrow[Function1] { def choice[A, B, C](f: A => C, g: B => C): Xor[A, B] => C = _ match { @@ -71,18 +71,18 @@ private[std] sealed trait Function1Instances extends Function1Instances0 { def compose[A, B, C](f: B => C, g: A => B): A => C = f.compose(g) } - implicit def catsMonoidForFunction1[A,B](implicit M: Monoid[B]): Monoid[A => B] = + implicit def catsStdMonoidForFunction1[A,B](implicit M: Monoid[B]): Monoid[A => B] = new Function1Monoid[A, B] { def B: Monoid[B] = M } - implicit val catsMonoidKForFunction1: MonoidK[Lambda[A => A => A]] = + implicit val catsStdMonoidKForFunction1: MonoidK[Lambda[A => A => A]] = new Function1MonoidK {} } private[std] sealed trait Function1Instances0 { - implicit def catsSemigroupForFunction1[A,B](implicit S: Semigroup[B]): Semigroup[A => B] = + implicit def catsStdSemigroupForFunction1[A,B](implicit S: Semigroup[B]): Semigroup[A => B] = new Function1Semigroup[A, B] { def B: Semigroup[B] = S } - implicit val catsSemigroupKForFunction1: SemigroupK[Lambda[A => A => A]] = + implicit val catsStdSemigroupKForFunction1: SemigroupK[Lambda[A => A => A]] = new Function1SemigroupK {} } diff --git a/core/src/main/scala/cats/std/future.scala b/core/src/main/scala/cats/std/future.scala index f9b255a4ff..40cc5cf556 100644 --- a/core/src/main/scala/cats/std/future.scala +++ b/core/src/main/scala/cats/std/future.scala @@ -9,7 +9,7 @@ import scala.concurrent.{ExecutionContext, Future} trait FutureInstances extends FutureInstances1 { - implicit def catsMonadErrorCoFlatMapForFuture(implicit ec: ExecutionContext): MonadError[Future, Throwable] with CoflatMap[Future] = + implicit def catsStdInstancesForFuture(implicit ec: ExecutionContext): MonadError[Future, Throwable] with CoflatMap[Future] = new FutureCoflatMap with MonadError[Future, Throwable]{ def pure[A](x: A): Future[A] = Future.successful(x) @@ -35,17 +35,17 @@ trait FutureInstances extends FutureInstances1 { override def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f) } - implicit def catsGroupForFuture[A: Group](implicit ec: ExecutionContext): Group[Future[A]] = + implicit def catsStdGroupForFuture[A: Group](implicit ec: ExecutionContext): Group[Future[A]] = new FutureGroup[A] } private[std] sealed trait FutureInstances1 extends FutureInstances2 { - implicit def catsMonoidForFuture[A: Monoid](implicit ec: ExecutionContext): Monoid[Future[A]] = + implicit def catsStdMonoidForFuture[A: Monoid](implicit ec: ExecutionContext): Monoid[Future[A]] = new FutureMonoid[A] } private[std] sealed trait FutureInstances2 { - implicit def catsSemigroupForFuture[A: Semigroup](implicit ec: ExecutionContext): Semigroup[Future[A]] = + implicit def catsStdSemigroupForFuture[A: Semigroup](implicit ec: ExecutionContext): Semigroup[Future[A]] = new FutureSemigroup[A] } diff --git a/core/src/main/scala/cats/std/list.scala b/core/src/main/scala/cats/std/list.scala index 885a93e4fe..102ad05e71 100644 --- a/core/src/main/scala/cats/std/list.scala +++ b/core/src/main/scala/cats/std/list.scala @@ -8,7 +8,7 @@ import scala.collection.mutable.ListBuffer trait ListInstances extends cats.kernel.std.ListInstances { - implicit val catsTraverseMonadCombineCoflatMapForList: Traverse[List] with MonadCombine[List] with CoflatMap[List] = + implicit val catsStdInstancesForList: Traverse[List] with MonadCombine[List] with CoflatMap[List] = new Traverse[List] with MonadCombine[List] with CoflatMap[List] { def empty[A]: List[A] = Nil @@ -61,7 +61,7 @@ trait ListInstances extends cats.kernel.std.ListInstances { override def isEmpty[A](fa: List[A]): Boolean = fa.isEmpty } - implicit def catsShowForList[A:Show]: Show[List[A]] = + implicit def catsStdShowForList[A:Show]: Show[List[A]] = new Show[List[A]] { def show(fa: List[A]): String = fa.map(_.show).mkString("List(", ", ", ")") } diff --git a/core/src/main/scala/cats/std/map.scala b/core/src/main/scala/cats/std/map.scala index 00a4b4323e..9ee18cb689 100644 --- a/core/src/main/scala/cats/std/map.scala +++ b/core/src/main/scala/cats/std/map.scala @@ -3,7 +3,7 @@ package std trait MapInstances extends cats.kernel.std.MapInstances { - implicit def catsShowForMap[A, B](implicit showA: Show[A], showB: Show[B]): Show[Map[A, B]] = + implicit def catsStdShowForMap[A, B](implicit showA: Show[A], showB: Show[B]): Show[Map[A, B]] = Show.show[Map[A, B]] { m => val body = m.map { case (a, b) => s"${showA.show(a)} -> ${showB.show(b)})" @@ -11,7 +11,7 @@ trait MapInstances extends cats.kernel.std.MapInstances { s"Map($body)" } - implicit def catsTraverseFlatMapForMap[K]: Traverse[Map[K, ?]] with FlatMap[Map[K, ?]] = + implicit def catsStdInstancesForMap[K]: Traverse[Map[K, ?]] with FlatMap[Map[K, ?]] = new Traverse[Map[K, ?]] with FlatMap[Map[K, ?]] { def traverse[G[_], A, B](fa: Map[K, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[Map[K, B]] = { diff --git a/core/src/main/scala/cats/std/option.scala b/core/src/main/scala/cats/std/option.scala index c6b4e435c3..af943b923b 100644 --- a/core/src/main/scala/cats/std/option.scala +++ b/core/src/main/scala/cats/std/option.scala @@ -3,7 +3,7 @@ package std trait OptionInstances extends cats.kernel.std.OptionInstances { - implicit val catsTraverseMonadErrorMonadCombineCoflatMapAlternativeForOption: Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] = + implicit val catsStdInstancesForOption: Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] = new Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] { def empty[A]: Option[A] = None @@ -62,7 +62,7 @@ trait OptionInstances extends cats.kernel.std.OptionInstances { fa.isEmpty } - implicit def catsShowForOption[A](implicit A: Show[A]): Show[Option[A]] = + implicit def catsStdShowForOption[A](implicit A: Show[A]): Show[Option[A]] = new Show[Option[A]] { def show(fa: Option[A]): String = fa match { case Some(a) => s"Some(${A.show(a)})" diff --git a/core/src/main/scala/cats/std/set.scala b/core/src/main/scala/cats/std/set.scala index 79dd1e8d9f..09da1a88de 100644 --- a/core/src/main/scala/cats/std/set.scala +++ b/core/src/main/scala/cats/std/set.scala @@ -5,7 +5,7 @@ import cats.syntax.show._ trait SetInstances extends cats.kernel.std.SetInstances { - implicit val catsFoldableMonoidKForSet: Foldable[Set] with MonoidK[Set] = + implicit val catsStdInstancesForSet: Foldable[Set] with MonoidK[Set] = new Foldable[Set] with MonoidK[Set] { def empty[A]: Set[A] = Set.empty[A] @@ -27,7 +27,7 @@ trait SetInstances extends cats.kernel.std.SetInstances { override def isEmpty[A](fa: Set[A]): Boolean = fa.isEmpty } - implicit def catsShowForSet[A:Show]: Show[Set[A]] = new Show[Set[A]] { + implicit def catsStdShowForSet[A:Show]: Show[Set[A]] = new Show[Set[A]] { def show(fa: Set[A]): String = fa.toIterator.map(_.show).mkString("Set(", ", ", ")") } diff --git a/core/src/main/scala/cats/std/stream.scala b/core/src/main/scala/cats/std/stream.scala index 3f694a7d3e..b2b5595dae 100644 --- a/core/src/main/scala/cats/std/stream.scala +++ b/core/src/main/scala/cats/std/stream.scala @@ -4,7 +4,7 @@ package std import cats.syntax.show._ trait StreamInstances extends cats.kernel.std.StreamInstances { - implicit val catsTraverseMonadCombineCoflatMapForStream: Traverse[Stream] with MonadCombine[Stream] with CoflatMap[Stream] = + implicit val catsStdInstancesForStream: Traverse[Stream] with MonadCombine[Stream] with CoflatMap[Stream] = new Traverse[Stream] with MonadCombine[Stream] with CoflatMap[Stream] { def empty[A]: Stream[A] = Stream.Empty @@ -55,7 +55,7 @@ trait StreamInstances extends cats.kernel.std.StreamInstances { override def isEmpty[A](fa: Stream[A]): Boolean = fa.isEmpty } - implicit def catsShowForStream[A: Show]: Show[Stream[A]] = + implicit def catsStdShowForStream[A: Show]: Show[Stream[A]] = new Show[Stream[A]] { def show(fa: Stream[A]): String = if(fa.isEmpty) "Stream()" else s"Stream(${fa.head.show}, ?)" } diff --git a/core/src/main/scala/cats/std/string.scala b/core/src/main/scala/cats/std/string.scala index c947774ccf..7f46057ea2 100644 --- a/core/src/main/scala/cats/std/string.scala +++ b/core/src/main/scala/cats/std/string.scala @@ -2,6 +2,6 @@ package cats package std trait StringInstances extends cats.kernel.std.StringInstances { - implicit val catsShowForString: Show[String] = + implicit val catsStdShowForString: Show[String] = Show.fromToString[String] } diff --git a/core/src/main/scala/cats/std/tuple.scala b/core/src/main/scala/cats/std/tuple.scala index 2314f7b709..76b9050e06 100644 --- a/core/src/main/scala/cats/std/tuple.scala +++ b/core/src/main/scala/cats/std/tuple.scala @@ -4,7 +4,7 @@ package std trait TupleInstances extends Tuple2Instances with cats.kernel.std.TupleInstances sealed trait Tuple2Instances { - implicit val catsBitraverseForTuple2: Bitraverse[Tuple2] = + implicit val catsStdBitraverseForTuple2: Bitraverse[Tuple2] = new Bitraverse[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)) @@ -16,7 +16,7 @@ sealed trait Tuple2Instances { g(fab._2, f(fab._1, c)) } - implicit def catsShowForTuple2[A, B](implicit aShow: Show[A], bShow: Show[B]): Show[(A, B)] = new Show[(A, B)] { + implicit def catsStdShowForTuple2[A, B](implicit aShow: Show[A], bShow: Show[B]): Show[(A, B)] = new Show[(A, B)] { override def show(f: (A, B)): String = { s"(${aShow.show(f._1)},${bShow.show(f._2)})" } diff --git a/core/src/main/scala/cats/std/vector.scala b/core/src/main/scala/cats/std/vector.scala index f67b3c8edc..62ead466a3 100644 --- a/core/src/main/scala/cats/std/vector.scala +++ b/core/src/main/scala/cats/std/vector.scala @@ -8,7 +8,7 @@ import scala.collection.+: import scala.collection.immutable.VectorBuilder trait VectorInstances extends cats.kernel.std.VectorInstances { - implicit val catsTraverseMonadCombineCoflatMapForVector: Traverse[Vector] with MonadCombine[Vector] with CoflatMap[Vector] = + implicit val catsStdInstancesForVector: Traverse[Vector] with MonadCombine[Vector] with CoflatMap[Vector] = new Traverse[Vector] with MonadCombine[Vector] with CoflatMap[Vector] { def empty[A]: Vector[A] = Vector.empty[A] @@ -55,7 +55,7 @@ trait VectorInstances extends cats.kernel.std.VectorInstances { override def isEmpty[A](fa: Vector[A]): Boolean = fa.isEmpty } - implicit def catsShowForVector[A:Show]: Show[Vector[A]] = + implicit def catsStdShowForVector[A:Show]: Show[Vector[A]] = new Show[Vector[A]] { def show(fa: Vector[A]): String = fa.map(_.show).mkString("Vector(", ", ", ")") } diff --git a/free/src/main/scala/cats/free/Trampoline.scala b/free/src/main/scala/cats/free/Trampoline.scala index e5f68c06b1..663fd32080 100644 --- a/free/src/main/scala/cats/free/Trampoline.scala +++ b/free/src/main/scala/cats/free/Trampoline.scala @@ -1,7 +1,7 @@ package cats package free -import cats.std.function.function0Instance +import cats.std.function.catsStdBimonadForFunction0 // To workaround SI-7139 `object Trampoline` needs to be defined inside the package object // together with the type alias. diff --git a/tests/src/test/scala/cats/tests/EitherTests.scala b/tests/src/test/scala/cats/tests/EitherTests.scala index d0ab3237ee..97d7d74e08 100644 --- a/tests/src/test/scala/cats/tests/EitherTests.scala +++ b/tests/src/test/scala/cats/tests/EitherTests.scala @@ -20,7 +20,7 @@ class EitherTests extends CatsSuite { checkAll("Either[?, ?]", BitraverseTests[Either].bitraverse[Option, Int, Int, Int, String, String, String]) checkAll("Bitraverse[Either]", SerializableTests.serializable(Bitraverse[Either])) - val partialOrder = eitherPartialOrder[Int, String] + val partialOrder = catsStdPartialOrderForEither[Int, String] val order = implicitly[Order[Either[Int, String]]] val monad = implicitly[Monad[Either[Int, ?]]] val show = implicitly[Show[Either[Int, String]]] @@ -38,7 +38,7 @@ class EitherTests extends CatsSuite { test("implicit instances resolve specifically") { - val eq = eitherEq[Int, String] + val eq = catsStdEqForEither[Int, String] assert(!eq.isInstanceOf[PartialOrder[_]]) assert(!eq.isInstanceOf[Order[_]]) assert(!partialOrder.isInstanceOf[Order[_]]) diff --git a/tests/src/test/scala/cats/tests/FunctionTests.scala b/tests/src/test/scala/cats/tests/FunctionTests.scala index d7ca5cf729..07e65eefcd 100644 --- a/tests/src/test/scala/cats/tests/FunctionTests.scala +++ b/tests/src/test/scala/cats/tests/FunctionTests.scala @@ -32,13 +32,13 @@ class FunctionTests extends CatsSuite { checkAll("Function1[Int, Int]", ContravariantTests[? => Int].contravariant[Int, Int, Int]) checkAll("Contravariant[? => Int]", SerializableTests.serializable(Contravariant[? => Int])) - checkAll("Function1[String, Int]", GroupLaws[Function1[String, Int]].semigroup(function1Semigroup[String, Int])) + checkAll("Function1[String, Int]", GroupLaws[Function1[String, Int]].semigroup(catsStdSemigroupForFunction1[String, Int])) checkAll("Function1[String, Int]", GroupLaws[Function1[String, Int]].monoid) checkAll("Function1[Int, Int]", MonoidKTests[Lambda[A => A => A]].semigroupK[Int]) - checkAll("SemigroupK[Lambda[A => A => A]", SerializableTests.serializable(function1SemigroupK)) + checkAll("SemigroupK[Lambda[A => A => A]", SerializableTests.serializable(catsStdSemigroupKForFunction1)) checkAll("Function1[Int, Int]", MonoidKTests[Lambda[A => A => A]].monoidK[Int]) - checkAll("MonoidK[Lambda[A => A => A]", SerializableTests.serializable(function1MonoidK)) + checkAll("MonoidK[Lambda[A => A => A]", SerializableTests.serializable(catsStdMonoidKForFunction1)) } diff --git a/tests/src/test/scala/cats/tests/SetTests.scala b/tests/src/test/scala/cats/tests/SetTests.scala index e4716df529..663151ac5d 100644 --- a/tests/src/test/scala/cats/tests/SetTests.scala +++ b/tests/src/test/scala/cats/tests/SetTests.scala @@ -22,7 +22,8 @@ class SetTests extends CatsSuite { } test("show keeps separate entries for items that map to identical strings"){ - implicit val intShow: Show[Int] = Show.show(_ => "1") + //note: this val name has to be the same to shadow the cats.std instance + implicit val catsStdShowForInt: Show[Int] = Show.show(_ => "1") // an implementation implemented as set.map(_.show).mkString(", ") would // only show one entry in the result instead of 3, because Set.map combines // duplicate items in the codomain. From 49c7eecdd8618b4a3ee74f2c18469cc8228295c3 Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 27 May 2016 10:43:35 -0400 Subject: [PATCH 179/185] renamed implicit vals in data, part 2 of #1061 --- core/src/main/scala/cats/data/Cokleisli.scala | 12 ++-- core/src/main/scala/cats/data/Const.scala | 22 +++---- core/src/main/scala/cats/data/Coproduct.scala | 14 ++--- core/src/main/scala/cats/data/Func.scala | 6 +- core/src/main/scala/cats/data/Ior.scala | 10 ++-- core/src/main/scala/cats/data/Kleisli.scala | 48 +++++++-------- core/src/main/scala/cats/data/OneAnd.scala | 18 +++--- core/src/main/scala/cats/data/OptionT.scala | 10 ++-- core/src/main/scala/cats/data/Prod.scala | 14 ++--- core/src/main/scala/cats/data/StateT.scala | 4 +- core/src/main/scala/cats/data/Validated.scala | 12 ++-- core/src/main/scala/cats/data/WriterT.scala | 60 +++++++++---------- core/src/main/scala/cats/data/Xor.scala | 18 +++--- core/src/main/scala/cats/data/XorT.scala | 26 ++++---- .../scala/cats/tests/CokleisliTests.scala | 4 +- .../test/scala/cats/tests/ConstTests.scala | 4 +- .../scala/cats/tests/CoproductTests.scala | 4 +- .../src/test/scala/cats/tests/FuncTests.scala | 6 +- .../test/scala/cats/tests/KleisliTests.scala | 36 +++++------ .../test/scala/cats/tests/OneAndTests.scala | 2 +- .../scala/cats/tests/ValidatedTests.scala | 2 +- .../test/scala/cats/tests/WriterTTests.scala | 2 +- .../src/test/scala/cats/tests/XorTTests.scala | 4 +- .../src/test/scala/cats/tests/XorTests.scala | 2 +- 24 files changed, 170 insertions(+), 170 deletions(-) diff --git a/core/src/main/scala/cats/data/Cokleisli.scala b/core/src/main/scala/cats/data/Cokleisli.scala index 4aa36b0d55..a4bfdecb67 100644 --- a/core/src/main/scala/cats/data/Cokleisli.scala +++ b/core/src/main/scala/cats/data/Cokleisli.scala @@ -44,10 +44,10 @@ object Cokleisli extends CokleisliInstances { } private[data] sealed abstract class CokleisliInstances extends CokleisliInstances0 { - implicit def cokleisliArrow[F[_]](implicit ev: Comonad[F]): Arrow[Cokleisli[F, ?, ?]] = + implicit def catsDataArrowForCokleisli[F[_]](implicit ev: Comonad[F]): Arrow[Cokleisli[F, ?, ?]] = new CokleisliArrow[F] { def F: Comonad[F] = ev } - implicit def cokleisliMonad[F[_], A]: Monad[Cokleisli[F, A, ?]] = new Monad[Cokleisli[F, A, ?]] { + 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) @@ -58,18 +58,18 @@ private[data] sealed abstract class CokleisliInstances extends CokleisliInstance fa.map(f) } - implicit def cokleisliMonoidK[F[_]](implicit ev: Comonad[F]): MonoidK[Lambda[A => Cokleisli[F, A, A]]] = + implicit def catsDataMonoidKForCokleisli[F[_]](implicit ev: Comonad[F]): MonoidK[Lambda[A => Cokleisli[F, A, A]]] = new CokleisliMonoidK[F] { def F: Comonad[F] = ev } } private[data] sealed abstract class CokleisliInstances0 { - implicit def cokleisliSplit[F[_]](implicit ev: CoflatMap[F]): Split[Cokleisli[F, ?, ?]] = + implicit def catsDataSplitForCokleisli[F[_]](implicit ev: CoflatMap[F]): Split[Cokleisli[F, ?, ?]] = new CokleisliSplit[F] { def F: CoflatMap[F] = ev } - implicit def cokleisliProfunctor[F[_]](implicit ev: Functor[F]): Profunctor[Cokleisli[F, ?, ?]] = + implicit def catsDataProfunctorForCokleisli[F[_]](implicit ev: Functor[F]): Profunctor[Cokleisli[F, ?, ?]] = new CokleisliProfunctor[F] { def F: Functor[F] = ev } - implicit def cokleisliSemigroupK[F[_]](implicit ev: CoflatMap[F]): SemigroupK[Lambda[A => Cokleisli[F, A, A]]] = + implicit def catsDataSemigroupKForCokleisli[F[_]](implicit ev: CoflatMap[F]): SemigroupK[Lambda[A => Cokleisli[F, A, A]]] = new CokleisliSemigroupK[F] { def F: CoflatMap[F] = ev } } diff --git a/core/src/main/scala/cats/data/Const.scala b/core/src/main/scala/cats/data/Const.scala index 32fc1e3b91..083bd236bf 100644 --- a/core/src/main/scala/cats/data/Const.scala +++ b/core/src/main/scala/cats/data/Const.scala @@ -39,21 +39,21 @@ object Const extends ConstInstances { } private[data] sealed abstract class ConstInstances extends ConstInstances0 { - implicit def constOrder[A: Order, B]: Order[Const[A, B]] = new Order[Const[A, B]] { + implicit def catsDataOrderForConst[A: Order, B]: Order[Const[A, B]] = new Order[Const[A, B]] { def compare(x: Const[A, B], y: Const[A, B]): Int = x compare y } - implicit def constShow[A: Show, B]: Show[Const[A, B]] = new Show[Const[A, B]] { + implicit def catsDataShowForConst[A: Show, B]: Show[Const[A, B]] = new Show[Const[A, B]] { def show(f: Const[A, B]): String = f.show } - implicit def constContravariant[C]: Contravariant[Const[C, ?]] = new Contravariant[Const[C, ?]] { + implicit def catsDataContravariantForConst[C]: Contravariant[Const[C, ?]] = new Contravariant[Const[C, ?]] { override def contramap[A, B](fa: Const[C, A])(f: (B) => A): Const[C, B] = fa.retag[B] } - implicit def constTraverse[C]: Traverse[Const[C, ?]] = new Traverse[Const[C, ?]] { + implicit def catsDataTraverseForConst[C]: Traverse[Const[C, ?]] = new Traverse[Const[C, ?]] { def traverse[G[_]: Applicative, A, B](fa: Const[C, A])(f: A => G[B]): G[Const[C, B]] = fa.traverse(f) @@ -62,7 +62,7 @@ private[data] sealed abstract class ConstInstances extends ConstInstances0 { def foldRight[A, B](fa: Const[C, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb } - implicit def constMonoid[A: Monoid, B]: Monoid[Const[A, B]] = new Monoid[Const[A, B]]{ + implicit def catsDataMonoidForConst[A: Monoid, B]: Monoid[Const[A, B]] = new Monoid[Const[A, B]]{ def empty: Const[A, B] = Const.empty @@ -70,7 +70,7 @@ private[data] sealed abstract class ConstInstances extends ConstInstances0 { x combine y } - implicit val constBifoldable: Bifoldable[Const] = + implicit val catsDataBifoldableForConst: Bifoldable[Const] = new Bifoldable[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) @@ -82,16 +82,16 @@ private[data] sealed abstract class ConstInstances extends ConstInstances0 { private[data] sealed abstract class ConstInstances0 extends ConstInstances1 { - implicit def constSemigroup[A: Semigroup, B]: Semigroup[Const[A, B]] = new Semigroup[Const[A, B]] { + implicit def catsDataSemigroupForConst[A: Semigroup, B]: Semigroup[Const[A, B]] = new Semigroup[Const[A, B]] { def combine(x: Const[A, B], y: Const[A, B]): Const[A, B] = x combine y } - implicit def constPartialOrder[A: PartialOrder, B]: PartialOrder[Const[A, B]] = new PartialOrder[Const[A, B]]{ + implicit def catsDataPartialOrderForConst[A: PartialOrder, B]: PartialOrder[Const[A, B]] = new PartialOrder[Const[A, B]]{ def partialCompare(x: Const[A, B], y: Const[A, B]): Double = x partialCompare y } - implicit def constApplicative[C: Monoid]: Applicative[Const[C, ?]] = new Applicative[Const[C, ?]] { + implicit def catsDataApplicativeForConst[C: Monoid]: Applicative[Const[C, ?]] = new Applicative[Const[C, ?]] { def pure[A](x: A): Const[C, A] = Const.empty @@ -107,12 +107,12 @@ private[data] sealed abstract class ConstInstances0 extends ConstInstances1 { } private[data] sealed abstract class ConstInstances1 { - implicit def constEq[A: Eq, B]: Eq[Const[A, B]] = new Eq[Const[A, B]] { + implicit def catsDataEqForConst[A: Eq, B]: Eq[Const[A, B]] = new Eq[Const[A, B]] { def eqv(x: Const[A, B], y: Const[A, B]): Boolean = x === y } - implicit def constApply[C: Semigroup]: Apply[Const[C, ?]] = new Apply[Const[C, ?]] { + implicit def catsDataApplyForConst[C: Semigroup]: Apply[Const[C, ?]] = new Apply[Const[C, ?]] { def ap[A, B](f: Const[C, A => B])(fa: Const[C, A]): Const[C, B] = fa.retag[B] combine f.retag[B] diff --git a/core/src/main/scala/cats/data/Coproduct.scala b/core/src/main/scala/cats/data/Coproduct.scala index 8e963b07a5..1428abd123 100644 --- a/core/src/main/scala/cats/data/Coproduct.scala +++ b/core/src/main/scala/cats/data/Coproduct.scala @@ -105,17 +105,17 @@ object Coproduct extends CoproductInstances { private[data] sealed abstract class CoproductInstances3 { - implicit def coproductEq[F[_], G[_], A](implicit E: Eq[F[A] Xor G[A]]): Eq[Coproduct[F, G, A]] = + implicit def catsDataEqForCoproduct[F[_], G[_], A](implicit E: Eq[F[A] Xor G[A]]): Eq[Coproduct[F, G, A]] = Eq.by(_.run) - implicit def coproductFunctor[F[_], G[_]](implicit F0: Functor[F], G0: Functor[G]): Functor[Coproduct[F, G, ?]] = + implicit def catsDataFunctorForCoproduct[F[_], G[_]](implicit F0: Functor[F], G0: Functor[G]): Functor[Coproduct[F, G, ?]] = new CoproductFunctor[F, G] { implicit def F: Functor[F] = F0 implicit def G: Functor[G] = G0 } - implicit def coproductFoldable[F[_], G[_]](implicit F0: Foldable[F], G0: Foldable[G]): Foldable[Coproduct[F, G, ?]] = + implicit def catsDataFoldableForCoproduct[F[_], G[_]](implicit F0: Foldable[F], G0: Foldable[G]): Foldable[Coproduct[F, G, ?]] = new CoproductFoldable[F, G] { implicit def F: Foldable[F] = F0 @@ -125,7 +125,7 @@ private[data] sealed abstract class CoproductInstances3 { private[data] sealed abstract class CoproductInstances2 extends CoproductInstances3 { - implicit def coproductContravariant[F[_], G[_]](implicit F0: Contravariant[F], G0: Contravariant[G]): Contravariant[Coproduct[F, G, ?]] = + implicit def catsDataContravariantForCoproduct[F[_], G[_]](implicit F0: Contravariant[F], G0: Contravariant[G]): Contravariant[Coproduct[F, G, ?]] = new CoproductContravariant[F, G] { implicit def F: Contravariant[F] = F0 @@ -134,7 +134,7 @@ private[data] sealed abstract class CoproductInstances2 extends CoproductInstanc } private[data] sealed abstract class CoproductInstances1 extends CoproductInstances2 { - implicit def coproductCoflatMap[F[_], G[_]](implicit F0: CoflatMap[F], G0: CoflatMap[G]): CoflatMap[Coproduct[F, G, ?]] = + implicit def catsDataCoflatMapForCoproduct[F[_], G[_]](implicit F0: CoflatMap[F], G0: CoflatMap[G]): CoflatMap[Coproduct[F, G, ?]] = new CoproductCoflatMap[F, G] { implicit def F: CoflatMap[F] = F0 @@ -143,7 +143,7 @@ private[data] sealed abstract class CoproductInstances1 extends CoproductInstanc } private[data] sealed abstract class CoproductInstances0 extends CoproductInstances1 { - implicit def coproductTraverse[F[_], G[_]](implicit F0: Traverse[F], G0: Traverse[G]): Traverse[Coproduct[F, G, ?]] = + implicit def catsDataTraverseForCoproduct[F[_], G[_]](implicit F0: Traverse[F], G0: Traverse[G]): Traverse[Coproduct[F, G, ?]] = new CoproductTraverse[F, G] { implicit def F: Traverse[F] = F0 @@ -153,7 +153,7 @@ private[data] sealed abstract class CoproductInstances0 extends CoproductInstanc sealed abstract class CoproductInstances extends CoproductInstances0 { - implicit def coproductComonad[F[_], G[_]](implicit F0: Comonad[F], G0: Comonad[G]): Comonad[Coproduct[F, G, ?]] = + implicit def catsDataComonadForCoproduct[F[_], G[_]](implicit F0: Comonad[F], G0: Comonad[G]): Comonad[Coproduct[F, G, ?]] = new CoproductComonad[F, G] { implicit def F: Comonad[F] = F0 diff --git a/core/src/main/scala/cats/data/Func.scala b/core/src/main/scala/cats/data/Func.scala index 54487031ff..d8ce1916f3 100644 --- a/core/src/main/scala/cats/data/Func.scala +++ b/core/src/main/scala/cats/data/Func.scala @@ -32,21 +32,21 @@ object Func extends FuncInstances { } private[data] abstract class FuncInstances extends FuncInstances0 { - implicit def funcApplicative[F[_], C](implicit FF: Applicative[F]): Applicative[Lambda[X => Func[F, C, X]]] = + implicit def catsDataApplicativeForFunc[F[_], C](implicit FF: Applicative[F]): Applicative[Lambda[X => Func[F, C, X]]] = new FuncApplicative[F, C] { def F: Applicative[F] = FF } } private[data] abstract class FuncInstances0 extends FuncInstances1 { - implicit def funcApply[F[_], C](implicit FF: Apply[F]): Apply[Lambda[X => Func[F, C, X]]] = + implicit def catsDataApplyForFunc[F[_], C](implicit FF: Apply[F]): Apply[Lambda[X => Func[F, C, X]]] = new FuncApply[F, C] { def F: Apply[F] = FF } } private[data] abstract class FuncInstances1 { - implicit def funcFunctor[F[_], C](implicit FF: Functor[F]): Functor[Lambda[X => Func[F, C, X]]] = + implicit def catsDataFunctorForFunc[F[_], C](implicit FF: Functor[F]): Functor[Lambda[X => Func[F, C, X]]] = new FuncFunctor[F, C] { def F: Functor[F] = FF } diff --git a/core/src/main/scala/cats/data/Ior.scala b/core/src/main/scala/cats/data/Ior.scala index 8ad8e6e6f6..4c60724314 100644 --- a/core/src/main/scala/cats/data/Ior.scala +++ b/core/src/main/scala/cats/data/Ior.scala @@ -132,20 +132,20 @@ object Ior extends IorInstances with IorFunctions { } private[data] sealed abstract class IorInstances extends IorInstances0 { - implicit def iorEq[A: Eq, B: Eq]: Eq[A Ior B] = new Eq[A Ior B] { + implicit def catsDataEqForIor[A: Eq, B: Eq]: Eq[A Ior B] = new Eq[A Ior B] { def eqv(x: A Ior B, y: A Ior B): Boolean = x === y } - implicit def iorShow[A: Show, B: Show]: Show[A Ior B] = new Show[A Ior B] { + implicit def catsDataShowForIor[A: Show, B: Show]: Show[A Ior B] = new Show[A Ior B] { def show(f: A Ior B): String = f.show } - implicit def iorMonad[A: Semigroup]: Monad[A Ior ?] = new Monad[A Ior ?] { + implicit def catsDataMonadForIor[A: Semigroup]: Monad[A Ior ?] = new Monad[A Ior ?] { def pure[B](b: B): A Ior B = Ior.right(b) def flatMap[B, C](fa: A Ior B)(f: B => A Ior C): A Ior C = fa.flatMap(f) } - implicit def iorBifunctor: Bifunctor[Ior] = + implicit def catsDataBifunctorForIor: Bifunctor[Ior] = new Bifunctor[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) } @@ -153,7 +153,7 @@ private[data] sealed abstract class IorInstances extends IorInstances0 { private[data] sealed abstract class IorInstances0 { - implicit def iorInstances[A]: Traverse[A Ior ?] with Functor[A Ior ?] = new Traverse[A Ior ?] with Functor[A Ior ?] { + implicit def catsDataTraverseFunctorForIor[A]: Traverse[A Ior ?] with Functor[A Ior ?] = new Traverse[A Ior ?] with Functor[A Ior ?] { def traverse[F[_]: Applicative, B, C](fa: A Ior B)(f: B => F[C]): F[A Ior C] = fa.traverse(f) def foldLeft[B, C](fa: A Ior B, b: C)(f: (C, B) => C): C = diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index 83c3ca502e..ea4b58c33c 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -79,22 +79,22 @@ private[data] sealed trait KleisliFunctions { private[data] sealed abstract class KleisliInstances extends KleisliInstances0 { - implicit def kleisliMonoid[F[_], A, B](implicit M: Monoid[F[B]]): Monoid[Kleisli[F, A, B]] = + implicit def catsDataMonoidForKleisli[F[_], A, B](implicit M: Monoid[F[B]]): Monoid[Kleisli[F, A, B]] = new KleisliMonoid[F, A, B] { def FB: Monoid[F[B]] = M } - implicit def kleisliMonoidK[F[_]](implicit M: Monad[F]): MonoidK[Lambda[A => Kleisli[F, A, A]]] = + implicit def catsDataMonoidKForKleisli[F[_]](implicit M: Monad[F]): MonoidK[Lambda[A => Kleisli[F, A, A]]] = new KleisliMonoidK[F] { def F: Monad[F] = M } - implicit val kleisliIdMonoidK: MonoidK[Lambda[A => Kleisli[Id, A, A]]] = - kleisliMonoidK[Id] + implicit val catsDataMonoidKForKleisliId: MonoidK[Lambda[A => Kleisli[Id, A, A]]] = + catsDataMonoidKForKleisli[Id] - implicit def kleisliArrow[F[_]](implicit ev: Monad[F]): Arrow[Kleisli[F, ?, ?]] = + implicit def catsDataArrowForKleisli[F[_]](implicit ev: Monad[F]): Arrow[Kleisli[F, ?, ?]] = new KleisliArrow[F] { def F: Monad[F] = ev } - implicit val kleisliIdArrow: Arrow[Kleisli[Id, ?, ?]] = - kleisliArrow[Id] + implicit val catsDataArrowForKleisliId: Arrow[Kleisli[Id, ?, ?]] = + catsDataArrowForKleisli[Id] - implicit def kleisliChoice[F[_]](implicit ev: Monad[F]): Choice[Kleisli[F, ?, ?]] = + implicit def catsDataChoiceForKleisli[F[_]](implicit ev: Monad[F]): Choice[Kleisli[F, ?, ?]] = new Choice[Kleisli[F, ?, ?]] { def id[A]: Kleisli[F, A, A] = Kleisli(ev.pure(_)) @@ -105,37 +105,37 @@ private[data] sealed abstract class KleisliInstances extends KleisliInstances0 { f.compose(g) } - implicit val kleisliIdChoice: Choice[Kleisli[Id, ?, ?]] = - kleisliChoice[Id] + implicit val catsDataChoiceForKleisliId: Choice[Kleisli[Id, ?, ?]] = + catsDataChoiceForKleisli[Id] - implicit def kleisliIdMonadReader[A]: MonadReader[Kleisli[Id, A, ?], A] = - kleisliMonadReader[Id, A] + implicit def catsDataMonadReaderForKleisliId[A]: MonadReader[Kleisli[Id, A, ?], A] = + catsDataMonadReaderForKleisli[Id, A] - implicit def kleisliContravariant[F[_], C]: Contravariant[Kleisli[F, ?, C]] = + implicit def catsDataContravariantForKleisli[F[_], C]: Contravariant[Kleisli[F, ?, C]] = new Contravariant[Kleisli[F, ?, C]] { override def contramap[A, B](fa: Kleisli[F, A, C])(f: (B) => A): Kleisli[F, B, C] = fa.local(f) } - implicit def kleisliTransLift[A]: TransLift.AuxId[Kleisli[?[_], A, ?]] = + implicit def catsDataTransLiftForKleisli[A]: TransLift.AuxId[Kleisli[?[_], A, ?]] = new TransLift[Kleisli[?[_], A, ?]] { type TC[M[_]] = Trivial def liftT[M[_], B](ma: M[B])(implicit ev: Trivial): Kleisli[M, A, B] = Kleisli[M, A, B](a => ma) } - implicit def kleisliApplicativeError[F[_], A, E](implicit AE: ApplicativeError[F, E]): ApplicativeError[Kleisli[F, A, ?], E] + implicit def catsDataApplicativeErrorForKleisli[F[_], A, E](implicit AE: ApplicativeError[F, E]): ApplicativeError[Kleisli[F, A, ?], E] = new KleisliApplicativeError[F, A, E] { implicit def AF: ApplicativeError[F, E] = AE } } private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 { - implicit def kleisliSplit[F[_]](implicit ev: FlatMap[F]): Split[Kleisli[F, ?, ?]] = + implicit def catsDataSplitForKleisli[F[_]](implicit ev: FlatMap[F]): Split[Kleisli[F, ?, ?]] = new KleisliSplit[F] { def F: FlatMap[F] = ev } - implicit def kleisliStrong[F[_]](implicit ev: Functor[F]): Strong[Kleisli[F, ?, ?]] = + implicit def catsDataStrongForKleisli[F[_]](implicit ev: Functor[F]): Strong[Kleisli[F, ?, ?]] = new KleisliStrong[F] { def F: Functor[F] = ev } - implicit def kleisliFlatMap[F[_]: FlatMap, A]: FlatMap[Kleisli[F, A, ?]] = new FlatMap[Kleisli[F, A, ?]] { + implicit def catsDataFlatMapForKleisli[F[_]: FlatMap, A]: FlatMap[Kleisli[F, A, ?]] = new FlatMap[Kleisli[F, A, ?]] { def flatMap[B, C](fa: Kleisli[F, A, B])(f: B => Kleisli[F, A, C]): Kleisli[F, A, C] = fa.flatMap(f) @@ -143,22 +143,22 @@ private[data] sealed abstract class KleisliInstances0 extends KleisliInstances1 fa.map(f) } - implicit def kleisliSemigroup[F[_], A, B](implicit M: Semigroup[F[B]]): Semigroup[Kleisli[F, A, B]] = + implicit def catsDataSemigroupForKleisli[F[_], A, B](implicit M: Semigroup[F[B]]): Semigroup[Kleisli[F, A, B]] = new KleisliSemigroup[F, A, B] { def FB: Semigroup[F[B]] = M } - implicit def kleisliSemigroupK[F[_]](implicit ev: FlatMap[F]): SemigroupK[Lambda[A => Kleisli[F, A, A]]] = + implicit def catsDataSemigroupKForKleisli[F[_]](implicit ev: FlatMap[F]): SemigroupK[Lambda[A => Kleisli[F, A, A]]] = new KleisliSemigroupK[F] { def F: FlatMap[F] = ev } } private[data] sealed abstract class KleisliInstances1 extends KleisliInstances2 { - implicit def kleisliApplicative[F[_], A](implicit A : Applicative[F]): Applicative[Kleisli[F, A, ?]] = new KleisliApplicative[F, A] { + implicit def catsDataApplicativeForKleisli[F[_], A](implicit A : Applicative[F]): Applicative[Kleisli[F, A, ?]] = new KleisliApplicative[F, A] { implicit def F: Applicative[F] = A } } private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3 { - implicit def kleisliApply[F[_]: Apply, A]: Apply[Kleisli[F, A, ?]] = new Apply[Kleisli[F, A, ?]] { + implicit def catsDataApplyForKleisli[F[_]: Apply, A]: Apply[Kleisli[F, A, ?]] = new Apply[Kleisli[F, A, ?]] { def ap[B, C](f: Kleisli[F, A, B => C])(fa: Kleisli[F, A, B]): Kleisli[F, A, C] = fa.ap(f) @@ -171,7 +171,7 @@ private[data] sealed abstract class KleisliInstances2 extends KleisliInstances3 } private[data] sealed abstract class KleisliInstances3 extends KleisliInstances4 { - implicit def kleisliFunctor[F[_]: Functor, A]: Functor[Kleisli[F, A, ?]] = new Functor[Kleisli[F, A, ?]] { + implicit def catsDataFunctorForKleisli[F[_]: Functor, A]: Functor[Kleisli[F, A, ?]] = new Functor[Kleisli[F, A, ?]] { def map[B, C](fa: Kleisli[F, A, B])(f: B => C): Kleisli[F, A, C] = fa.map(f) } @@ -179,7 +179,7 @@ private[data] sealed abstract class KleisliInstances3 extends KleisliInstances4 private[data] sealed abstract class KleisliInstances4 { - implicit def kleisliMonadReader[F[_]: Monad, A]: MonadReader[Kleisli[F, A, ?], A] = + implicit def catsDataMonadReaderForKleisli[F[_]: Monad, A]: MonadReader[Kleisli[F, A, ?], A] = new MonadReader[Kleisli[F, A, ?], A] { def pure[B](x: B): Kleisli[F, A, B] = Kleisli.pure[F, A, B](x) diff --git a/core/src/main/scala/cats/data/OneAnd.scala b/core/src/main/scala/cats/data/OneAnd.scala index 84f81d9bc7..1541ceeaff 100644 --- a/core/src/main/scala/cats/data/OneAnd.scala +++ b/core/src/main/scala/cats/data/OneAnd.scala @@ -96,29 +96,29 @@ final case class OneAnd[F[_], A](head: A, tail: F[A]) { private[data] sealed trait OneAndInstances extends OneAndLowPriority2 { - implicit def oneAndEq[A, F[_]](implicit A: Eq[A], FA: Eq[F[A]]): Eq[OneAnd[F, A]] = + implicit def catsDataEqForOneAnd[A, F[_]](implicit A: Eq[A], FA: Eq[F[A]]): Eq[OneAnd[F, A]] = new Eq[OneAnd[F, A]]{ def eqv(x: OneAnd[F, A], y: OneAnd[F, A]): Boolean = x === y } - implicit def oneAndShow[A, F[_]](implicit A: Show[A], FA: Show[F[A]]): Show[OneAnd[F, A]] = + implicit def catsDataShowForOneAnd[A, F[_]](implicit A: Show[A], FA: Show[F[A]]): Show[OneAnd[F, A]] = Show.show[OneAnd[F, A]](_.show) - implicit def oneAndSemigroupK[F[_]: MonadCombine]: SemigroupK[OneAnd[F, ?]] = + implicit def catsDataSemigroupKForOneAnd[F[_]: MonadCombine]: SemigroupK[OneAnd[F, ?]] = new SemigroupK[OneAnd[F, ?]] { def combineK[A](a: OneAnd[F, A], b: OneAnd[F, A]): OneAnd[F, A] = a combine b } - implicit def oneAndSemigroup[F[_]: MonadCombine, A]: Semigroup[OneAnd[F, A]] = - oneAndSemigroupK[F].algebra + implicit def catsDataSemigroupForOneAnd[F[_]: MonadCombine, A]: Semigroup[OneAnd[F, A]] = + catsDataSemigroupKForOneAnd[F].algebra - implicit def oneAndReducible[F[_]](implicit F: Foldable[F]): Reducible[OneAnd[F, ?]] = + implicit def catsDataReducibleForOneAnd[F[_]](implicit F: Foldable[F]): Reducible[OneAnd[F, ?]] = new NonEmptyReducible[OneAnd[F,?], F] { override def split[A](fa: OneAnd[F,A]): (A, F[A]) = (fa.head, fa.tail) } - implicit def oneAndMonad[F[_]](implicit monad: MonadCombine[F]): Monad[OneAnd[F, ?]] = + implicit def catsDataMonadForOneAnd[F[_]](implicit monad: MonadCombine[F]): Monad[OneAnd[F, ?]] = new Monad[OneAnd[F, ?]] { override def map[A, B](fa: OneAnd[F, A])(f: A => B): OneAnd[F, B] = fa map f @@ -158,7 +158,7 @@ trait OneAndLowPriority0 { } trait OneAndLowPriority1 extends OneAndLowPriority0 { - implicit def oneAndFunctor[F[_]](implicit F: Functor[F]): Functor[OneAnd[F, ?]] = + implicit def catsDataFunctorForOneAnd[F[_]](implicit F: Functor[F]): Functor[OneAnd[F, ?]] = new Functor[OneAnd[F, ?]] { def map[A, B](fa: OneAnd[F, A])(f: A => B): OneAnd[F, B] = fa map f @@ -167,7 +167,7 @@ trait OneAndLowPriority1 extends OneAndLowPriority0 { } trait OneAndLowPriority2 extends OneAndLowPriority1 { - implicit def oneAndTraverse[F[_]](implicit F: Traverse[F]): Traverse[OneAnd[F, ?]] = + implicit def catsDataTraverseForOneAnd[F[_]](implicit F: Traverse[F]): Traverse[OneAnd[F, ?]] = new Traverse[OneAnd[F, ?]] { def traverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[OneAnd[F, B]] = { G.map2Eval(f(fa.head), Always(F.traverse(fa.tail)(f)))(OneAnd(_, _)).value diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index 6db53b4243..91575a4510 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -133,14 +133,14 @@ object OptionT extends OptionTInstances { } private[data] sealed trait OptionTInstances1 { - implicit def optionTFunctor[F[_]:Functor]: Functor[OptionT[F, ?]] = + implicit def catsDataFunctorForOptionT[F[_]:Functor]: Functor[OptionT[F, ?]] = new Functor[OptionT[F, ?]] { override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] = fa.map(f) } // do NOT change this to val! I know it looks like it should work, and really I agree, but it doesn't (for... reasons) - implicit def optionTTransLift: TransLift.Aux[OptionT, Functor] = + implicit def catsDataTransLiftForOptionT: TransLift.Aux[OptionT, Functor] = new TransLift[OptionT] { type TC[M[_]] = Functor[M] @@ -150,7 +150,7 @@ private[data] sealed trait OptionTInstances1 { private[data] sealed trait OptionTInstances extends OptionTInstances1 { - implicit def optionTMonad[F[_]](implicit F: Monad[F]): Monad[OptionT[F, ?]] = + implicit def catsDataMonadForOptionT[F[_]](implicit F: Monad[F]): Monad[OptionT[F, ?]] = new Monad[OptionT[F, ?]] { def pure[A](a: A): OptionT[F, A] = OptionT.pure(a) @@ -161,9 +161,9 @@ private[data] sealed trait OptionTInstances extends OptionTInstances1 { fa.map(f) } - implicit def optionTEq[F[_], A](implicit FA: Eq[F[Option[A]]]): Eq[OptionT[F, A]] = + implicit def catsDataEqForOptionT[F[_], A](implicit FA: Eq[F[Option[A]]]): Eq[OptionT[F, A]] = FA.on(_.value) - implicit def optionTShow[F[_], A](implicit F: Show[F[Option[A]]]): Show[OptionT[F, A]] = + implicit def catsDataShowForOptionT[F[_], A](implicit F: Show[F[Option[A]]]): Show[OptionT[F, A]] = functor.Contravariant[Show].contramap(F)(_.value) } diff --git a/core/src/main/scala/cats/data/Prod.scala b/core/src/main/scala/cats/data/Prod.scala index 202c49a234..2db91594c8 100644 --- a/core/src/main/scala/cats/data/Prod.scala +++ b/core/src/main/scala/cats/data/Prod.scala @@ -11,47 +11,47 @@ final case class Prod[F[_], G[_], A](first: F[A], second: G[A]) object Prod extends ProdInstances private[data] sealed abstract class ProdInstances extends ProdInstances0 { - implicit def prodAlternative[F[_], G[_]](implicit FF: Alternative[F], GG: Alternative[G]): Alternative[Lambda[X => Prod[F, G, X]]] = new ProdAlternative[F, G] { + implicit def catsDataAlternativeForProd[F[_], G[_]](implicit FF: Alternative[F], GG: Alternative[G]): Alternative[Lambda[X => Prod[F, G, X]]] = new ProdAlternative[F, G] { def F: Alternative[F] = FF def G: Alternative[G] = GG } - implicit def prodEq[F[_], G[_], A](implicit FF: Eq[F[A]], GG: Eq[G[A]]): Eq[Prod[F, G, A]] = new Eq[Prod[F, G, A]] { + implicit def catsDataEqForProd[F[_], G[_], A](implicit FF: Eq[F[A]], GG: Eq[G[A]]): Eq[Prod[F, G, A]] = new Eq[Prod[F, G, A]] { def eqv(x: Prod[F, G, A], y: Prod[F, G, A]): Boolean = FF.eqv(x.first, y.first) && GG.eqv(x.second, y.second) } } private[data] sealed abstract class ProdInstances0 extends ProdInstances1 { - implicit def prodMonoidK[F[_], G[_]](implicit FF: MonoidK[F], GG: MonoidK[G]): MonoidK[Lambda[X => Prod[F, G, X]]] = new ProdMonoidK[F, G] { + implicit def catsDataMonoidKForProd[F[_], G[_]](implicit FF: MonoidK[F], GG: MonoidK[G]): MonoidK[Lambda[X => Prod[F, G, X]]] = new ProdMonoidK[F, G] { def F: MonoidK[F] = FF def G: MonoidK[G] = GG } } private[data] sealed abstract class ProdInstances1 extends ProdInstances2 { - implicit def prodSemigroupK[F[_], G[_]](implicit FF: SemigroupK[F], GG: SemigroupK[G]): SemigroupK[Lambda[X => Prod[F, G, X]]] = new ProdSemigroupK[F, G] { + implicit def catsDataSemigroupKForProd[F[_], G[_]](implicit FF: SemigroupK[F], GG: SemigroupK[G]): SemigroupK[Lambda[X => Prod[F, G, X]]] = new ProdSemigroupK[F, G] { def F: SemigroupK[F] = FF def G: SemigroupK[G] = GG } } private[data] sealed abstract class ProdInstances2 extends ProdInstances3 { - implicit def prodApplicative[F[_], G[_]](implicit FF: Applicative[F], GG: Applicative[G]): Applicative[Lambda[X => Prod[F, G, X]]] = new ProdApplicative[F, G] { + implicit def catsDataApplicativeForProd[F[_], G[_]](implicit FF: Applicative[F], GG: Applicative[G]): Applicative[Lambda[X => Prod[F, G, X]]] = new ProdApplicative[F, G] { def F: Applicative[F] = FF def G: Applicative[G] = GG } } private[data] sealed abstract class ProdInstances3 extends ProdInstances4 { - implicit def prodApply[F[_], G[_]](implicit FF: Apply[F], GG: Apply[G]): Apply[Lambda[X => Prod[F, G, X]]] = new ProdApply[F, G] { + implicit def catsDataApplyForProd[F[_], G[_]](implicit FF: Apply[F], GG: Apply[G]): Apply[Lambda[X => Prod[F, G, X]]] = new ProdApply[F, G] { def F: Apply[F] = FF def G: Apply[G] = GG } } private[data] sealed abstract class ProdInstances4 { - implicit def prodFunctor[F[_], G[_]](implicit FF: Functor[F], GG: Functor[G]): Functor[Lambda[X => Prod[F, G, X]]] = new ProdFunctor[F, G] { + implicit def catsDataFunctorForProd[F[_], G[_]](implicit FF: Functor[F], GG: Functor[G]): Functor[Lambda[X => Prod[F, G, X]]] = new ProdFunctor[F, G] { def F: Functor[F] = FF def G: Functor[G] = GG } diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index f3a2f2b3f4..e8f1406ac0 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -136,7 +136,7 @@ object StateT extends StateTInstances { } private[data] sealed abstract class StateTInstances { - implicit def stateTMonadState[F[_], S](implicit F: Monad[F]): MonadState[StateT[F, S, ?], S] = + implicit def catsDataMonadStateForStateT[F[_], S](implicit F: Monad[F]): MonadState[StateT[F, S, ?], S] = new MonadState[StateT[F, S, ?], S] { def pure[A](a: A): StateT[F, S, A] = StateT.pure(a) @@ -152,7 +152,7 @@ private[data] sealed abstract class StateTInstances { fa.map(f) } - implicit def stateTLift[S]: TransLift.Aux[StateT[?[_], S, ?], Applicative] = + implicit def catsDataLiftForStateT[S]: TransLift.Aux[StateT[?[_], S, ?], Applicative] = new TransLift[StateT[?[_], S, ?]] { type TC[M[_]] = Applicative[M] diff --git a/core/src/main/scala/cats/data/Validated.scala b/core/src/main/scala/cats/data/Validated.scala index 6a26c710ed..0d52616c4a 100644 --- a/core/src/main/scala/cats/data/Validated.scala +++ b/core/src/main/scala/cats/data/Validated.scala @@ -225,7 +225,7 @@ object Validated extends ValidatedInstances with ValidatedFunctions{ private[data] sealed abstract class ValidatedInstances extends ValidatedInstances1 { - implicit def validatedSemigroupK[A](implicit A: Semigroup[A]): SemigroupK[Validated[A,?]] = + implicit def catsDataSemigroupKForValidated[A](implicit A: Semigroup[A]): SemigroupK[Validated[A,?]] = new SemigroupK[Validated[A,?]] { def combineK[B](x: Validated[A,B], y: Validated[A,B]): Validated[A,B] = x match { case v @ Valid(_) => v @@ -236,22 +236,22 @@ private[data] sealed abstract class ValidatedInstances extends ValidatedInstance } } - implicit def validatedMonoid[A, B](implicit A: Semigroup[A], B: Monoid[B]): Monoid[Validated[A, B]] = new Monoid[Validated[A, B]] { + implicit def catsDataMonoidForValidated[A, B](implicit A: Semigroup[A], B: Monoid[B]): Monoid[Validated[A, B]] = new Monoid[Validated[A, B]] { def empty: Validated[A, B] = Valid(B.empty) def combine(x: Validated[A, B], y: Validated[A, B]): Validated[A, B] = x combine y } - implicit def validatedOrder[A: Order, B: Order]: Order[Validated[A,B]] = new Order[Validated[A,B]] { + implicit def catsDataOrderForValidated[A: Order, B: Order]: Order[Validated[A,B]] = new Order[Validated[A,B]] { def compare(x: Validated[A,B], y: Validated[A,B]): Int = x compare y override def partialCompare(x: Validated[A,B], y: Validated[A,B]): Double = x partialCompare y override def eqv(x: Validated[A,B], y: Validated[A,B]): Boolean = x === y } - implicit def validatedShow[A, B](implicit A: Show[A], B: Show[B]): Show[Validated[A,B]] = new Show[Validated[A,B]] { + implicit def catsDataShowForValidated[A, B](implicit A: Show[A], B: Show[B]): Show[Validated[A,B]] = new Show[Validated[A,B]] { def show(f: Validated[A,B]): String = f.show } - implicit val validatedBitraverse: Bitraverse[Validated] = + implicit val catsDataBitraverseForValidated: Bitraverse[Validated] = new Bitraverse[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 { @@ -278,7 +278,7 @@ private[data] sealed abstract class ValidatedInstances extends ValidatedInstance fab.leftMap(f) } - implicit def validatedInstances[E](implicit E: Semigroup[E]): Traverse[Validated[E, ?]] with ApplicativeError[Validated[E, ?], E] = + implicit def catsDataInstancesForValidated[E](implicit E: Semigroup[E]): Traverse[Validated[E, ?]] with ApplicativeError[Validated[E, ?], E] = new Traverse[Validated[E, ?]] with ApplicativeError[Validated[E, ?], E] { def traverse[F[_]: Applicative, A, B](fa: Validated[E,A])(f: A => F[B]): F[Validated[E,B]] = fa.traverse(f) diff --git a/core/src/main/scala/cats/data/WriterT.scala b/core/src/main/scala/cats/data/WriterT.scala index 2c89f43dba..9df6c5013a 100644 --- a/core/src/main/scala/cats/data/WriterT.scala +++ b/core/src/main/scala/cats/data/WriterT.scala @@ -52,19 +52,19 @@ object WriterT extends WriterTInstances with WriterTFunctions private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { - implicit def writerTIdMonad[L:Monoid]: Monad[WriterT[Id, L, ?]] = - writerTMonadWriter[Id, L] + implicit def catsDataMonadForWriterTId[L:Monoid]: Monad[WriterT[Id, L, ?]] = + catsDataMonadWriterForWriterT[Id, L] - implicit def writerTIdEq[L: Eq, V: Eq]: Eq[WriterT[Id, L, V]] = - writerTEq[Id, L, V] + implicit def catsDataEqForWriterTId[L: Eq, V: Eq]: Eq[WriterT[Id, L, V]] = + catsDataEqForWriterT[Id, L, V] - implicit def writerTBifunctor[F[_]:Functor]: Bifunctor[WriterT[F, ?, ?]] = + implicit def catsDataBifunctorForWriterT[F[_]:Functor]: Bifunctor[WriterT[F, ?, ?]] = new Bifunctor[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) } - implicit def writerTTransLift[W](implicit W: Monoid[W]): TransLift.Aux[WriterT[?[_], W, ?], Functor] = + implicit def catsDataTransLiftForWriterT[W](implicit W: Monoid[W]): TransLift.Aux[WriterT[?[_], W, ?], Functor] = new TransLift[WriterT[?[_], W, ?]] { type TC[M[_]] = Functor[M] @@ -72,62 +72,63 @@ private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { WriterT(Functor[M].map(ma)((W.empty, _))) } - implicit def writerTShow[F[_], L, V](implicit F: Show[F[(L, V)]]): Show[WriterT[F, L, V]] = new Show[WriterT[F, L, V]] { + implicit def catsDataShowForWriterT[F[_], L, V](implicit F: Show[F[(L, V)]]): Show[WriterT[F, L, V]] = new Show[WriterT[F, L, V]] { override def show(f: WriterT[F, L, V]): String = f.show } - implicit def writerTIdMonoid[L:Monoid, V:Monoid]: Monoid[WriterT[Id, L, V]] = - writerTMonoid[Id, L, V] + implicit def catsDataMonoidForWriterTId[L:Monoid, V:Monoid]: Monoid[WriterT[Id, L, V]] = + catsDataMonoidForWriterT[Id, L, V] } private[data] sealed abstract class WriterTInstances0 extends WriterTInstances1 { - implicit def writerTMonadCombine[F[_], L](implicit F: MonadCombine[F], L: Monoid[L]): MonadCombine[WriterT[F, L, ?]] = + implicit def catsDataMonadCombineForWriterT[F[_], L](implicit F: MonadCombine[F], L: Monoid[L]): MonadCombine[WriterT[F, L, ?]] = new WriterTMonadCombine[F, L] { implicit val F0: MonadCombine[F] = F implicit val L0: Monoid[L] = L } - implicit def writerTIdFlatMap[L:Semigroup]: FlatMap[WriterT[Id, L, ?]] = - writerTFlatMap[Id, L] + implicit def catsDataFlatMapForWriterTId[L:Semigroup]: FlatMap[WriterT[Id, L, ?]] = + catsDataFlatMapForWriterT[Id, L] - implicit def writerTEq[F[_], L, V](implicit F: Eq[F[(L, V)]]): Eq[WriterT[F, L, V]] = + implicit def catsDataEqForWriterT[F[_], L, V](implicit F: Eq[F[(L, V)]]): Eq[WriterT[F, L, V]] = F.on(_.run) - implicit def writerTIdSemigroup[L:Semigroup, V:Semigroup]: Semigroup[WriterT[Id, L, V]] = - writerTSemigroup[Id, L, V] + implicit def catsDataSemigroupForWriterTId[L:Semigroup, V:Semigroup]: Semigroup[WriterT[Id, L, V]] = + catsDataSemigroupForWriterT[Id, L, V] } private[data] sealed abstract class WriterTInstances1 extends WriterTInstances2 { - implicit def writerTMonadFilter[F[_], L](implicit F: MonadFilter[F], L: Monoid[L]): MonadFilter[WriterT[F, L, ?]] = + implicit def catsDataMonadFilterForWriterT[F[_], L](implicit F: MonadFilter[F], L: Monoid[L]): MonadFilter[WriterT[F, L, ?]] = new WriterTMonadFilter[F, L] { implicit val F0: MonadFilter[F] = F implicit val L0: Monoid[L] = L } - implicit def writerTMonoid[F[_], L, V](implicit W: Monoid[F[(L, V)]]): Monoid[WriterT[F, L, V]] = + implicit def catsDataMonoidForWriterT[F[_], L, V](implicit W: Monoid[F[(L, V)]]): Monoid[WriterT[F, L, V]] = new WriterTMonoid[F, L, V] { implicit val F0: Monoid[F[(L, V)]] = W } - implicit def writerTIdCoflatMap[L]: CoflatMap[WriterT[Id, L, ?]] = - writerTCoflatMap[Id, L] + implicit def catsDataCoflatMapForWriterTId[L]: CoflatMap[WriterT[Id, L, ?]] = + catsDataCoflatMapForWriterT[Id, L] + } private[data] sealed abstract class WriterTInstances2 extends WriterTInstances3 { - implicit def writerTMonadWriter[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadWriter[WriterT[F, L, ?], L] = + implicit def catsDataMonadWriterForWriterT[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadWriter[WriterT[F, L, ?], L] = new WriterTMonadWriter[F, L] { implicit val F0: Monad[F] = F implicit val L0: Monoid[L] = L } - implicit def writerTSemigroup[F[_], L, V](implicit W: Semigroup[F[(L, V)]]): Semigroup[WriterT[F, L, V]] = + implicit def catsDataSemigroupForWriterT[F[_], L, V](implicit W: Semigroup[F[(L, V)]]): Semigroup[WriterT[F, L, V]] = new WriterTSemigroup[F, L, V] { implicit val F0: Semigroup[F[(L, V)]] = W } } private[data] sealed abstract class WriterTInstances3 extends WriterTInstances4 { - implicit def writerTAlternative[F[_], L](implicit F: Alternative[F], L: Monoid[L]): Alternative[WriterT[F, L, ?]] = + implicit def catsDataAlternativeForWriterT[F[_], L](implicit F: Alternative[F], L: Monoid[L]): Alternative[WriterT[F, L, ?]] = new WriterTAlternative[F, L] { implicit val F0: Alternative[F] = F implicit val L0: Monoid[L] = L @@ -136,34 +137,33 @@ private[data] sealed abstract class WriterTInstances3 extends WriterTInstances4 } private[data] sealed abstract class WriterTInstances4 extends WriterTInstances5 { - implicit def writerTApplicative[F[_], L](implicit F: Applicative[F], L: Monoid[L]): Applicative[WriterT[F, L, ?]] = + implicit def catsDataApplicativeForWriterT[F[_], L](implicit F: Applicative[F], L: Monoid[L]): Applicative[WriterT[F, L, ?]] = new WriterTApplicative[F, L] { implicit val F0: Applicative[F] = F implicit val L0: Monoid[L] = L } - implicit def writerTMonoidK[F[_], L](implicit F: MonoidK[F]): MonoidK[WriterT[F, L, ?]] = + implicit def catsDataMonoidKForWriterT[F[_], L](implicit F: MonoidK[F]): MonoidK[WriterT[F, L, ?]] = new WriterTMonoidK[F, L] { implicit val F0: MonoidK[F] = F } } private[data] sealed abstract class WriterTInstances5 extends WriterTInstances6 { - implicit def writerTFlatMap[F[_], L](implicit F: FlatMap[F], L: Semigroup[L]): FlatMap[WriterT[F, L, ?]] = + implicit def catsDataFlatMapForWriterT[F[_], L](implicit F: FlatMap[F], L: Semigroup[L]): FlatMap[WriterT[F, L, ?]] = new WriterTFlatMap[F, L] { implicit val F0: FlatMap[F] = F implicit val L0: Semigroup[L] = L } - implicit def writerTSemigroupK[F[_], L](implicit F: SemigroupK[F]): SemigroupK[WriterT[F, L, ?]] = + implicit def catsDataSemigroupKForWriterT[F[_], L](implicit F: SemigroupK[F]): SemigroupK[WriterT[F, L, ?]] = new WriterTSemigroupK[F, L] { implicit val F0: SemigroupK[F] = F } } private[data] sealed abstract class WriterTInstances6 extends WriterTInstances7 { - - implicit def writerTApply[F[_], L](implicit F: Apply[F], L: Semigroup[L]): Apply[WriterT[F, L, ?]] = + implicit def catsDataApplyForWriterT[F[_], L](implicit F: Apply[F], L: Semigroup[L]): Apply[WriterT[F, L, ?]] = new WriterTApply[F, L] { implicit val F0: Apply[F] = F implicit val L0: Semigroup[L] = L @@ -172,10 +172,10 @@ private[data] sealed abstract class WriterTInstances6 extends WriterTInstances7 private[data] sealed abstract class WriterTInstances7 { - implicit def writerTCoflatMap[F[_], L](implicit F: Functor[F]): CoflatMap[WriterT[F, L, ?]] = + implicit def catsDataCoflatMapForWriterT[F[_], L](implicit F: Functor[F]): CoflatMap[WriterT[F, L, ?]] = new WriterTCoflatMap[F, L] { implicit val F0: Functor[F] = F - } + } } private[data] sealed trait WriterTFunctor[F[_], L] extends Functor[WriterT[F, L, ?]] { diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index 427fd011c1..d8a6a4a2b3 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -186,25 +186,25 @@ object Xor extends XorInstances with XorFunctions { } private[data] sealed abstract class XorInstances extends XorInstances1 { - implicit def xorOrder[A: Order, B: Order]: Order[A Xor B] = + implicit def catsDataOrderForXor[A: Order, B: Order]: Order[A Xor B] = new Order[A Xor B] { def compare(x: A Xor B, y: A Xor B): Int = x compare y override def partialCompare(x: A Xor B, y: A Xor B): Double = x partialCompare y override def eqv(x: A Xor B, y: A Xor B): Boolean = x === y } - implicit def xorShow[A, B](implicit A: Show[A], B: Show[B]): Show[A Xor B] = + implicit def catsDataShowForXor[A, B](implicit A: Show[A], B: Show[B]): Show[A Xor B] = new Show[A Xor B] { def show(f: A Xor B): String = f.show } - implicit def xorMonoid[A, B](implicit B: Monoid[B]): Monoid[A Xor B] = + implicit def catsDataMonoidForXor[A, B](implicit B: Monoid[B]): Monoid[A Xor B] = new Monoid[A Xor B] { def empty: A Xor B = Xor.Right(B.empty) def combine(x: A Xor B, y: A Xor B): A Xor B = x combine y } - implicit def xorSemigroupK[L]: SemigroupK[Xor[L,?]] = + implicit def catsDataSemigroupKForXor[L]: SemigroupK[Xor[L,?]] = new SemigroupK[Xor[L,?]] { def combineK[A](x: Xor[L,A], y: Xor[L,A]): Xor[L,A] = x match { case Xor.Left(_) => y @@ -212,7 +212,7 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { } } - implicit val xorBitraverse: Bitraverse[Xor] = + implicit val catsDataBitraverseForXor: Bitraverse[Xor] = new Bitraverse[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 { @@ -233,7 +233,7 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { } } - implicit def xorInstances[A]: Traverse[A Xor ?] with MonadError[Xor[A, ?], A] = + implicit def catsDataInstancesForXor[A]: Traverse[A Xor ?] with MonadError[Xor[A, ?], A] = new Traverse[A Xor ?] with MonadError[Xor[A, ?], A] { def traverse[F[_]: Applicative, B, C](fa: A Xor B)(f: B => F[C]): F[A Xor C] = fa.traverse(f) def foldLeft[B, C](fa: A Xor B, c: C)(f: (C, B) => C): C = fa.foldLeft(c)(f) @@ -259,19 +259,19 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { private[data] sealed abstract class XorInstances1 extends XorInstances2 { - implicit def xorSemigroup[A, B](implicit B: Semigroup[B]): Semigroup[A Xor B] = + implicit def catsDataSemigroupForXor[A, B](implicit B: Semigroup[B]): Semigroup[A Xor B] = new Semigroup[A Xor B] { def combine(x: A Xor B, y: A Xor B): A Xor B = x combine y } - implicit def xorPartialOrder[A: PartialOrder, B: PartialOrder]: PartialOrder[A Xor B] = new PartialOrder[A Xor B] { + implicit def catsDataPartialOrderForXor[A: PartialOrder, B: PartialOrder]: PartialOrder[A Xor B] = new PartialOrder[A Xor B] { def partialCompare(x: A Xor B, y: A Xor B): Double = x partialCompare y override def eqv(x: A Xor B, y: A Xor B): Boolean = x === y } } private[data] sealed abstract class XorInstances2 { - implicit def xorEq[A: Eq, B: Eq]: Eq[A Xor B] = + implicit def catsDataEqForXor[A: Eq, B: Eq]: Eq[A Xor B] = new Eq[A Xor B] { def eqv(x: A Xor B, y: A Xor B): Boolean = x === y } diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index 2fdf95282e..555f9c1b39 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -218,27 +218,27 @@ trait XorTFunctions { private[data] abstract class XorTInstances extends XorTInstances1 { /* TODO violates right absorbtion, right distributivity, and left distributivity -- re-enable when MonadCombine laws are split in to weak/strong - implicit def xorTMonadCombine[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadCombine[XorT[F, L, ?]] = { + implicit def catsDataMonadCombineForXorT[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadCombine[XorT[F, L, ?]] = { implicit val F0 = F implicit val L0 = L new XorTMonadCombine[F, L] { implicit val F = F0; implicit val L = L0 } } */ - implicit def xorTOrder[F[_], L, R](implicit F: Order[F[L Xor R]]): Order[XorT[F, L, R]] = + implicit def catsDataOrderForXorT[F[_], L, R](implicit F: Order[F[L Xor R]]): Order[XorT[F, L, R]] = new XorTOrder[F, L, R] { val F0: Order[F[L Xor R]] = F } - implicit def xorTShow[F[_], L, R](implicit sh: Show[F[L Xor R]]): Show[XorT[F, L, R]] = + implicit def catsDataShowForXorT[F[_], L, R](implicit sh: Show[F[L Xor R]]): Show[XorT[F, L, R]] = functor.Contravariant[Show].contramap(sh)(_.value) - implicit def xorTBifunctor[F[_]](implicit F: Functor[F]): Bifunctor[XorT[F, ?, ?]] = + implicit def catsDataBifunctorForXorT[F[_]](implicit F: Functor[F]): Bifunctor[XorT[F, ?, ?]] = new Bifunctor[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) } - implicit def xorTTraverse[F[_], L](implicit F: Traverse[F]): Traverse[XorT[F, L, ?]] = + implicit def catsDataTraverseForXorT[F[_], L](implicit F: Traverse[F]): Traverse[XorT[F, L, ?]] = new XorTTraverse[F, L] { val F0: Traverse[F] = F } @@ -255,36 +255,36 @@ private[data] abstract class XorTInstances extends XorTInstances1 { private[data] abstract class XorTInstances1 extends XorTInstances2 { /* TODO violates monadFilter right empty law -- re-enable when MonadFilter laws are split in to weak/strong - implicit def xorTMonadFilter[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadFilter[XorT[F, L, ?]] = { + implicit def catsDataMonadFilterForXorT[F[_], L](implicit F: Monad[F], L: Monoid[L]): MonadFilter[XorT[F, L, ?]] = { implicit val F0 = F implicit val L0 = L new XorTMonadFilter[F, L] { implicit val F = F0; implicit val L = L0 } } */ - implicit def xorTFoldable[F[_], L](implicit F: Foldable[F]): Foldable[XorT[F, L, ?]] = + implicit def catsDataFoldableForXorT[F[_], L](implicit F: Foldable[F]): Foldable[XorT[F, L, ?]] = new XorTFoldable[F, L] { val F0: Foldable[F] = F } - implicit def xorTPartialOrder[F[_], L, R](implicit F: PartialOrder[F[L Xor R]]): PartialOrder[XorT[F, L, R]] = + implicit def catsDataPartialOrderForXorT[F[_], L, R](implicit F: PartialOrder[F[L Xor R]]): PartialOrder[XorT[F, L, R]] = new XorTPartialOrder[F, L, R] { val F0: PartialOrder[F[L Xor R]] = F } - implicit def xorTBitraverse[F[_]](implicit F: Traverse[F]): Bitraverse[XorT[F, ?, ?]] = + implicit def catsDataBitraverseForXorT[F[_]](implicit F: Traverse[F]): Bitraverse[XorT[F, ?, ?]] = new XorTBitraverse[F] { val F0: Traverse[F] = F } } private[data] abstract class XorTInstances2 extends XorTInstances3 { - implicit def xorTMonadError[F[_], L](implicit F: Monad[F]): MonadError[XorT[F, L, ?], L] = { + implicit def catsDataMonadErrorForXorT[F[_], L](implicit F: Monad[F]): MonadError[XorT[F, L, ?], L] = { implicit val F0 = F new XorTMonadError[F, L] { implicit val F = F0 } } - implicit def xorTSemigroupK[F[_], L](implicit F: Monad[F]): SemigroupK[XorT[F, L, ?]] = + implicit def catsDataSemigroupKForXorT[F[_], L](implicit F: Monad[F]): SemigroupK[XorT[F, L, ?]] = new SemigroupK[XorT[F,L,?]] { def combineK[A](x: XorT[F,L,A], y: XorT[F, L, A]): XorT[F, L, A] = XorT(F.flatMap(x.value) { @@ -293,14 +293,14 @@ private[data] abstract class XorTInstances2 extends XorTInstances3 { }) } - implicit def xorTEq[F[_], L, R](implicit F: Eq[F[L Xor R]]): Eq[XorT[F, L, R]] = + implicit def catsDataEqForXorT[F[_], L, R](implicit F: Eq[F[L Xor R]]): Eq[XorT[F, L, R]] = new XorTEq[F, L, R] { val F0: Eq[F[L Xor R]] = F } } private[data] abstract class XorTInstances3 { - implicit def xorTFunctor[F[_], L](implicit F: Functor[F]): Functor[XorT[F, L, ?]] = { + implicit def catsDataFunctorForXorT[F[_], L](implicit F: Functor[F]): Functor[XorT[F, L, ?]] = { implicit val F0 = F new XorTFunctor[F, L] { implicit val F = F0 } } diff --git a/tests/src/test/scala/cats/tests/CokleisliTests.scala b/tests/src/test/scala/cats/tests/CokleisliTests.scala index 084a23ec52..a158f1aeef 100644 --- a/tests/src/test/scala/cats/tests/CokleisliTests.scala +++ b/tests/src/test/scala/cats/tests/CokleisliTests.scala @@ -57,13 +57,13 @@ class CokleisliTests extends SlowCatsSuite { cokleisliEqE[NonEmptyList, A](oneAndArbitrary, Eq[A]) { - implicit val cokleisliMonoidK = Cokleisli.cokleisliMonoidK[NonEmptyList] + implicit val cokleisliMonoidK = Cokleisli.catsDataMonoidKForCokleisli[NonEmptyList] checkAll("Cokleisli[NonEmptyList, Int, Int]", MonoidKTests[CokleisliNELE].monoidK[Int]) checkAll("MonoidK[Lambda[A => Cokleisli[NonEmptyList, A, A]]]", SerializableTests.serializable(cokleisliMonoidK)) } { - implicit val cokleisliSemigroupK = Cokleisli.cokleisliSemigroupK[NonEmptyList] + implicit val cokleisliSemigroupK = Cokleisli.catsDataSemigroupKForCokleisli[NonEmptyList] checkAll("Cokleisli[NonEmptyList, Int, Int]", SemigroupKTests[CokleisliNELE].semigroupK[Int]) checkAll("SemigroupK[Lambda[A => Cokleisli[NonEmptyList, A, A]]]", SerializableTests.serializable(cokleisliSemigroupK)) } diff --git a/tests/src/test/scala/cats/tests/ConstTests.scala b/tests/src/test/scala/cats/tests/ConstTests.scala index 1b487575ea..fafce196bc 100644 --- a/tests/src/test/scala/cats/tests/ConstTests.scala +++ b/tests/src/test/scala/cats/tests/ConstTests.scala @@ -10,7 +10,7 @@ import cats.laws.discipline.arbitrary.{constArbitrary, oneAndArbitrary} class ConstTests extends CatsSuite { - implicit val iso = CartesianTests.Isomorphisms.invariant[Const[String, ?]](Const.constTraverse) + implicit val iso = CartesianTests.Isomorphisms.invariant[Const[String, ?]](Const.catsDataTraverseForConst) checkAll("Const[String, Int]", CartesianTests[Const[String, ?]].cartesian[Int, Int, Int]) checkAll("Cartesian[Const[String, ?]]", SerializableTests.serializable(Cartesian[Const[String, ?]])) @@ -24,7 +24,7 @@ class ConstTests extends CatsSuite { // Get Apply[Const[C : Semigroup, ?]], not Applicative[Const[C : Monoid, ?]] { implicit def nonEmptyListSemigroup[A]: Semigroup[NonEmptyList[A]] = SemigroupK[NonEmptyList].algebra - implicit val iso = CartesianTests.Isomorphisms.invariant[Const[NonEmptyList[String], ?]](Const.constContravariant) + implicit val iso = CartesianTests.Isomorphisms.invariant[Const[NonEmptyList[String], ?]](Const.catsDataContravariantForConst) checkAll("Apply[Const[NonEmptyList[String], Int]]", ApplyTests[Const[NonEmptyList[String], ?]].apply[Int, Int, Int]) checkAll("Apply[Const[NonEmptyList[String], ?]]", SerializableTests.serializable(Apply[Const[NonEmptyList[String], ?]])) } diff --git a/tests/src/test/scala/cats/tests/CoproductTests.scala b/tests/src/test/scala/cats/tests/CoproductTests.scala index 6c0279efd2..ff6a40e324 100644 --- a/tests/src/test/scala/cats/tests/CoproductTests.scala +++ b/tests/src/test/scala/cats/tests/CoproductTests.scala @@ -14,7 +14,7 @@ class CoproductTests extends CatsSuite { checkAll("Traverse[Coproduct[Option, Option, ?]]", SerializableTests.serializable(Traverse[Coproduct[Option, Option, ?]])) { - implicit val foldable = Coproduct.coproductFoldable[Option, Option] + implicit val foldable = Coproduct.catsDataFoldableForCoproduct[Option, Option] checkAll("Coproduct[Option, Option, ?]", FoldableTests[Coproduct[Option, Option, ?]].foldable[Int, Int]) checkAll("Foldable[Coproduct[Option, Option, ?]]", SerializableTests.serializable(Foldable[Coproduct[Option, Option, ?]])) } @@ -23,7 +23,7 @@ class CoproductTests extends CatsSuite { checkAll("Comonad[Coproduct[Eval, Eval, ?]]", SerializableTests.serializable(Comonad[Coproduct[Eval, Eval, ?]])) { - implicit val coflatMap = Coproduct.coproductCoflatMap[Eval, Eval] + implicit val coflatMap = Coproduct.catsDataCoflatMapForCoproduct[Eval, Eval] checkAll("Coproduct[Eval, Eval, ?]", CoflatMapTests[Coproduct[Eval, Eval, ?]].coflatMap[Int, Int, Int]) checkAll("CoflatMap[Coproduct[Eval, Eval, ?]]", SerializableTests.serializable(CoflatMap[Coproduct[Eval, Eval, ?]])) } diff --git a/tests/src/test/scala/cats/tests/FuncTests.scala b/tests/src/test/scala/cats/tests/FuncTests.scala index 0a2083bf7c..fe1216e03a 100644 --- a/tests/src/test/scala/cats/tests/FuncTests.scala +++ b/tests/src/test/scala/cats/tests/FuncTests.scala @@ -20,19 +20,19 @@ class FuncTests extends CatsSuite { checkAll("Cartesian[Func[Option, Int, ?]]", SerializableTests.serializable(Cartesian[Func[Option, Int, ?]])) { - implicit val funcApp = Func.funcApplicative[Option, Int] + implicit val catsDataApplicativeForFunc = Func.catsDataApplicativeForFunc[Option, Int] checkAll("Func[Option, Int, Int]", ApplicativeTests[Func[Option, Int, ?]].applicative[Int, Int, Int]) checkAll("Applicative[Func[Option, Int, ?]]", SerializableTests.serializable(Applicative[Func[Option, Int, ?]])) } { - implicit val funcApply = Func.funcApply[Option, Int] + implicit val catsDataApplyForFunc = Func.catsDataApplyForFunc[Option, Int] checkAll("Func[Option, Int, Int]", ApplyTests[Func[Option, Int, ?]].apply[Int, Int, Int]) checkAll("Apply[Func[Option, Int, ?]]", SerializableTests.serializable(Apply[Func[Option, Int, ?]])) } { - implicit val funcFunctor = Func.funcFunctor[Option, Int] + implicit val catsDataFunctorForFunc = Func.catsDataFunctorForFunc[Option, Int] checkAll("Func[Option, Int, Int]", FunctorTests[Func[Option, Int, ?]].functor[Int, Int, Int]) checkAll("Functor[Func[Option, Int, ?]]", SerializableTests.serializable(Functor[Func[Option, Int, ?]])) } diff --git a/tests/src/test/scala/cats/tests/KleisliTests.scala b/tests/src/test/scala/cats/tests/KleisliTests.scala index a081346975..bcfd7f1ba5 100644 --- a/tests/src/test/scala/cats/tests/KleisliTests.scala +++ b/tests/src/test/scala/cats/tests/KleisliTests.scala @@ -15,7 +15,7 @@ class KleisliTests extends CatsSuite { implicit def kleisliEq[F[_], A, B](implicit A: Arbitrary[A], FB: Eq[F[B]]): Eq[Kleisli[F, A, B]] = Eq.by[Kleisli[F, A, B], A => F[B]](_.run) - implicit val xorTEq = XorT.xorTEq[Kleisli[Option, Int, ?], Unit, Int] + implicit val xorTEq = XorT.catsDataEqForXorT[Kleisli[Option, Int, ?], Unit, Int] implicit val iso = CartesianTests.Isomorphisms.invariant[Kleisli[Option, Int, ?]] @@ -26,81 +26,81 @@ class KleisliTests extends CatsSuite { checkAll("Cartesian[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Cartesian[Kleisli[Option, Int, ?]])) { - implicit val kleisliArrow = Kleisli.kleisliArrow[Option] + implicit val catsDataArrowForKleisli = Kleisli.catsDataArrowForKleisli[Option] checkAll("Kleisli[Option, Int, Int]", ArrowTests[Kleisli[Option, ?, ?]].arrow[Int, Int, Int, Int, Int, Int]) checkAll("Arrow[Kleisli[Option, ?, ?]]", SerializableTests.serializable(Arrow[Kleisli[Option, ?, ?]])) } { - implicit val kleisliChoice = Kleisli.kleisliChoice[Option] + implicit val catsDataChoiceForKleisli = Kleisli.catsDataChoiceForKleisli[Option] checkAll("Kleisli[Option, Int, Int]", ChoiceTests[Kleisli[Option, ?, ?]].choice[Int, Int, Int, Int]) checkAll("Choice[Kleisli[Option, ?, ?]]", SerializableTests.serializable(Choice[Kleisli[Option, ?, ?]])) } { - implicit val kleisliMonadReader = Kleisli.kleisliMonadReader[Option, Int] + implicit val catsDataMonadReaderForKleisli = Kleisli.catsDataMonadReaderForKleisli[Option, Int] checkAll("Kleisli[Option, Int, Int]", MonadReaderTests[Kleisli[Option, Int, ?], Int].monadReader[Int, Int, Int]) checkAll("MonadReader[Kleisli[Option, ?, ?], Int]", SerializableTests.serializable(MonadReader[Kleisli[Option, Int, ?], Int])) } { - implicit val kleisliSplit = Kleisli.kleisliSplit[Option] + implicit val kleisliSplit = Kleisli.catsDataSplitForKleisli[Option] checkAll("Kleisli[Option, Int, Int]", SplitTests[Kleisli[Option, ?, ?]].split[Int, Int, Int, Int, Int, Int]) checkAll("Split[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Split[Kleisli[Option, ?, ?]])) } { - implicit val kleisliStrong = Kleisli.kleisliStrong[Option] + implicit val catsDataStrongForKleisli = Kleisli.catsDataStrongForKleisli[Option] checkAll("Kleisli[Option, Int, Int]", StrongTests[Kleisli[Option, ?, ?]].strong[Int, Int, Int, Int, Int, Int]) checkAll("Strong[Kleisli[Option, ?, ?]]", SerializableTests.serializable(Strong[Kleisli[Option, ?, ?]])) } { - implicit val kleisliFlatMap = Kleisli.kleisliFlatMap[Option, Int] + implicit val catsDataFlatMapForKleisli = Kleisli.catsDataFlatMapForKleisli[Option, Int] checkAll("Kleisli[Option, Int, Int]", FlatMapTests[Kleisli[Option, Int, ?]].flatMap[Int, Int, Int]) checkAll("FlatMap[Kleisli[Option, Int, ?]]", SerializableTests.serializable(FlatMap[Kleisli[Option, Int, ?]])) } { - implicit val kleisliApplicative = Kleisli.kleisliApplicative[Option, Int] + implicit val catsDataApplicativeForKleisli = Kleisli.catsDataApplicativeForKleisli[Option, Int] checkAll("Kleisli[Option, Int, Int]", ApplicativeTests[Kleisli[Option, Int, ?]].applicative[Int, Int, Int]) checkAll("Applicative[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Applicative[Kleisli[Option, Int, ?]])) } { - implicit val kleisliApply = Kleisli.kleisliApply[Option, Int] + implicit val catsDataApplyForKleisli = Kleisli.catsDataApplyForKleisli[Option, Int] checkAll("Kleisli[Option, Int, Int]", ApplyTests[Kleisli[Option, Int, ?]].apply[Int, Int, Int]) checkAll("Apply[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Apply[Kleisli[Option, Int, ?]])) } { - implicit val kleisliFunctor = Kleisli.kleisliFunctor[Option, Int] + implicit val catsDataFunctorForKleisli = Kleisli.catsDataFunctorForKleisli[Option, Int] checkAll("Kleisli[Option, Int, Int]", FunctorTests[Kleisli[Option, Int, ?]].functor[Int, Int, Int]) checkAll("Functor[Kleisli[Option, Int, ?]]", SerializableTests.serializable(Functor[Kleisli[Option, Int, ?]])) } { - implicit val kleisliMonoid = Kleisli.kleisliMonoid[Option, Int, String] + implicit val catsDataMonoidForKleisli = Kleisli.catsDataMonoidForKleisli[Option, Int, String] checkAll("Kleisli[Option, Int, String]", GroupLaws[Kleisli[Option, Int, String]].monoid) - checkAll("Monoid[Kleisli[Option, Int, String]]", SerializableTests.serializable(kleisliMonoid)) + checkAll("Monoid[Kleisli[Option, Int, String]]", SerializableTests.serializable(catsDataMonoidForKleisli)) } { - implicit val kleisliSemigroup = Kleisli.kleisliSemigroup[Option, Int, String] + implicit val catsDataSemigroupForKleisli = Kleisli.catsDataSemigroupForKleisli[Option, Int, String] checkAll("Kleisli[Option, Int, String]", GroupLaws[Kleisli[Option, Int, String]].semigroup) - checkAll("Semigroup[Kleisli[Option, Int, String]]", SerializableTests.serializable(kleisliSemigroup)) + checkAll("Semigroup[Kleisli[Option, Int, String]]", SerializableTests.serializable(catsDataSemigroupForKleisli)) } { - implicit val kleisliMonoidK = Kleisli.kleisliMonoidK[Option] + implicit val catsDataMonoidKForKleisli = Kleisli.catsDataMonoidKForKleisli[Option] checkAll("Kleisli[Option, Int, Int]", MonoidKTests[Lambda[A => Kleisli[Option, A, A]]].monoidK[Int]) - checkAll("MonoidK[Lambda[A => Kleisli[Option, A, A]]]", SerializableTests.serializable(kleisliMonoidK)) + checkAll("MonoidK[Lambda[A => Kleisli[Option, A, A]]]", SerializableTests.serializable(catsDataMonoidKForKleisli)) } { - implicit val kleisliSemigroupK = Kleisli.kleisliSemigroupK[Option] + implicit val catsDataSemigroupKForKleisli = Kleisli.catsDataSemigroupKForKleisli[Option] checkAll("Kleisli[Option, Int, Int]", SemigroupKTests[Lambda[A => Kleisli[Option, A, A]]].semigroupK[Int]) - checkAll("SemigroupK[Lambda[A => Kleisli[Option, A, A]]]", SerializableTests.serializable(kleisliSemigroupK)) + checkAll("SemigroupK[Lambda[A => Kleisli[Option, A, A]]]", SerializableTests.serializable(catsDataSemigroupKForKleisli)) } checkAll("Kleisli[Option, ?, Int]", ContravariantTests[Kleisli[Option, ?, Int]].contravariant[Int, Int, Int]) diff --git a/tests/src/test/scala/cats/tests/OneAndTests.scala b/tests/src/test/scala/cats/tests/OneAndTests.scala index 453df0283f..569535205f 100644 --- a/tests/src/test/scala/cats/tests/OneAndTests.scala +++ b/tests/src/test/scala/cats/tests/OneAndTests.scala @@ -20,7 +20,7 @@ class OneAndTests extends CatsSuite { checkAll("OneAnd[List, Int]", ReducibleTests[OneAnd[List, ?]].reducible[Option, Int, Int]) checkAll("Reducible[OneAnd[List, ?]]", SerializableTests.serializable(Reducible[OneAnd[List, ?]])) - implicit val iso = CartesianTests.Isomorphisms.invariant[OneAnd[ListWrapper, ?]](OneAnd.oneAndFunctor(ListWrapper.functor)) + implicit val iso = CartesianTests.Isomorphisms.invariant[OneAnd[ListWrapper, ?]](OneAnd.catsDataFunctorForOneAnd(ListWrapper.functor)) // Test instances that have more general constraints { diff --git a/tests/src/test/scala/cats/tests/ValidatedTests.scala b/tests/src/test/scala/cats/tests/ValidatedTests.scala index ece8418ed6..22cc72fb6f 100644 --- a/tests/src/test/scala/cats/tests/ValidatedTests.scala +++ b/tests/src/test/scala/cats/tests/ValidatedTests.scala @@ -19,7 +19,7 @@ class ValidatedTests extends CatsSuite { checkAll("Validated[?, ?]", BitraverseTests[Validated].bitraverse[Option, Int, Int, Int, String, String, String]) checkAll("Bitraverse[Validated]", SerializableTests.serializable(Bitraverse[Validated])) - implicit val eq0 = XorT.xorTEq[Validated[String, ?], String, Int] + implicit val eq0 = XorT.catsDataEqForXorT[Validated[String, ?], String, Int] checkAll("Validated[String, Int]", ApplicativeErrorTests[Validated[String, ?], String].applicativeError[Int, Int, Int]) checkAll("ApplicativeError[Xor, String]", SerializableTests.serializable(ApplicativeError[Validated[String, ?], String])) diff --git a/tests/src/test/scala/cats/tests/WriterTTests.scala b/tests/src/test/scala/cats/tests/WriterTTests.scala index 55bff93d00..e567248e73 100644 --- a/tests/src/test/scala/cats/tests/WriterTTests.scala +++ b/tests/src/test/scala/cats/tests/WriterTTests.scala @@ -101,7 +101,7 @@ class WriterTTests extends CatsSuite { checkAll("Bifunctor[WriterT[ListWrapper, ?, ?]]", SerializableTests.serializable(Bifunctor[WriterT[ListWrapper, ?, ?]])) } - implicit val iso = CartesianTests.Isomorphisms.invariant[WriterT[ListWrapper, ListWrapper[Int], ?]](WriterT.writerTCoflatMap(ListWrapper.functor)) + implicit val iso = CartesianTests.Isomorphisms.invariant[WriterT[ListWrapper, ListWrapper[Int], ?]](WriterT.catsDataCoflatMapForWriterT(ListWrapper.functor)) // We have varying instances available depending on `F` and `L`. // We also battle some inference issues with `Id`. diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index 13c279a76b..b88e4c2958 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -8,8 +8,8 @@ import cats.laws.discipline.arbitrary._ import cats.kernel.laws.OrderLaws class XorTTests extends CatsSuite { - implicit val eq0 = XorT.xorTEq[List, String, String Xor Int] - implicit val eq1 = XorT.xorTEq[XorT[List, String, ?], String, Int](eq0) + implicit val eq0 = XorT.catsDataEqForXorT[List, String, String Xor Int] + implicit val eq1 = XorT.catsDataEqForXorT[XorT[List, String, ?], String, Int](eq0) implicit val iso = CartesianTests.Isomorphisms.invariant[XorT[List, String, ?]] checkAll("XorT[List, String, Int]", MonadErrorTests[XorT[List, String, ?], String].monadError[Int, Int, Int]) checkAll("MonadError[XorT[List, ?, ?]]", SerializableTests.serializable(MonadError[XorT[List, String, ?], String])) diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index ed123b545d..6afa7308e0 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -22,7 +22,7 @@ class XorTests extends CatsSuite { checkAll("Xor[String, NonEmptyList[Int]]", GroupLaws[Xor[String, NonEmptyList[Int]]].semigroup) - implicit val eq0 = XorT.xorTEq[Xor[String, ?], String, Int] + implicit val eq0 = XorT.catsDataEqForXorT[Xor[String, ?], String, Int] checkAll("Xor[String, Int]", MonadErrorTests[Xor[String, ?], String].monadError[Int, Int, Int]) checkAll("MonadError[Xor, String]", SerializableTests.serializable(MonadError[Xor[String, ?], String])) From 9459d6246a78a127dacaeeabd755655c82f60e9e Mon Sep 17 00:00:00 2001 From: Kailuo Wang Date: Fri, 27 May 2016 15:17:21 -0400 Subject: [PATCH 180/185] renamed implicit val defs in kernel --- .../src/main/scala/cats/kernel/std/bigInt.scala | 4 ++-- .../src/main/scala/cats/kernel/std/boolean.scala | 2 +- kernel/src/main/scala/cats/kernel/std/byte.scala | 4 ++-- kernel/src/main/scala/cats/kernel/std/char.scala | 2 +- .../src/main/scala/cats/kernel/std/double.scala | 4 ++-- .../src/main/scala/cats/kernel/std/float.scala | 4 ++-- kernel/src/main/scala/cats/kernel/std/int.scala | 4 ++-- kernel/src/main/scala/cats/kernel/std/list.scala | 8 ++++---- kernel/src/main/scala/cats/kernel/std/long.scala | 4 ++-- kernel/src/main/scala/cats/kernel/std/map.scala | 4 ++-- .../src/main/scala/cats/kernel/std/option.scala | 8 ++++---- kernel/src/main/scala/cats/kernel/std/set.scala | 4 ++-- .../src/main/scala/cats/kernel/std/short.scala | 4 ++-- .../src/main/scala/cats/kernel/std/stream.scala | 8 ++++---- .../src/main/scala/cats/kernel/std/string.scala | 4 ++-- kernel/src/main/scala/cats/kernel/std/unit.scala | 4 ++-- .../src/main/scala/cats/kernel/std/vector.scala | 8 ++++---- project/KernelBoiler.scala | 16 ++++++++-------- 18 files changed, 48 insertions(+), 48 deletions(-) diff --git a/kernel/src/main/scala/cats/kernel/std/bigInt.scala b/kernel/src/main/scala/cats/kernel/std/bigInt.scala index 36094a3512..a03b1c2ad1 100644 --- a/kernel/src/main/scala/cats/kernel/std/bigInt.scala +++ b/kernel/src/main/scala/cats/kernel/std/bigInt.scala @@ -4,9 +4,9 @@ package std package object bigInt extends BigIntInstances trait BigIntInstances { - implicit val bigIntOrder: Order[BigInt] = + implicit val catsKernelStdOrderForBigInt: Order[BigInt] = new BigIntOrder - implicit val bigIntGroup: CommutativeGroup[BigInt] = + implicit val catsKernelStdGroupForBigInt: CommutativeGroup[BigInt] = new BigIntGroup } diff --git a/kernel/src/main/scala/cats/kernel/std/boolean.scala b/kernel/src/main/scala/cats/kernel/std/boolean.scala index 6d3214efc5..379021a27d 100644 --- a/kernel/src/main/scala/cats/kernel/std/boolean.scala +++ b/kernel/src/main/scala/cats/kernel/std/boolean.scala @@ -4,7 +4,7 @@ package std package object boolean extends BooleanInstances trait BooleanInstances { - implicit val booleanOrder: Order[Boolean] = + implicit val catsKernelStdOrderForBoolean: Order[Boolean] = new BooleanOrder } diff --git a/kernel/src/main/scala/cats/kernel/std/byte.scala b/kernel/src/main/scala/cats/kernel/std/byte.scala index d635597c1f..69f444a2d6 100644 --- a/kernel/src/main/scala/cats/kernel/std/byte.scala +++ b/kernel/src/main/scala/cats/kernel/std/byte.scala @@ -4,8 +4,8 @@ package std package object byte extends ByteInstances trait ByteInstances { - implicit val byteOrder: Order[Byte] = new ByteOrder - implicit val byteGroup: CommutativeGroup[Byte] = new ByteGroup + implicit val catsKernelStdOrderForByte: Order[Byte] = new ByteOrder + implicit val catsKernelStdGroupForByte: CommutativeGroup[Byte] = new ByteGroup } class ByteGroup extends CommutativeGroup[Byte] { diff --git a/kernel/src/main/scala/cats/kernel/std/char.scala b/kernel/src/main/scala/cats/kernel/std/char.scala index 7a863a8cab..64946a5107 100644 --- a/kernel/src/main/scala/cats/kernel/std/char.scala +++ b/kernel/src/main/scala/cats/kernel/std/char.scala @@ -4,7 +4,7 @@ package std package object char extends CharInstances trait CharInstances { - implicit val charOrder = new CharOrder + implicit val catsKernelStdOrderForChar = new CharOrder } class CharOrder extends Order[Char] { diff --git a/kernel/src/main/scala/cats/kernel/std/double.scala b/kernel/src/main/scala/cats/kernel/std/double.scala index 215fa96564..b656b220e5 100644 --- a/kernel/src/main/scala/cats/kernel/std/double.scala +++ b/kernel/src/main/scala/cats/kernel/std/double.scala @@ -4,8 +4,8 @@ package std import java.lang.Math trait DoubleInstances { - implicit val doubleOrder: Order[Double] = new DoubleOrder - implicit val doubleGroup: CommutativeGroup[Double] = new DoubleGroup + implicit val catsKernelStdOrderForDouble: Order[Double] = new DoubleOrder + implicit val catsKernelStdGroupForDouble: CommutativeGroup[Double] = new DoubleGroup } class DoubleGroup extends CommutativeGroup[Double] { diff --git a/kernel/src/main/scala/cats/kernel/std/float.scala b/kernel/src/main/scala/cats/kernel/std/float.scala index 81c72ed5d5..c41f7978bf 100644 --- a/kernel/src/main/scala/cats/kernel/std/float.scala +++ b/kernel/src/main/scala/cats/kernel/std/float.scala @@ -2,8 +2,8 @@ package cats.kernel package std trait FloatInstances { - implicit val floatOrder: Order[Float] = new FloatOrder - implicit val floatGroup: CommutativeGroup[Float] = new FloatGroup + implicit val catsKernelStdOrderForFloat: Order[Float] = new FloatOrder + implicit val catsKernelStdGroupForFloat: CommutativeGroup[Float] = new FloatGroup } /** diff --git a/kernel/src/main/scala/cats/kernel/std/int.scala b/kernel/src/main/scala/cats/kernel/std/int.scala index 7224bb059e..048805133d 100644 --- a/kernel/src/main/scala/cats/kernel/std/int.scala +++ b/kernel/src/main/scala/cats/kernel/std/int.scala @@ -4,8 +4,8 @@ package std package object int extends IntInstances trait IntInstances { - implicit val intOrder: Order[Int] = new IntOrder - implicit val intGroup: CommutativeGroup[Int] = new IntGroup + implicit val catsKernelStdOrderForInt: Order[Int] = new IntOrder + implicit val catsKernelStdGroupForInt: CommutativeGroup[Int] = new IntGroup } class IntGroup extends CommutativeGroup[Int] { diff --git a/kernel/src/main/scala/cats/kernel/std/list.scala b/kernel/src/main/scala/cats/kernel/std/list.scala index 056e4f36f2..6de5e369cf 100644 --- a/kernel/src/main/scala/cats/kernel/std/list.scala +++ b/kernel/src/main/scala/cats/kernel/std/list.scala @@ -7,19 +7,19 @@ import scala.collection.mutable package object list extends ListInstances trait ListInstances extends ListInstances1 { - implicit def listOrder[A: Order]: Order[List[A]] = + implicit def catsKernelStdOrderForList[A: Order]: Order[List[A]] = new ListOrder[A] - implicit def listMonoid[A]: Monoid[List[A]] = + implicit def catsKernelStdMonoidForList[A]: Monoid[List[A]] = new ListMonoid[A] } trait ListInstances1 extends ListInstances2 { - implicit def listPartialOrder[A: PartialOrder]: PartialOrder[List[A]] = + implicit def catsKernelStdPartialOrderForList[A: PartialOrder]: PartialOrder[List[A]] = new ListPartialOrder[A] } trait ListInstances2 { - implicit def listEq[A: Eq]: Eq[List[A]] = + implicit def catsKernelStdEqForList[A: Eq]: Eq[List[A]] = new ListEq[A] } diff --git a/kernel/src/main/scala/cats/kernel/std/long.scala b/kernel/src/main/scala/cats/kernel/std/long.scala index 21552b6a6f..dbfec69ff1 100644 --- a/kernel/src/main/scala/cats/kernel/std/long.scala +++ b/kernel/src/main/scala/cats/kernel/std/long.scala @@ -4,8 +4,8 @@ package std package object long extends LongInstances trait LongInstances { - implicit val longOrder: Order[Long] = new LongOrder - implicit val longGroup: CommutativeGroup[Long] = new LongGroup + implicit val catsKernelStdOrderForLong: Order[Long] = new LongOrder + implicit val catsKernelStdGroupForLong: CommutativeGroup[Long] = new LongGroup } class LongGroup extends CommutativeGroup[Long] { diff --git a/kernel/src/main/scala/cats/kernel/std/map.scala b/kernel/src/main/scala/cats/kernel/std/map.scala index 3743be1518..ad4411fed8 100644 --- a/kernel/src/main/scala/cats/kernel/std/map.scala +++ b/kernel/src/main/scala/cats/kernel/std/map.scala @@ -6,9 +6,9 @@ import cats.kernel.std.util.StaticMethods.addMap package object map extends MapInstances trait MapInstances { - implicit def mapEq[K, V: Eq]: Eq[Map[K, V]] = + implicit def catsKernelStdEqForMap[K, V: Eq]: Eq[Map[K, V]] = new MapEq[K, V] - implicit def mapMonoid[K, V: Semigroup]: Monoid[Map[K, V]] = + implicit def catsKernelStdMonoidForMap[K, V: Semigroup]: Monoid[Map[K, V]] = new MapMonoid[K, V] } diff --git a/kernel/src/main/scala/cats/kernel/std/option.scala b/kernel/src/main/scala/cats/kernel/std/option.scala index 4a6fcf36d0..f42a96ec8d 100644 --- a/kernel/src/main/scala/cats/kernel/std/option.scala +++ b/kernel/src/main/scala/cats/kernel/std/option.scala @@ -4,19 +4,19 @@ package std package object option extends OptionInstances trait OptionInstances extends OptionInstances1 { - implicit def optionOrder[A: Order]: Order[Option[A]] = + implicit def catsKernelStdOrderForOptioin[A: Order]: Order[Option[A]] = new OptionOrder[A] - implicit def optionMonoid[A: Semigroup]: Monoid[Option[A]] = + implicit def catsKernelStdMonoidForOptioin[A: Semigroup]: Monoid[Option[A]] = new OptionMonoid[A] } trait OptionInstances1 extends OptionInstances0 { - implicit def optionPartialOrder[A: PartialOrder]: PartialOrder[Option[A]] = + implicit def catsKernelStdPartialOrderForOptioin[A: PartialOrder]: PartialOrder[Option[A]] = new OptionPartialOrder[A] } trait OptionInstances0 { - implicit def optionEq[A: Eq]: Eq[Option[A]] = + implicit def catsKernelStdEqForOptioin[A: Eq]: Eq[Option[A]] = new OptionEq[A] } diff --git a/kernel/src/main/scala/cats/kernel/std/set.scala b/kernel/src/main/scala/cats/kernel/std/set.scala index ae84e1d09d..c7012ccf0d 100644 --- a/kernel/src/main/scala/cats/kernel/std/set.scala +++ b/kernel/src/main/scala/cats/kernel/std/set.scala @@ -4,10 +4,10 @@ package std package object set extends SetInstances trait SetInstances { - implicit def setPartialOrder[A]: PartialOrder[Set[A]] = + implicit def catsKernelStdPartialOrderForSet[A]: PartialOrder[Set[A]] = new SetPartialOrder[A] - implicit def setSemilattice[A]: BoundedSemilattice[Set[A]] = + implicit def catsKernelStdSemilatticeForSet[A]: BoundedSemilattice[Set[A]] = new SetSemilattice[A] } diff --git a/kernel/src/main/scala/cats/kernel/std/short.scala b/kernel/src/main/scala/cats/kernel/std/short.scala index 531dfae07d..50cd1fe145 100644 --- a/kernel/src/main/scala/cats/kernel/std/short.scala +++ b/kernel/src/main/scala/cats/kernel/std/short.scala @@ -4,8 +4,8 @@ package std package object short extends ShortInstances trait ShortInstances { - implicit val shortOrder: Order[Short] = new ShortOrder - implicit val shortGroup: CommutativeGroup[Short] = new ShortGroup + implicit val catsKernelStdOrderForShort: Order[Short] = new ShortOrder + implicit val catsKernelStdGroupForShort: CommutativeGroup[Short] = new ShortGroup } class ShortGroup extends CommutativeGroup[Short] { diff --git a/kernel/src/main/scala/cats/kernel/std/stream.scala b/kernel/src/main/scala/cats/kernel/std/stream.scala index edea35a471..05fba59126 100644 --- a/kernel/src/main/scala/cats/kernel/std/stream.scala +++ b/kernel/src/main/scala/cats/kernel/std/stream.scala @@ -6,19 +6,19 @@ import cats.kernel.std.util.StaticMethods package object stream extends StreamInstances trait StreamInstances extends StreamInstances1 { - implicit def streamOrder[A: Order]: Order[Stream[A]] = + implicit def catsKernelStdOrderForStream[A: Order]: Order[Stream[A]] = new StreamOrder[A] - implicit def streamMonoid[A]: Monoid[Stream[A]] = + implicit def catsKernelStdMonoidForStream[A]: Monoid[Stream[A]] = new StreamMonoid[A] } trait StreamInstances1 extends StreamInstances2 { - implicit def streamPartialOrder[A: PartialOrder]: PartialOrder[Stream[A]] = + implicit def catsKernelStdPartialOrderForStream[A: PartialOrder]: PartialOrder[Stream[A]] = new StreamPartialOrder[A] } trait StreamInstances2 { - implicit def streamEq[A: Eq]: Eq[Stream[A]] = + implicit def catsKernelStdEqForStream[A: Eq]: Eq[Stream[A]] = new StreamEq[A] } diff --git a/kernel/src/main/scala/cats/kernel/std/string.scala b/kernel/src/main/scala/cats/kernel/std/string.scala index 6e46989761..3caad03c93 100644 --- a/kernel/src/main/scala/cats/kernel/std/string.scala +++ b/kernel/src/main/scala/cats/kernel/std/string.scala @@ -4,8 +4,8 @@ package std package object string extends StringInstances trait StringInstances { - implicit val stringOrder: Order[String] = new StringOrder - implicit val stringMonoid: Monoid[String] = new StringMonoid + implicit val catsKernelStdOrderForString: Order[String] = new StringOrder + implicit val catsKernelStdMonoidForString: Monoid[String] = new StringMonoid } class StringOrder extends Order[String] { diff --git a/kernel/src/main/scala/cats/kernel/std/unit.scala b/kernel/src/main/scala/cats/kernel/std/unit.scala index fd0b8b8b1a..bc71510f38 100644 --- a/kernel/src/main/scala/cats/kernel/std/unit.scala +++ b/kernel/src/main/scala/cats/kernel/std/unit.scala @@ -4,10 +4,10 @@ package std package object unit extends UnitInstances trait UnitInstances { - implicit val unitOrder: Order[Unit] = + implicit val catsKernelStdOrderForUnit: Order[Unit] = new UnitOrder - implicit val unitAlgebra: BoundedSemilattice[Unit] with CommutativeGroup[Unit] = + implicit val catsKernelStdAlgebraForUnit: BoundedSemilattice[Unit] with CommutativeGroup[Unit] = new UnitAlgebra } diff --git a/kernel/src/main/scala/cats/kernel/std/vector.scala b/kernel/src/main/scala/cats/kernel/std/vector.scala index 77ed2ae954..f559ffe7ea 100644 --- a/kernel/src/main/scala/cats/kernel/std/vector.scala +++ b/kernel/src/main/scala/cats/kernel/std/vector.scala @@ -6,19 +6,19 @@ import cats.kernel.std.util.StaticMethods package object vector extends VectorInstances trait VectorInstances extends VectorInstances1 { - implicit def vectorOrder[A: Order]: Order[Vector[A]] = + implicit def catsKernelStdOrderForVector[A: Order]: Order[Vector[A]] = new VectorOrder[A] - implicit def vectorMonoid[A]: Monoid[Vector[A]] = + implicit def catsKernelStdMonoidForVector[A]: Monoid[Vector[A]] = new VectorMonoid[A] } trait VectorInstances1 extends VectorInstances2 { - implicit def vectorPartialOrder[A: PartialOrder]: PartialOrder[Vector[A]] = + implicit def catsKernelStdPartialOrderForVector[A: PartialOrder]: PartialOrder[Vector[A]] = new VectorPartialOrder[A] } trait VectorInstances2 { - implicit def vectorEq[A: Eq]: Eq[Vector[A]] = + implicit def catsKernelStdEqForVector[A: Eq]: Eq[Vector[A]] = new VectorEq[A] } diff --git a/project/KernelBoiler.scala b/project/KernelBoiler.scala index 69a46537fe..e86048d115 100644 --- a/project/KernelBoiler.scala +++ b/project/KernelBoiler.scala @@ -121,47 +121,47 @@ object KernelBoiler { |package std | |trait TupleInstances { - - implicit def tuple${arity}Band[${`A..N`}](implicit ${constraints("Band")}): Band[${`(A..N)`}] = + - implicit def catsKernelStdBandForTuple${arity}[${`A..N`}](implicit ${constraints("Band")}): Band[${`(A..N)`}] = - new Band[${`(A..N)`}] { - def combine(x: ${`(A..N)`}, y: ${`(A..N)`}): ${`(A..N)`} = ${binTuple("combine")} - } - - - implicit def tuple${arity}Group[${`A..N`}](implicit ${constraints("Group")}): Group[${`(A..N)`}] = + - implicit def catsKernelStdGroupForTuple${arity}[${`A..N`}](implicit ${constraints("Group")}): Group[${`(A..N)`}] = - new Group[${`(A..N)`}] { - def combine(x: ${`(A..N)`}, y: ${`(A..N)`}): ${`(A..N)`} = ${binTuple("combine")} - def empty: ${`(A..N)`} = ${nullaryTuple("empty")} - def inverse(x: ${`(A..N)`}): ${`(A..N)`} = ${unaryTuple("inverse")} - } - - - implicit def tuple${arity}Eq[${`A..N`}](implicit ${constraints("Eq")}): Eq[${`(A..N)`}] = + - implicit def catsKernelStdEqForTuple${arity}[${`A..N`}](implicit ${constraints("Eq")}): Eq[${`(A..N)`}] = - new Eq[${`(A..N)`}] { - def eqv(x: ${`(A..N)`}, y: ${`(A..N)`}): Boolean = ${binMethod("eqv").mkString(" && ")} - } - - - implicit def tuple${arity}Monoid[${`A..N`}](implicit ${constraints("Monoid")}): Monoid[${`(A..N)`}] = + - implicit def catsKernelStdMonoidForTuple${arity}[${`A..N`}](implicit ${constraints("Monoid")}): Monoid[${`(A..N)`}] = - new Monoid[${`(A..N)`}] { - def combine(x: ${`(A..N)`}, y: ${`(A..N)`}): ${`(A..N)`} = ${binTuple("combine")} - def empty: ${`(A..N)`} = ${nullaryTuple("empty")} - } - - - implicit def tuple${arity}Order[${`A..N`}](implicit ${constraints("Order")}): Order[${`(A..N)`}] = + - implicit def catsKernelStdOrderForTuple${arity}[${`A..N`}](implicit ${constraints("Order")}): Order[${`(A..N)`}] = - new Order[${`(A..N)`}] { - def compare(x: ${`(A..N)`}, y: ${`(A..N)`}): Int = - ${binMethod("compare").find(_ != 0).getOrElse(0)} - } - - - implicit def tuple${arity}PartialOrder[${`A..N`}](implicit ${constraints("PartialOrder")}): PartialOrder[${`(A..N)`}] = + - implicit def catsKernelStdPartialOrderForTuple${arity}[${`A..N`}](implicit ${constraints("PartialOrder")}): PartialOrder[${`(A..N)`}] = - new PartialOrder[${`(A..N)`}] { - def partialCompare(x: ${`(A..N)`}, y: ${`(A..N)`}): Double = - ${binMethod("partialCompare").find(_ != 0.0).getOrElse(0.0)} - } - - - implicit def tuple${arity}Semigroup[${`A..N`}](implicit ${constraints("Semigroup")}): Semigroup[${`(A..N)`}] = + - implicit def catsKernelStdSemigroupForTuple${arity}[${`A..N`}](implicit ${constraints("Semigroup")}): Semigroup[${`(A..N)`}] = - new Semigroup[${`(A..N)`}] { - def combine(x: ${`(A..N)`}, y: ${`(A..N)`}): ${`(A..N)`} = ${binTuple("combine")} - } - - - implicit def tuple${arity}Semilattice[${`A..N`}](implicit ${constraints("Semilattice")}): Semilattice[${`(A..N)`}] = + - implicit def catsKernelStdSemilatticeForTuple${arity}[${`A..N`}](implicit ${constraints("Semilattice")}): Semilattice[${`(A..N)`}] = - new Semilattice[${`(A..N)`}] { - def combine(x: ${`(A..N)`}, y: ${`(A..N)`}): ${`(A..N)`} = ${binTuple("combine")} - } From 0c6997bad5e384aee31d4505cb2db59f4eaa4855 Mon Sep 17 00:00:00 2001 From: Tomas Mikula Date: Mon, 16 May 2016 19:40:12 -0400 Subject: [PATCH 181/185] Add FlatMapRec, MonadRec. --- core/src/main/scala/cats/FlatMapRec.scala | 24 +++++++++++++++++++++++ core/src/main/scala/cats/MonadRec.scala | 5 +++++ 2 files changed, 29 insertions(+) create mode 100644 core/src/main/scala/cats/FlatMapRec.scala create mode 100644 core/src/main/scala/cats/MonadRec.scala diff --git a/core/src/main/scala/cats/FlatMapRec.scala b/core/src/main/scala/cats/FlatMapRec.scala new file mode 100644 index 0000000000..5658b3fe00 --- /dev/null +++ b/core/src/main/scala/cats/FlatMapRec.scala @@ -0,0 +1,24 @@ +package cats + +import simulacrum.typeclass + +import cats.data.Xor + +/** + * Version of [[cats.FlatMap]] capable of stack-safe recursive `flatMap`s. + * + * Based on Phil Freeman's + * [[http://functorial.com/stack-safety-for-free/index.pdf Stack Safety for Free]]. + */ +@typeclass trait FlatMapRec[F[_]] extends FlatMap[F] { + + /** + * Keeps calling `f` until a `[[cats.data.Xor.Right Right]][B]` is returned. + * + * Implementations of this method must use constant stack space. + * + * `f` must use constant stack space. (It is OK to use a constant number of + * `map`s and `flatMap`s inside `f`.) + */ + def tailRecM[A, B](a: A)(f: A => F[A Xor B]): F[B] +} diff --git a/core/src/main/scala/cats/MonadRec.scala b/core/src/main/scala/cats/MonadRec.scala new file mode 100644 index 0000000000..e61588c8da --- /dev/null +++ b/core/src/main/scala/cats/MonadRec.scala @@ -0,0 +1,5 @@ +package cats + +import simulacrum.typeclass + +@typeclass trait MonadRec[F[_]] extends Monad[F] with FlatMapRec[F] From 5a38209b2a13ded2652528a8767b1683e74d339d Mon Sep 17 00:00:00 2001 From: Tomas Mikula Date: Mon, 16 May 2016 21:49:43 -0400 Subject: [PATCH 182/185] MonadRec instances for Id, Option, OptionT, Either, Xor, XorT, Free, List. --- core/src/main/scala/cats/data/OptionT.scala | 42 ++++++++++++++----- core/src/main/scala/cats/data/Xor.scala | 11 ++++- core/src/main/scala/cats/data/XorT.scala | 22 +++++++++- core/src/main/scala/cats/package.scala | 11 ++++- core/src/main/scala/cats/std/either.scala | 15 ++++++- core/src/main/scala/cats/std/list.scala | 20 ++++++++- core/src/main/scala/cats/std/option.scala | 15 ++++++- free/src/main/scala/cats/free/Free.scala | 9 +++- free/src/test/scala/cats/free/FreeTests.scala | 14 +++++-- .../main/scala/cats/laws/FlatMapRecLaws.scala | 26 ++++++++++++ .../main/scala/cats/laws/MonadRecLaws.scala | 14 +++++++ .../laws/discipline/FlatMapRecTests.scala | 35 ++++++++++++++++ .../cats/laws/discipline/MonadRecTests.scala | 38 +++++++++++++++++ .../test/scala/cats/tests/EitherTests.scala | 6 +-- tests/src/test/scala/cats/tests/IdTests.scala | 3 ++ .../cats/tests/MonadRecInstancesTests.scala | 41 ++++++++++++++++++ .../test/scala/cats/tests/OptionTTests.scala | 8 ++-- .../test/scala/cats/tests/OptionTests.scala | 3 ++ .../src/test/scala/cats/tests/XorTTests.scala | 2 + .../src/test/scala/cats/tests/XorTests.scala | 5 ++- 20 files changed, 304 insertions(+), 36 deletions(-) create mode 100644 laws/src/main/scala/cats/laws/FlatMapRecLaws.scala create mode 100644 laws/src/main/scala/cats/laws/MonadRecLaws.scala create mode 100644 laws/src/main/scala/cats/laws/discipline/FlatMapRecTests.scala create mode 100644 laws/src/main/scala/cats/laws/discipline/MonadRecTests.scala create mode 100644 tests/src/test/scala/cats/tests/MonadRecInstancesTests.scala diff --git a/core/src/main/scala/cats/data/OptionT.scala b/core/src/main/scala/cats/data/OptionT.scala index 91575a4510..2754604656 100644 --- a/core/src/main/scala/cats/data/OptionT.scala +++ b/core/src/main/scala/cats/data/OptionT.scala @@ -132,7 +132,7 @@ object OptionT extends OptionTInstances { def liftF[F[_], A](fa: F[A])(implicit F: Functor[F]): OptionT[F, A] = OptionT(F.map(fa)(Some(_))) } -private[data] sealed trait OptionTInstances1 { +private[data] sealed trait OptionTInstances2 { implicit def catsDataFunctorForOptionT[F[_]:Functor]: Functor[OptionT[F, ?]] = new Functor[OptionT[F, ?]] { override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] = @@ -148,18 +148,15 @@ private[data] sealed trait OptionTInstances1 { } } -private[data] sealed trait OptionTInstances extends OptionTInstances1 { - - implicit def catsDataMonadForOptionT[F[_]](implicit F: Monad[F]): Monad[OptionT[F, ?]] = - new Monad[OptionT[F, ?]] { - def pure[A](a: A): OptionT[F, A] = OptionT.pure(a) +private[data] sealed trait OptionTInstances1 extends OptionTInstances2 { - def flatMap[A, B](fa: OptionT[F, A])(f: A => OptionT[F, B]): OptionT[F, B] = - fa.flatMap(f) + implicit def catsDataMonadForOptionT[F[_]](implicit F0: Monad[F]): Monad[OptionT[F, ?]] = + new OptionTMonad[F] { implicit val F = F0 } +} - override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] = - fa.map(f) - } +private[data] sealed trait OptionTInstances extends OptionTInstances1 { + implicit def catsDataMonadRecForOptionT[F[_]](implicit F0: MonadRec[F]): MonadRec[OptionT[F, ?]] = + new OptionTMonadRec[F] { implicit val F = F0 } implicit def catsDataEqForOptionT[F[_], A](implicit FA: Eq[F[Option[A]]]): Eq[OptionT[F, A]] = FA.on(_.value) @@ -167,3 +164,26 @@ private[data] sealed trait OptionTInstances extends OptionTInstances1 { implicit def catsDataShowForOptionT[F[_], A](implicit F: Show[F[Option[A]]]): Show[OptionT[F, A]] = functor.Contravariant[Show].contramap(F)(_.value) } + +private[data] trait OptionTMonad[F[_]] extends Monad[OptionT[F, ?]] { + implicit val F: Monad[F] + + def pure[A](a: A): OptionT[F, A] = OptionT.pure(a) + + def flatMap[A, B](fa: OptionT[F, A])(f: A => OptionT[F, B]): OptionT[F, B] = + fa.flatMap(f) + + override def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] = + fa.map(f) +} + +private[data] trait OptionTMonadRec[F[_]] extends MonadRec[OptionT[F, ?]] with OptionTMonad[F] { + implicit val F: MonadRec[F] + + def tailRecM[A, B](a: A)(f: A => OptionT[F, A Xor B]): OptionT[F, B] = + OptionT(F.tailRecM(a)(a0 => F.map(f(a0).value){ + case None => Xor.Right(None) + case Some(Xor.Left(a1)) => Xor.Left(a1) + case Some(Xor.Right(b)) => Xor.Right(Some(b)) + })) +} diff --git a/core/src/main/scala/cats/data/Xor.scala b/core/src/main/scala/cats/data/Xor.scala index d8a6a4a2b3..08136b4eb0 100644 --- a/core/src/main/scala/cats/data/Xor.scala +++ b/core/src/main/scala/cats/data/Xor.scala @@ -1,6 +1,7 @@ package cats package data +import scala.annotation.tailrec import scala.reflect.ClassTag import scala.util.{Failure, Success, Try} @@ -233,13 +234,19 @@ private[data] sealed abstract class XorInstances extends XorInstances1 { } } - implicit def catsDataInstancesForXor[A]: Traverse[A Xor ?] with MonadError[Xor[A, ?], A] = - new Traverse[A Xor ?] with MonadError[Xor[A, ?], A] { + implicit def catsDataInstancesForXor[A]: Traverse[A Xor ?] with MonadRec[A Xor ?] with MonadError[Xor[A, ?], A] = + new Traverse[A Xor ?] with MonadRec[A Xor ?] with MonadError[Xor[A, ?], A] { def traverse[F[_]: Applicative, B, C](fa: A Xor B)(f: B => F[C]): F[A Xor C] = fa.traverse(f) def foldLeft[B, C](fa: A Xor B, c: C)(f: (C, B) => C): C = fa.foldLeft(c)(f) def foldRight[B, C](fa: A Xor B, lc: Eval[C])(f: (B, Eval[C]) => Eval[C]): Eval[C] = fa.foldRight(lc)(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) + @tailrec def tailRecM[B, C](b: B)(f: B => A Xor (B Xor C)): A Xor C = + f(b) match { + case Xor.Left(a) => Xor.Left(a) + case Xor.Right(Xor.Left(b1)) => tailRecM(b1)(f) + case Xor.Right(Xor.Right(c)) => Xor.Right(c) + } def handleErrorWith[B](fea: Xor[A, B])(f: A => Xor[A, B]): Xor[A, B] = fea match { case Xor.Left(e) => f(e) diff --git a/core/src/main/scala/cats/data/XorT.scala b/core/src/main/scala/cats/data/XorT.scala index 555f9c1b39..91feb688c4 100644 --- a/core/src/main/scala/cats/data/XorT.scala +++ b/core/src/main/scala/cats/data/XorT.scala @@ -279,6 +279,11 @@ private[data] abstract class XorTInstances1 extends XorTInstances2 { } private[data] abstract class XorTInstances2 extends XorTInstances3 { + implicit def catsDataMonadRecForXorT[F[_], L](implicit F0: MonadRec[F]): MonadRec[XorT[F, L, ?]] = + new XorTMonadRec[F, L] { implicit val F = F0 } +} + +private[data] abstract class XorTInstances3 extends XorTInstances4 { implicit def catsDataMonadErrorForXorT[F[_], L](implicit F: Monad[F]): MonadError[XorT[F, L, ?], L] = { implicit val F0 = F new XorTMonadError[F, L] { implicit val F = F0 } @@ -299,7 +304,7 @@ private[data] abstract class XorTInstances2 extends XorTInstances3 { } } -private[data] abstract class XorTInstances3 { +private[data] abstract class XorTInstances4 { implicit def catsDataFunctorForXorT[F[_], L](implicit F: Functor[F]): Functor[XorT[F, L, ?]] = { implicit val F0 = F new XorTFunctor[F, L] { implicit val F = F0 } @@ -311,10 +316,13 @@ private[data] trait XorTFunctor[F[_], L] extends Functor[XorT[F, L, ?]] { override def map[A, B](fa: XorT[F, L, A])(f: A => B): XorT[F, L, B] = fa map f } -private[data] trait XorTMonadError[F[_], L] extends MonadError[XorT[F, L, ?], L] with XorTFunctor[F, L] { +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 flatMap[A, B](fa: XorT[F, L, A])(f: A => XorT[F, L, B]): XorT[F, L, B] = fa flatMap f +} + +private[data] trait XorTMonadError[F[_], L] extends MonadError[XorT[F, L, ?], L] with XorTMonad[F, L] { def handleErrorWith[A](fea: XorT[F, L, A])(f: L => XorT[F, L, A]): XorT[F, L, A] = XorT(F.flatMap(fea.value) { case Xor.Left(e) => f(e).value @@ -333,6 +341,16 @@ private[data] trait XorTMonadError[F[_], L] extends MonadError[XorT[F, L, ?], L] fla.recoverWith(pf) } +private[data] trait XorTMonadRec[F[_], L] extends MonadRec[XorT[F, L, ?]] with XorTMonad[F, L] { + implicit val F: MonadRec[F] + def tailRecM[A, B](a: A)(f: A => XorT[F, L, A Xor B]): XorT[F, L, B] = + XorT(F.tailRecM(a)(a0 => F.map(f(a0).value){ + case Xor.Left(l) => Xor.Right(Xor.Left(l)) + case Xor.Right(Xor.Left(a1)) => Xor.Left(a1) + case Xor.Right(Xor.Right(b)) => Xor.Right(Xor.Right(b)) + })) +} + private[data] trait XorTMonadFilter[F[_], L] extends MonadFilter[XorT[F, L, ?]] with XorTMonadError[F, L] { implicit val F: Monad[F] implicit val L: Monoid[L] diff --git a/core/src/main/scala/cats/package.scala b/core/src/main/scala/cats/package.scala index d1e7ad5730..cc90780828 100644 --- a/core/src/main/scala/cats/package.scala +++ b/core/src/main/scala/cats/package.scala @@ -1,3 +1,6 @@ +import scala.annotation.tailrec +import cats.data.Xor + /** * Symbolic aliases for various types are defined here. */ @@ -26,12 +29,16 @@ package object cats { * encodes pure unary function application. */ type Id[A] = A - implicit val idInstances: Bimonad[Id] with Traverse[Id] = - new Bimonad[Id] with Traverse[Id] { + implicit val idInstances: Bimonad[Id] with MonadRec[Id] with Traverse[Id] = + new Bimonad[Id] with MonadRec[Id] with Traverse[Id] { def pure[A](a: A): A = a def extract[A](a: A): A = a def flatMap[A, B](a: A)(f: A => B): B = f(a) def coflatMap[A, B](a: A)(f: A => B): B = f(a) + @tailrec def tailRecM[A, B](a: A)(f: A => A Xor B): B = f(a) match { + case Xor.Left(a1) => tailRecM(a1)(f) + case Xor.Right(b) => b + } override def map[A, B](fa: A)(f: A => B): B = f(fa) override def ap[A, B](ff: A => B)(fa: A): B = ff(fa) override def flatten[A](ffa: A): A = ffa diff --git a/core/src/main/scala/cats/std/either.scala b/core/src/main/scala/cats/std/either.scala index 43962597bf..019a4dc29f 100644 --- a/core/src/main/scala/cats/std/either.scala +++ b/core/src/main/scala/cats/std/either.scala @@ -1,6 +1,9 @@ package cats package std +import scala.annotation.tailrec +import cats.data.Xor + trait EitherInstances extends EitherInstances1 { implicit val catsStdBitraverseForEither: Bitraverse[Either] = new Bitraverse[Either] { @@ -23,8 +26,8 @@ trait EitherInstances extends EitherInstances1 { } } - implicit def catsStdInstancesForEither[A]: Monad[Either[A, ?]] with Traverse[Either[A, ?]] = - new Monad[Either[A, ?]] with Traverse[Either[A, ?]] { + implicit def catsStdInstancesForEither[A]: MonadRec[Either[A, ?]] with Traverse[Either[A, ?]] = + new MonadRec[Either[A, ?]] with Traverse[Either[A, ?]] { def pure[B](b: B): Either[A, B] = Right(b) def flatMap[B, C](fa: Either[A, B])(f: B => Either[A, C]): Either[A, C] = @@ -33,6 +36,14 @@ trait EitherInstances extends EitherInstances1 { override def map[B, C](fa: Either[A, B])(f: B => C): Either[A, C] = fa.right.map(f) + @tailrec + def tailRecM[B, C](b: B)(f: B => Either[A, B Xor C]): Either[A, C] = + f(b) match { + case Left(a) => Left(a) + case Right(Xor.Left(b1)) => tailRecM(b1)(f) + case Right(Xor.Right(c)) => Right(c) + } + override def map2Eval[B, C, Z](fb: Either[A, B], fc: Eval[Either[A, C]])(f: (B, C) => Z): Eval[Either[A, Z]] = fb match { // This should be safe, but we are forced to use `asInstanceOf`, diff --git a/core/src/main/scala/cats/std/list.scala b/core/src/main/scala/cats/std/list.scala index 102ad05e71..6154c7d494 100644 --- a/core/src/main/scala/cats/std/list.scala +++ b/core/src/main/scala/cats/std/list.scala @@ -6,10 +6,12 @@ import cats.syntax.show._ import scala.annotation.tailrec import scala.collection.mutable.ListBuffer +import cats.data.Xor + trait ListInstances extends cats.kernel.std.ListInstances { - implicit val catsStdInstancesForList: Traverse[List] with MonadCombine[List] with CoflatMap[List] = - new Traverse[List] with MonadCombine[List] with CoflatMap[List] { + implicit val catsStdInstancesForList: Traverse[List] with MonadCombine[List] with MonadRec[List] with CoflatMap[List] = + new Traverse[List] with MonadCombine[List] with MonadRec[List] with CoflatMap[List] { def empty[A]: List[A] = Nil @@ -26,6 +28,20 @@ trait ListInstances extends cats.kernel.std.ListInstances { override def map2[A, B, Z](fa: List[A], fb: List[B])(f: (A, B) => Z): List[Z] = fa.flatMap(a => fb.map(b => f(a, b))) + def tailRecM[A, B](a: A)(f: A => List[A Xor B]): List[B] = { + val buf = List.newBuilder[B] + @tailrec def go(lists: List[List[A Xor B]]): Unit = lists match { + case (ab :: abs) :: tail => ab match { + case Xor.Right(b) => buf += b; go(abs :: tail) + case Xor.Left(a) => go(f(a) :: abs :: tail) + } + case Nil :: tail => go(tail) + case Nil => () + } + go(f(a) :: Nil) + buf.result + } + def coflatMap[A, B](fa: List[A])(f: List[A] => B): List[B] = { @tailrec def loop(buf: ListBuffer[B], as: List[A]): List[B] = as match { diff --git a/core/src/main/scala/cats/std/option.scala b/core/src/main/scala/cats/std/option.scala index af943b923b..41204c5326 100644 --- a/core/src/main/scala/cats/std/option.scala +++ b/core/src/main/scala/cats/std/option.scala @@ -1,10 +1,13 @@ package cats package std +import scala.annotation.tailrec +import cats.data.Xor + trait OptionInstances extends cats.kernel.std.OptionInstances { - implicit val catsStdInstancesForOption: Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] = - new Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with CoflatMap[Option] with Alternative[Option] { + implicit val catsStdInstancesForOption: Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with MonadRec[Option] with CoflatMap[Option] with Alternative[Option] = + new Traverse[Option] with MonadError[Option, Unit] with MonadCombine[Option] with MonadRec[Option] with CoflatMap[Option] with Alternative[Option] { def empty[A]: Option[A] = None @@ -18,6 +21,14 @@ trait OptionInstances extends cats.kernel.std.OptionInstances { def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] = fa.flatMap(f) + @tailrec + def tailRecM[A, B](a: A)(f: A => Option[A Xor B]): Option[B] = + f(a) match { + case None => None + case Some(Xor.Left(a1)) => tailRecM(a1)(f) + case Some(Xor.Right(b)) => Some(b) + } + override def map2[A, B, Z](fa: Option[A], fb: Option[B])(f: (A, B) => Z): Option[Z] = fa.flatMap(a => fb.map(b => f(a, b))) diff --git a/free/src/main/scala/cats/free/Free.scala b/free/src/main/scala/cats/free/Free.scala index d6a14d3a70..c4e2233b7b 100644 --- a/free/src/main/scala/cats/free/Free.scala +++ b/free/src/main/scala/cats/free/Free.scala @@ -40,11 +40,16 @@ object Free { /** * `Free[S, ?]` has a monad for any type constructor `S[_]`. */ - implicit def freeMonad[S[_]]: Monad[Free[S, ?]] = - new Monad[Free[S, ?]] { + implicit def freeMonad[S[_]]: MonadRec[Free[S, ?]] = + new MonadRec[Free[S, ?]] { def pure[A](a: A): Free[S, A] = Free.pure(a) override def map[A, B](fa: Free[S, A])(f: A => B): Free[S, B] = fa.map(f) def flatMap[A, B](a: Free[S, A])(f: A => Free[S, B]): Free[S, B] = a.flatMap(f) + def tailRecM[A, B](a: A)(f: A => Free[S, A Xor B]): Free[S, B] = + f(a).flatMap(_ match { + case Xor.Left(a1) => tailRecM(a1)(f) // recursion OK here, since Free is lazy + case Xor.Right(b) => pure(b) + }) } } diff --git a/free/src/test/scala/cats/free/FreeTests.scala b/free/src/test/scala/cats/free/FreeTests.scala index 1b9c7d63be..5291c60d5c 100644 --- a/free/src/test/scala/cats/free/FreeTests.scala +++ b/free/src/test/scala/cats/free/FreeTests.scala @@ -3,7 +3,8 @@ package free import cats.tests.CatsSuite import cats.arrow.NaturalTransformation -import cats.laws.discipline.{CartesianTests, MonadTests, SerializableTests} +import cats.data.Xor +import cats.laws.discipline.{CartesianTests, MonadRecTests, SerializableTests} import cats.laws.discipline.arbitrary.function0Arbitrary import org.scalacheck.{Arbitrary, Gen} @@ -14,8 +15,8 @@ class FreeTests extends CatsSuite { implicit val iso = CartesianTests.Isomorphisms.invariant[Free[Option, ?]] - checkAll("Free[Option, ?]", MonadTests[Free[Option, ?]].monad[Int, Int, Int]) - checkAll("Monad[Free[Option, ?]]", SerializableTests.serializable(Monad[Free[Option, ?]])) + checkAll("Free[Option, ?]", MonadRecTests[Free[Option, ?]].monadRec[Int, Int, Int]) + checkAll("MonadRec[Free[Option, ?]]", SerializableTests.serializable(MonadRec[Free[Option, ?]])) test("mapSuspension id"){ forAll { x: Free[List, Int] => @@ -43,6 +44,13 @@ class FreeTests extends CatsSuite { } } + test("tailRecM is stack safe") { + val n = 50000 + val fa = MonadRec[Free[Option, ?]].tailRecM(0)(i => + Free.pure[Option, Int Xor Int](if(i < n) Xor.Left(i+1) else Xor.Right(i))) + fa should === (Free.pure[Option, Int](n)) + } + ignore("foldMap is stack safe") { trait FTestApi[A] case class TB(i: Int) extends FTestApi[Int] diff --git a/laws/src/main/scala/cats/laws/FlatMapRecLaws.scala b/laws/src/main/scala/cats/laws/FlatMapRecLaws.scala new file mode 100644 index 0000000000..8ff0254262 --- /dev/null +++ b/laws/src/main/scala/cats/laws/FlatMapRecLaws.scala @@ -0,0 +1,26 @@ +package cats +package laws + +import cats.data.Xor +import cats.syntax.flatMap._ +import cats.syntax.functor._ + +/** + * Laws that must be obeyed by any `FlatMapRec`. + */ +trait FlatMapRecLaws[F[_]] extends FlatMapLaws[F] { + implicit override def F: FlatMapRec[F] + + def tailRecMConsistentFlatMap[A](a: A, f: A => F[A]): IsEq[F[A]] = { + val bounce = F.tailRecM[(A, Int), A]((a, 1)) { case (a0, i) => + if(i > 0) f(a0).map(a1 => Xor.left((a1, i-1))) + else f(a0).map(Xor.right) + } + bounce <-> f(a).flatMap(f) + } +} + +object FlatMapRecLaws { + def apply[F[_]](implicit ev: FlatMapRec[F]): FlatMapRecLaws[F] = + new FlatMapRecLaws[F] { def F: FlatMapRec[F] = ev } +} diff --git a/laws/src/main/scala/cats/laws/MonadRecLaws.scala b/laws/src/main/scala/cats/laws/MonadRecLaws.scala new file mode 100644 index 0000000000..2b2e2e90a1 --- /dev/null +++ b/laws/src/main/scala/cats/laws/MonadRecLaws.scala @@ -0,0 +1,14 @@ +package cats +package laws + +/** + * Laws that must be obeyed by any `MonadRec`. + */ +trait MonadRecLaws[F[_]] extends MonadLaws[F] with FlatMapRecLaws[F] { + implicit override def F: MonadRec[F] +} + +object MonadRecLaws { + def apply[F[_]](implicit ev: MonadRec[F]): MonadRecLaws[F] = + new MonadRecLaws[F] { def F: MonadRec[F] = ev } +} diff --git a/laws/src/main/scala/cats/laws/discipline/FlatMapRecTests.scala b/laws/src/main/scala/cats/laws/discipline/FlatMapRecTests.scala new file mode 100644 index 0000000000..c4d2c3a2b5 --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/FlatMapRecTests.scala @@ -0,0 +1,35 @@ +package cats +package laws +package discipline + +import cats.laws.discipline.CartesianTests.Isomorphisms +import org.scalacheck.Arbitrary +import org.scalacheck.Prop +import Prop._ + +trait FlatMapRecTests[F[_]] extends FlatMapTests[F] { + def laws: FlatMapRecLaws[F] + + def flatMapRec[A: Arbitrary, B: Arbitrary, C: Arbitrary](implicit + ArbFA: Arbitrary[F[A]], + ArbFB: Arbitrary[F[B]], + ArbFC: Arbitrary[F[C]], + ArbFAtoB: Arbitrary[F[A => B]], + ArbFBtoC: Arbitrary[F[B => C]], + EqFA: Eq[F[A]], + EqFB: Eq[F[B]], + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] + ): RuleSet = { + new DefaultRuleSet( + name = "flatMapRec", + parent = Some(flatMap[A, B, C]), + "tailRecM consistent flatMap" -> forAll(laws.tailRecMConsistentFlatMap[A] _)) + } +} + +object FlatMapRecTests { + def apply[F[_]: FlatMapRec]: FlatMapRecTests[F] = + new FlatMapRecTests[F] { def laws: FlatMapRecLaws[F] = FlatMapRecLaws[F] } +} diff --git a/laws/src/main/scala/cats/laws/discipline/MonadRecTests.scala b/laws/src/main/scala/cats/laws/discipline/MonadRecTests.scala new file mode 100644 index 0000000000..f6904ca6e4 --- /dev/null +++ b/laws/src/main/scala/cats/laws/discipline/MonadRecTests.scala @@ -0,0 +1,38 @@ +package cats +package laws +package discipline + +import cats.laws.discipline.CartesianTests.Isomorphisms +import org.scalacheck.Arbitrary +import org.scalacheck.Prop + +trait MonadRecTests[F[_]] extends MonadTests[F] with FlatMapRecTests[F] { + def laws: MonadRecLaws[F] + + def monadRec[A: Arbitrary: Eq, B: Arbitrary: Eq, C: Arbitrary: Eq](implicit + ArbFA: Arbitrary[F[A]], + ArbFB: Arbitrary[F[B]], + ArbFC: Arbitrary[F[C]], + ArbFAtoB: Arbitrary[F[A => B]], + ArbFBtoC: Arbitrary[F[B => C]], + EqFA: Eq[F[A]], + EqFB: Eq[F[B]], + EqFC: Eq[F[C]], + EqFABC: Eq[F[(A, B, C)]], + iso: Isomorphisms[F] + ): RuleSet = { + new RuleSet { + def name: String = "monadRec" + def bases: Seq[(String, RuleSet)] = Nil + def parents: Seq[RuleSet] = Seq(monad[A, B, C], flatMapRec[A, B, C]) + def props: Seq[(String, Prop)] = Nil + } + } +} + +object MonadRecTests { + def apply[F[_]: MonadRec]: MonadRecTests[F] = + new MonadRecTests[F] { + def laws: MonadRecLaws[F] = MonadRecLaws[F] + } +} diff --git a/tests/src/test/scala/cats/tests/EitherTests.scala b/tests/src/test/scala/cats/tests/EitherTests.scala index 97d7d74e08..b39fc91040 100644 --- a/tests/src/test/scala/cats/tests/EitherTests.scala +++ b/tests/src/test/scala/cats/tests/EitherTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.laws.discipline.{BitraverseTests, TraverseTests, MonadTests, SerializableTests, CartesianTests} +import cats.laws.discipline.{BitraverseTests, TraverseTests, MonadRecTests, SerializableTests, CartesianTests} import cats.kernel.laws.OrderLaws class EitherTests extends CatsSuite { @@ -11,8 +11,8 @@ class EitherTests extends CatsSuite { checkAll("Either[Int, Int]", CartesianTests[Either[Int, ?]].cartesian[Int, Int, Int]) checkAll("Cartesian[Either[Int, ?]]", SerializableTests.serializable(Cartesian[Either[Int, ?]])) - checkAll("Either[Int, Int]", MonadTests[Either[Int, ?]].monad[Int, Int, Int]) - checkAll("Monad[Either[Int, ?]]", SerializableTests.serializable(Monad[Either[Int, ?]])) + checkAll("Either[Int, Int]", MonadRecTests[Either[Int, ?]].monadRec[Int, Int, Int]) + checkAll("MonadRec[Either[Int, ?]]", SerializableTests.serializable(MonadRec[Either[Int, ?]])) checkAll("Either[Int, Int] with Option", TraverseTests[Either[Int, ?]].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[Either[Int, ?]", SerializableTests.serializable(Traverse[Either[Int, ?]])) diff --git a/tests/src/test/scala/cats/tests/IdTests.scala b/tests/src/test/scala/cats/tests/IdTests.scala index 115ccc875e..c7421e1369 100644 --- a/tests/src/test/scala/cats/tests/IdTests.scala +++ b/tests/src/test/scala/cats/tests/IdTests.scala @@ -9,6 +9,9 @@ class IdTests extends CatsSuite { checkAll("Id[Int]", BimonadTests[Id].bimonad[Int, Int, Int]) checkAll("Bimonad[Id]", SerializableTests.serializable(Bimonad[Id])) + checkAll("Id[Int]", MonadRecTests[Id].monadRec[Int, Int, Int]) + checkAll("MonadRec[Id]", SerializableTests.serializable(MonadRec[Id])) + checkAll("Id[Int]", TraverseTests[Id].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[Id]", SerializableTests.serializable(Traverse[Id])) } diff --git a/tests/src/test/scala/cats/tests/MonadRecInstancesTests.scala b/tests/src/test/scala/cats/tests/MonadRecInstancesTests.scala new file mode 100644 index 0000000000..649cd60c01 --- /dev/null +++ b/tests/src/test/scala/cats/tests/MonadRecInstancesTests.scala @@ -0,0 +1,41 @@ +package cats +package tests + +import cats.data.{OptionT, Xor, XorT} + +class MonadRecInstancesTests extends CatsSuite { + def tailRecMStackSafety[M[_]](implicit M: MonadRec[M], Eq: Eq[M[Int]]): Unit = { + val n = 50000 + val res = M.tailRecM(0)(i => M.pure(if(i < n) Xor.Left(i + 1) else Xor.Right(i))) + res should === (M.pure(n)) + } + + test("tailRecM stack-safety for Id") { + tailRecMStackSafety[Id] + } + + test("tailRecM stack-safety for Option") { + tailRecMStackSafety[Option] + } + + test("tailRecM stack-safety for OptionT") { + tailRecMStackSafety[OptionT[Option, ?]] + } + + test("tailRecM stack-safety for Either") { + tailRecMStackSafety[Either[String, ?]] + } + + test("tailRecM stack-safety for Xor") { + tailRecMStackSafety[String Xor ?] + } + + test("tailRecM stack-safety for XorT") { + tailRecMStackSafety[XorT[Option, String, ?]] + } + + test("tailRecM stack-safety for List") { + tailRecMStackSafety[List] + } + +} diff --git a/tests/src/test/scala/cats/tests/OptionTTests.scala b/tests/src/test/scala/cats/tests/OptionTTests.scala index 95cb4fac7d..9d12864e88 100644 --- a/tests/src/test/scala/cats/tests/OptionTTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTTests.scala @@ -1,8 +1,8 @@ package cats.tests -import cats.{Id, Monad, Cartesian, Show} +import cats.{Id, MonadRec, Cartesian, Show} import cats.data.{OptionT, Xor} -import cats.laws.discipline.{FunctorTests, SerializableTests, CartesianTests, MonadTests} +import cats.laws.discipline.{FunctorTests, SerializableTests, CartesianTests, MonadRecTests} import cats.laws.discipline.arbitrary._ class OptionTTests extends CatsSuite { @@ -160,8 +160,8 @@ class OptionTTests extends CatsSuite { } } - checkAll("Monad[OptionT[List, Int]]", MonadTests[OptionT[List, ?]].monad[Int, Int, Int]) - checkAll("Monad[OptionT[List, ?]]", SerializableTests.serializable(Monad[OptionT[List, ?]])) + checkAll("OptionT[List, Int]", MonadRecTests[OptionT[List, ?]].monadRec[Int, Int, Int]) + checkAll("MonadRec[OptionT[List, ?]]", SerializableTests.serializable(MonadRec[OptionT[List, ?]])) { implicit val F = ListWrapper.functor diff --git a/tests/src/test/scala/cats/tests/OptionTests.scala b/tests/src/test/scala/cats/tests/OptionTests.scala index 8c6ef464b9..a7e0d69d94 100644 --- a/tests/src/test/scala/cats/tests/OptionTests.scala +++ b/tests/src/test/scala/cats/tests/OptionTests.scala @@ -14,6 +14,9 @@ class OptionTests extends CatsSuite { checkAll("Option[Int]", MonadCombineTests[Option].monadCombine[Int, Int, Int]) checkAll("MonadCombine[Option]", SerializableTests.serializable(MonadCombine[Option])) + checkAll("Option[Int]", MonadRecTests[Option].monadRec[Int, Int, Int]) + checkAll("MonadRec[Option]", SerializableTests.serializable(MonadRec[Option])) + checkAll("Option[Int] with Option", TraverseTests[Option].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[Option]", SerializableTests.serializable(Traverse[Option])) diff --git a/tests/src/test/scala/cats/tests/XorTTests.scala b/tests/src/test/scala/cats/tests/XorTTests.scala index b88e4c2958..80c96a6601 100644 --- a/tests/src/test/scala/cats/tests/XorTTests.scala +++ b/tests/src/test/scala/cats/tests/XorTTests.scala @@ -13,6 +13,8 @@ class XorTTests extends CatsSuite { implicit val iso = CartesianTests.Isomorphisms.invariant[XorT[List, String, ?]] checkAll("XorT[List, String, Int]", MonadErrorTests[XorT[List, String, ?], String].monadError[Int, Int, Int]) checkAll("MonadError[XorT[List, ?, ?]]", SerializableTests.serializable(MonadError[XorT[List, String, ?], String])) + checkAll("XorT[List, String, Int]", MonadRecTests[XorT[List, String, ?]].monadRec[Int, Int, Int]) + checkAll("MonadRec[XorT[List, String, ?]]", SerializableTests.serializable(MonadRec[XorT[List, String, ?]])) checkAll("XorT[List, ?, ?]", BifunctorTests[XorT[List, ?, ?]].bifunctor[Int, Int, Int, String, String, String]) checkAll("Bifunctor[XorT[List, ?, ?]]", SerializableTests.serializable(Bifunctor[XorT[List, ?, ?]])) checkAll("XorT[List, ?, ?]", BitraverseTests[XorT[List, ?, ?]].bitraverse[Option, Int, Int, Int, String, String, String]) diff --git a/tests/src/test/scala/cats/tests/XorTests.scala b/tests/src/test/scala/cats/tests/XorTests.scala index 6afa7308e0..ac984230f6 100644 --- a/tests/src/test/scala/cats/tests/XorTests.scala +++ b/tests/src/test/scala/cats/tests/XorTests.scala @@ -5,7 +5,7 @@ import cats.data.{NonEmptyList, Xor, XorT} import cats.data.Xor._ import cats.laws.discipline.{SemigroupKTests} import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.{BitraverseTests, TraverseTests, MonadErrorTests, SerializableTests, CartesianTests} +import cats.laws.discipline.{BitraverseTests, TraverseTests, MonadErrorTests, MonadRecTests, SerializableTests, CartesianTests} import cats.kernel.laws.{GroupLaws, OrderLaws} import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary._ @@ -27,6 +27,9 @@ class XorTests extends CatsSuite { checkAll("Xor[String, Int]", MonadErrorTests[Xor[String, ?], String].monadError[Int, Int, Int]) checkAll("MonadError[Xor, String]", SerializableTests.serializable(MonadError[Xor[String, ?], String])) + checkAll("Xor[String, Int]", MonadRecTests[Xor[String, ?]].monadRec[Int, Int, Int]) + checkAll("MonadRec[Xor[String, ?]]", SerializableTests.serializable(MonadRec[Xor[String, ?]])) + checkAll("Xor[String, Int] with Option", TraverseTests[Xor[String, ?]].traverse[Int, Int, Int, Int, Option, Option]) checkAll("Traverse[Xor[String,?]]", SerializableTests.serializable(Traverse[Xor[String, ?]])) From de5d911e02f2fb70d07da05ce1623b4d8b9b8d3e Mon Sep 17 00:00:00 2001 From: Tomas Mikula Date: Tue, 31 May 2016 01:00:19 -0400 Subject: [PATCH 183/185] MonadRec instances for Eval and StateT. --- core/src/main/scala/cats/Eval.scala | 10 +++- core/src/main/scala/cats/data/StateT.scala | 52 +++++++++++++------ .../src/test/scala/cats/tests/EvalTests.scala | 4 +- .../cats/tests/MonadRecInstancesTests.scala | 11 +++- .../test/scala/cats/tests/StateTTests.scala | 12 ++++- 5 files changed, 67 insertions(+), 22 deletions(-) diff --git a/core/src/main/scala/cats/Eval.scala b/core/src/main/scala/cats/Eval.scala index b0970cc808..a56e6215bf 100644 --- a/core/src/main/scala/cats/Eval.scala +++ b/core/src/main/scala/cats/Eval.scala @@ -1,6 +1,7 @@ package cats import scala.annotation.tailrec +import cats.data.Xor import cats.syntax.all._ /** @@ -294,14 +295,19 @@ object Eval extends EvalInstances { private[cats] trait EvalInstances extends EvalInstances0 { - implicit val evalBimonad: Bimonad[Eval] = - new Bimonad[Eval] { + implicit val evalBimonad: Bimonad[Eval] with MonadRec[Eval] = + new Bimonad[Eval] with MonadRec[Eval] { override def map[A, B](fa: Eval[A])(f: A => B): Eval[B] = fa.map(f) def pure[A](a: A): Eval[A] = Now(a) override def pureEval[A](la: Eval[A]): Eval[A] = la def flatMap[A, B](fa: Eval[A])(f: A => Eval[B]): Eval[B] = fa.flatMap(f) def extract[A](la: Eval[A]): A = la.value def coflatMap[A, B](fa: Eval[A])(f: Eval[A] => B): Eval[B] = Later(f(fa)) + def tailRecM[A, B](a: A)(f: A => Eval[A Xor B]): Eval[B] = + f(a).flatMap(_ match { + case Xor.Left(a1) => tailRecM(a1)(f) // recursion OK here, since flatMap is lazy + case Xor.Right(b) => Eval.now(b) + }) } implicit def evalOrder[A: Order]: Order[Eval[A]] = diff --git a/core/src/main/scala/cats/data/StateT.scala b/core/src/main/scala/cats/data/StateT.scala index e8f1406ac0..169e7832d7 100644 --- a/core/src/main/scala/cats/data/StateT.scala +++ b/core/src/main/scala/cats/data/StateT.scala @@ -135,22 +135,9 @@ object StateT extends StateTInstances { StateT(s => F.pure((s, a))) } -private[data] sealed abstract class StateTInstances { - implicit def catsDataMonadStateForStateT[F[_], S](implicit F: Monad[F]): MonadState[StateT[F, S, ?], S] = - new MonadState[StateT[F, S, ?], S] { - def pure[A](a: A): StateT[F, S, A] = - StateT.pure(a) - - def flatMap[A, B](fa: StateT[F, S, A])(f: A => StateT[F, S, B]): StateT[F, S, B] = - fa.flatMap(f) - - val get: StateT[F, S, S] = StateT(a => F.pure((a, a))) - - def set(s: S): StateT[F, S, Unit] = StateT(_ => F.pure((s, ()))) - - override def map[A, B](fa: StateT[F, S, A])(f: A => B): StateT[F, S, B] = - fa.map(f) - } +private[data] sealed abstract class StateTInstances extends StateTInstances1 { + implicit def catsDataMonadStateForStateT[F[_], S](implicit F0: Monad[F]): MonadState[StateT[F, S, ?], S] = + new StateTMonadState[F, S] { implicit def F = F0 } implicit def catsDataLiftForStateT[S]: TransLift.Aux[StateT[?[_], S, ?], Applicative] = new TransLift[StateT[?[_], S, ?]] { @@ -161,6 +148,11 @@ private[data] sealed abstract class StateTInstances { } +private[data] sealed abstract class StateTInstances1 { + implicit def catsDataMonadRecForStateT[F[_], S](implicit F0: MonadRec[F]): MonadRec[StateT[F, S, ?]] = + new StateTMonadRec[F, S] { implicit def F = F0 } +} + // To workaround SI-7139 `object State` needs to be defined inside the package object // together with the type alias. private[data] abstract class StateFunctions { @@ -193,3 +185,31 @@ private[data] abstract class StateFunctions { */ def set[S](s: S): State[S, Unit] = State(_ => (s, ())) } + +private[data] sealed trait StateTMonad[F[_], S] extends Monad[StateT[F, S, ?]] { + implicit def F: Monad[F] + + def pure[A](a: A): StateT[F, S, A] = + StateT.pure(a) + + 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] = + fa.map(f) +} + +private[data] sealed trait StateTMonadState[F[_], S] extends MonadState[StateT[F, S, ?], S] with StateTMonad[F, S] { + val get: StateT[F, S, S] = StateT(s => F.pure((s, s))) + + def set(s: S): StateT[F, S, Unit] = StateT(_ => F.pure((s, ()))) +} + +private[data] sealed trait StateTMonadRec[F[_], S] extends MonadRec[StateT[F, S, ?]] with StateTMonad[F, S] { + override implicit def F: MonadRec[F] + + def tailRecM[A, B](a: A)(f: A => StateT[F, S, A Xor B]): StateT[F, S, B] = + StateT[F, S, B](s => F.tailRecM[(S, A), (S, B)]((s, a)) { + case (s, a) => F.map(f(a).run(s)) { case (s, ab) => ab.bimap((s, _), (s, _)) } + }) +} diff --git a/tests/src/test/scala/cats/tests/EvalTests.scala b/tests/src/test/scala/cats/tests/EvalTests.scala index 76fb4f48d0..6e6ddaa44d 100644 --- a/tests/src/test/scala/cats/tests/EvalTests.scala +++ b/tests/src/test/scala/cats/tests/EvalTests.scala @@ -3,7 +3,7 @@ package tests import scala.math.min import cats.laws.ComonadLaws -import cats.laws.discipline.{CartesianTests, BimonadTests, SerializableTests} +import cats.laws.discipline.{BimonadTests, CartesianTests, MonadRecTests, SerializableTests} import cats.laws.discipline.arbitrary._ import cats.kernel.laws.{GroupLaws, OrderLaws} @@ -93,8 +93,10 @@ class EvalTests extends CatsSuite { { implicit val iso = CartesianTests.Isomorphisms.invariant[Eval] checkAll("Eval[Int]", BimonadTests[Eval].bimonad[Int, Int, Int]) + checkAll("Eval[Int]", MonadRecTests[Eval].monadRec[Int, Int, Int]) } checkAll("Bimonad[Eval]", SerializableTests.serializable(Bimonad[Eval])) + checkAll("MonadRec[Eval]", SerializableTests.serializable(MonadRec[Eval])) checkAll("Eval[Int]", GroupLaws[Eval[Int]].group) diff --git a/tests/src/test/scala/cats/tests/MonadRecInstancesTests.scala b/tests/src/test/scala/cats/tests/MonadRecInstancesTests.scala index 649cd60c01..89ad883e0d 100644 --- a/tests/src/test/scala/cats/tests/MonadRecInstancesTests.scala +++ b/tests/src/test/scala/cats/tests/MonadRecInstancesTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.data.{OptionT, Xor, XorT} +import cats.data.{OptionT, StateT, Xor, XorT} class MonadRecInstancesTests extends CatsSuite { def tailRecMStackSafety[M[_]](implicit M: MonadRec[M], Eq: Eq[M[Int]]): Unit = { @@ -38,4 +38,13 @@ class MonadRecInstancesTests extends CatsSuite { tailRecMStackSafety[List] } + test("tailRecM stack-safety for Eval") { + tailRecMStackSafety[Eval] + } + + test("tailRecM stack-safety for StateT") { + import StateTTests._ // import implicit Eq[StateT[...]] + tailRecMStackSafety[StateT[Option, Int, ?]] + } + } diff --git a/tests/src/test/scala/cats/tests/StateTTests.scala b/tests/src/test/scala/cats/tests/StateTTests.scala index 677af2ea02..1f7b3aee75 100644 --- a/tests/src/test/scala/cats/tests/StateTTests.scala +++ b/tests/src/test/scala/cats/tests/StateTTests.scala @@ -2,7 +2,7 @@ package cats package tests import cats.kernel.std.tuple._ -import cats.laws.discipline.{CartesianTests, MonadStateTests, SerializableTests} +import cats.laws.discipline.{CartesianTests, MonadRecTests, MonadStateTests, SerializableTests} import cats.data.{State, StateT} import cats.laws.discipline.eq._ import cats.laws.discipline.arbitrary._ @@ -116,14 +116,22 @@ class StateTTests extends CatsSuite { { implicit val iso = CartesianTests.Isomorphisms.invariant[StateT[Option, Int, ?]] + checkAll("StateT[Option, Int, Int]", MonadStateTests[StateT[Option, Int, ?], Int].monadState[Int, Int, Int]) - checkAll("MonadState[StateT[Option, ?, ?], Int]", SerializableTests.serializable(MonadState[StateT[Option, Int, ?], Int])) + checkAll("MonadState[StateT[Option, Int, ?], Int]", SerializableTests.serializable(MonadState[StateT[Option, Int, ?], Int])) + + checkAll("StateT[Option, Int, Int]", MonadRecTests[StateT[Option, Int, ?]].monadRec[Int, Int, Int]) + checkAll("MonadRec[StateT[Option, Int, ?]]", SerializableTests.serializable(MonadRec[StateT[Option, Int, ?]])) } { implicit val iso = CartesianTests.Isomorphisms.invariant[State[Long, ?]] + checkAll("State[Long, ?]", MonadStateTests[State[Long, ?], Long].monadState[Int, Int, Int]) checkAll("MonadState[State[Long, ?], Long]", SerializableTests.serializable(MonadState[State[Long, ?], Long])) + + checkAll("State[Long, ?]", MonadRecTests[State[Long, ?]].monadRec[Int, Int, Int]) + checkAll("MonadRec[State[Long, ?]]", SerializableTests.serializable(MonadRec[State[Long, ?]])) } } From 8509cd41765ef82dc01184d9a20af203c814c935 Mon Sep 17 00:00:00 2001 From: peterneyens Date: Mon, 30 May 2016 19:23:18 +0200 Subject: [PATCH 184/185] Rename NaturalTransformation to FunctionK. --- .../src/main/scala/cats/arrow/FunctionK.scala | 31 +++++++++++++++++++ .../cats/arrow/NaturalTransformation.scala | 31 ------------------- core/src/main/scala/cats/data/Coproduct.scala | 10 +++--- core/src/main/scala/cats/data/Kleisli.scala | 4 +-- core/src/main/scala/cats/package.scala | 3 +- docs/src/main/tut/freeapplicative.md | 10 +++--- docs/src/main/tut/freemonad.md | 8 ++--- free/src/main/scala/cats/free/Coyoneda.scala | 4 +-- free/src/main/scala/cats/free/Free.scala | 10 +++--- .../scala/cats/free/FreeApplicative.scala | 16 +++++----- .../test/scala/cats/free/CoyonedaTests.scala | 4 +-- .../cats/free/FreeApplicativeTests.scala | 14 ++++----- free/src/test/scala/cats/free/FreeTests.scala | 10 +++--- .../test/scala/cats/free/InjectTests.scala | 8 ++--- ...mationTests.scala => FunctionKTests.scala} | 14 ++++----- .../test/scala/cats/tests/KleisliTests.scala | 4 +-- 16 files changed, 90 insertions(+), 91 deletions(-) create mode 100644 core/src/main/scala/cats/arrow/FunctionK.scala delete mode 100644 core/src/main/scala/cats/arrow/NaturalTransformation.scala rename tests/src/test/scala/cats/tests/{NaturalTransformationTests.scala => FunctionKTests.scala} (77%) diff --git a/core/src/main/scala/cats/arrow/FunctionK.scala b/core/src/main/scala/cats/arrow/FunctionK.scala new file mode 100644 index 0000000000..5e1cf69a8a --- /dev/null +++ b/core/src/main/scala/cats/arrow/FunctionK.scala @@ -0,0 +1,31 @@ +package cats +package arrow + +import cats.data.{Xor, Coproduct} + +trait FunctionK[F[_], G[_]] extends Serializable { self => + def apply[A](fa: F[A]): G[A] + + def compose[E[_]](f: FunctionK[E, F]): FunctionK[E, G] = + new FunctionK[E, G] { + def apply[A](fa: E[A]): G[A] = self.apply(f(fa)) + } + + def andThen[H[_]](f: FunctionK[G, H]): FunctionK[F, H] = + f.compose(self) + + def or[H[_]](h: FunctionK[H,G]): FunctionK[Coproduct[F, H, ?], G] = + new FunctionK[Coproduct[F, H, ?], G] { + def apply[A](fa: Coproduct[F, H, A]): G[A] = fa.run match { + case Xor.Left(ff) => self(ff) + case Xor.Right(gg) => h(gg) + } + } +} + +object FunctionK { + def id[F[_]]: FunctionK[F, F] = + new FunctionK[F, F] { + def apply[A](fa: F[A]): F[A] = fa + } +} diff --git a/core/src/main/scala/cats/arrow/NaturalTransformation.scala b/core/src/main/scala/cats/arrow/NaturalTransformation.scala deleted file mode 100644 index 0b792bc9cf..0000000000 --- a/core/src/main/scala/cats/arrow/NaturalTransformation.scala +++ /dev/null @@ -1,31 +0,0 @@ -package cats -package arrow - -import cats.data.{Xor, Coproduct} - -trait NaturalTransformation[F[_], G[_]] extends Serializable { self => - def apply[A](fa: F[A]): G[A] - - def compose[E[_]](f: NaturalTransformation[E, F]): NaturalTransformation[E, G] = - new NaturalTransformation[E, G] { - def apply[A](fa: E[A]): G[A] = self.apply(f(fa)) - } - - def andThen[H[_]](f: NaturalTransformation[G, H]): NaturalTransformation[F, H] = - f.compose(self) - - def or[H[_]](h: NaturalTransformation[H,G]): NaturalTransformation[Coproduct[F, H, ?],G] = - new (NaturalTransformation[Coproduct[F, H, ?],G]) { - def apply[A](fa: Coproduct[F, H, A]): G[A] = fa.run match { - case Xor.Left(ff) => self(ff) - case Xor.Right(gg) => h(gg) - } - } -} - -object NaturalTransformation { - def id[F[_]]: NaturalTransformation[F, F] = - new NaturalTransformation[F, F] { - def apply[A](fa: F[A]): F[A] = fa - } -} diff --git a/core/src/main/scala/cats/data/Coproduct.scala b/core/src/main/scala/cats/data/Coproduct.scala index 1428abd123..09a16e8f9a 100644 --- a/core/src/main/scala/cats/data/Coproduct.scala +++ b/core/src/main/scala/cats/data/Coproduct.scala @@ -1,7 +1,7 @@ package cats package data -import cats.arrow.NaturalTransformation +import cats.arrow.FunctionK import cats.functor.Contravariant /** `F` on the left and `G` on the right of [[Xor]]. @@ -63,13 +63,13 @@ final case class Coproduct[F[_], G[_], A](run: F[A] Xor G[A]) { * * Example: * {{{ - * scala> import cats.arrow.NaturalTransformation + * scala> import cats.arrow.FunctionK * scala> import cats.data.Coproduct * scala> val listToOption = - * | new NaturalTransformation[List, Option] { + * | new FunctionK[List, Option] { * | def apply[A](fa: List[A]): Option[A] = fa.headOption * | } - * scala> val optionToOption = NaturalTransformation.id[Option] + * scala> val optionToOption = FunctionK.id[Option] * scala> val cp1: Coproduct[List, Option, Int] = Coproduct.leftc(List(1,2,3)) * scala> val cp2: Coproduct[List, Option, Int] = Coproduct.rightc(Some(4)) * scala> cp1.fold(listToOption, optionToOption) @@ -78,7 +78,7 @@ final case class Coproduct[F[_], G[_], A](run: F[A] Xor G[A]) { * res1: Option[Int] = Some(4) * }}} */ - def fold[H[_]](f: NaturalTransformation[F, H], g: NaturalTransformation[G, H]): H[A] = + def fold[H[_]](f: FunctionK[F, H], g: FunctionK[G, H]): H[A] = run.fold(f.apply, g.apply) } diff --git a/core/src/main/scala/cats/data/Kleisli.scala b/core/src/main/scala/cats/data/Kleisli.scala index ea4b58c33c..6f06bb1e17 100644 --- a/core/src/main/scala/cats/data/Kleisli.scala +++ b/core/src/main/scala/cats/data/Kleisli.scala @@ -1,7 +1,7 @@ package cats package data -import cats.arrow.{Arrow, Choice, Split, NaturalTransformation} +import cats.arrow.{Arrow, Choice, Split, FunctionK} import cats.functor.{Contravariant, Strong} /** @@ -48,7 +48,7 @@ final case class Kleisli[F[_], A, B](run: A => F[B]) { self => def local[AA](f: AA => A): Kleisli[F, AA, B] = Kleisli(f.andThen(run)) - def transform[G[_]](f: NaturalTransformation[F,G]): Kleisli[G, A, B] = + def transform[G[_]](f: FunctionK[F,G]): Kleisli[G, A, B] = Kleisli(a => f(run(a))) def lower(implicit F: Applicative[F]): Kleisli[F, A, F[B]] = diff --git a/core/src/main/scala/cats/package.scala b/core/src/main/scala/cats/package.scala index cc90780828..3bd885db4e 100644 --- a/core/src/main/scala/cats/package.scala +++ b/core/src/main/scala/cats/package.scala @@ -6,8 +6,7 @@ import cats.data.Xor */ package object cats { - type ~>[F[_], G[_]] = arrow.NaturalTransformation[F, G] - type <~[F[_], G[_]] = arrow.NaturalTransformation[G, F] + type ~>[F[_], G[_]] = arrow.FunctionK[F, G] type ⊥ = Nothing type ⊤ = Any diff --git a/docs/src/main/tut/freeapplicative.md b/docs/src/main/tut/freeapplicative.md index 238d44ded1..f9534a8e77 100644 --- a/docs/src/main/tut/freeapplicative.md +++ b/docs/src/main/tut/freeapplicative.md @@ -54,14 +54,14 @@ at this point. To make our program useful we need to interpret it. ```tut:silent import cats.Id -import cats.arrow.NaturalTransformation +import cats.arrow.FunctionK import cats.std.function._ // a function that takes a string as input type FromString[A] = String => A val compiler = - new NaturalTransformation[ValidationOp, FromString] { + new FunctionK[ValidationOp, FromString] { def apply[A](fa: ValidationOp[A]): String => A = str => fa match { @@ -103,7 +103,7 @@ import scala.concurrent.ExecutionContext.Implicits.global type ParValidator[A] = Kleisli[Future, String, A] val parCompiler = - new NaturalTransformation[ValidationOp, ParValidator] { + new FunctionK[ValidationOp, ParValidator] { def apply[A](fa: ValidationOp[A]): ParValidator[A] = Kleisli { str => fa match { @@ -130,7 +130,7 @@ import cats.std.list._ type Log[A] = Const[List[String], A] val logCompiler = - new NaturalTransformation[ValidationOp, Log] { + new FunctionK[ValidationOp, Log] { def apply[A](fa: ValidationOp[A]): Log[A] = fa match { case Size(size) => Const(List(s"size >= $size")) @@ -166,7 +166,7 @@ import cats.data.Prod type ValidateAndLog[A] = Prod[ParValidator, Log, A] val prodCompiler = - new NaturalTransformation[ValidationOp, ValidateAndLog] { + new FunctionK[ValidationOp, ValidateAndLog] { def apply[A](fa: ValidationOp[A]): ValidateAndLog[A] = { fa match { case Size(size) => diff --git a/docs/src/main/tut/freemonad.md b/docs/src/main/tut/freemonad.md index d8cf2a2848..2563623091 100644 --- a/docs/src/main/tut/freemonad.md +++ b/docs/src/main/tut/freemonad.md @@ -166,14 +166,14 @@ DSL. By itself, this DSL only represents a sequence of operations To do this, we will use a *natural transformation* between type containers. Natural transformations go between types like `F[_]` and `G[_]` (this particular transformation would be written as -`NaturalTransformation[F,G]` or as done here using the symbolic +`FunctionK[F,G]` or as done here using the symbolic alternative as `F ~> G`). In our case, we will use a simple mutable map to represent our key value store: ```tut:silent -import cats.arrow.NaturalTransformation +import cats.arrow.FunctionK import cats.{Id, ~>} import scala.collection.mutable @@ -244,7 +244,7 @@ recursive structure by: This operation is called `Free.foldMap`: ```scala -final def foldMap[M[_]](f: NaturalTransformation[S,M])(M: Monad[M]): M[A] = ... +final def foldMap[M[_]](f: FunctionK[S,M])(M: Monad[M]): M[A] = ... ``` `M` must be a `Monad` to be flattenable (the famous monoid aspect @@ -366,7 +366,7 @@ def program(implicit I : Interacts[CatsApp], D : DataSource[CatsApp]): Free[Cats } ``` -Finally we write one interpreter per ADT and combine them with a `NaturalTransformation` to `Coproduct` so they can be +Finally we write one interpreter per ADT and combine them with a `FunctionK` to `Coproduct` so they can be compiled and applied to our `Free` program. ```tut:invisible diff --git a/free/src/main/scala/cats/free/Coyoneda.scala b/free/src/main/scala/cats/free/Coyoneda.scala index 9afa427ca3..2bf8ec5774 100644 --- a/free/src/main/scala/cats/free/Coyoneda.scala +++ b/free/src/main/scala/cats/free/Coyoneda.scala @@ -1,7 +1,7 @@ package cats package free -import cats.arrow.NaturalTransformation +import cats.arrow.FunctionK /** * The dual view of the Yoneda lemma. Also a free functor on `F`. @@ -38,7 +38,7 @@ sealed abstract class Coyoneda[F[_], A] extends Serializable { self => final def map[B](f: A => B): Aux[F, B, Pivot] = apply(fi)(f compose k) - final def transform[G[_]](f: NaturalTransformation[F,G]): Aux[G, A, Pivot] = + final def transform[G[_]](f: FunctionK[F,G]): Aux[G, A, Pivot] = apply(f(fi))(k) } diff --git a/free/src/main/scala/cats/free/Free.scala b/free/src/main/scala/cats/free/Free.scala index c4e2233b7b..f34934daa8 100644 --- a/free/src/main/scala/cats/free/Free.scala +++ b/free/src/main/scala/cats/free/Free.scala @@ -4,7 +4,7 @@ package free import scala.annotation.tailrec import cats.data.Xor, Xor.{Left, Right} -import cats.arrow.NaturalTransformation +import cats.arrow.FunctionK object Free { /** @@ -135,7 +135,7 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { * Run to completion, mapping the suspension with the given transformation at each step and * accumulating into the monad `M`. */ - final def foldMap[M[_]](f: NaturalTransformation[S,M])(implicit M: Monad[M]): M[A] = + final def foldMap[M[_]](f: FunctionK[S,M])(implicit M: Monad[M]): M[A] = step match { case Pure(a) => M.pure(a) case Suspend(s) => f(s) @@ -147,13 +147,13 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { * using the given natural transformation. * Be careful if your natural transformation is effectful, effects are applied by mapSuspension. */ - final def mapSuspension[T[_]](f: NaturalTransformation[S,T]): Free[T, A] = + final def mapSuspension[T[_]](f: FunctionK[S,T]): Free[T, A] = foldMap[Free[T, ?]] { - new NaturalTransformation[S, Free[T, ?]] { + new FunctionK[S, Free[T, ?]] { def apply[B](fa: S[B]): Free[T, B] = Suspend(f(fa)) } }(Free.freeMonad) - final def compile[T[_]](f: NaturalTransformation[S,T]): Free[T, A] = mapSuspension(f) + final def compile[T[_]](f: FunctionK[S,T]): Free[T, A] = mapSuspension(f) } diff --git a/free/src/main/scala/cats/free/FreeApplicative.scala b/free/src/main/scala/cats/free/FreeApplicative.scala index e5a9268ac8..b581ca5a4d 100644 --- a/free/src/main/scala/cats/free/FreeApplicative.scala +++ b/free/src/main/scala/cats/free/FreeApplicative.scala @@ -1,7 +1,7 @@ package cats package free -import cats.arrow.NaturalTransformation +import cats.arrow.FunctionK import cats.data.Const /** Applicative Functor for Free */ @@ -27,7 +27,7 @@ sealed abstract class FreeApplicative[F[_], A] extends Product with Serializable /** Interprets/Runs the sequence of operations using the semantics of Applicative G * Tail recursive only if G provides tail recursive interpretation (ie G is FreeMonad) */ - final def foldMap[G[_]](f: NaturalTransformation[F,G])(implicit G: Applicative[G]): G[A] = + final def foldMap[G[_]](f: FunctionK[F,G])(implicit G: Applicative[G]): G[A] = this match { case Pure(a) => G.pure(a) case Ap(pivot, fn) => G.map2(f(pivot), fn.foldMap(f))((a, g) => g(a)) @@ -37,26 +37,26 @@ sealed abstract class FreeApplicative[F[_], A] extends Product with Serializable * Tail recursive only if `F` provides tail recursive interpretation. */ final def fold(implicit F: Applicative[F]): F[A] = - foldMap(NaturalTransformation.id[F]) + foldMap(FunctionK.id[F]) /** Interpret this algebra into another FreeApplicative */ - final def compile[G[_]](f: NaturalTransformation[F,G]): FA[G, A] = + final def compile[G[_]](f: FunctionK[F,G]): FA[G, A] = foldMap[FA[G, ?]] { - new NaturalTransformation[F, FA[G, ?]] { + new FunctionK[F, FA[G, ?]] { def apply[B](fa: F[B]): FA[G, B] = lift(f(fa)) } } /** Interpret this algebra into a Monoid */ - final def analyze[M:Monoid](f: NaturalTransformation[F,λ[α => M]]): M = - foldMap[Const[M, ?]](new (NaturalTransformation[F,Const[M, ?]]) { + final def analyze[M:Monoid](f: FunctionK[F,λ[α => M]]): M = + foldMap[Const[M, ?]](new (FunctionK[F,Const[M, ?]]) { def apply[X](x: F[X]): Const[M,X] = Const(f(x)) }).getConst /** Compile this FreeApplicative algebra into a Free algebra. */ final def monad: Free[F, A] = foldMap[Free[F, ?]] { - new NaturalTransformation[F, Free[F, ?]] { + new FunctionK[F, Free[F, ?]] { def apply[B](fa: F[B]): Free[F, B] = Free.liftF(fa) } } diff --git a/free/src/test/scala/cats/free/CoyonedaTests.scala b/free/src/test/scala/cats/free/CoyonedaTests.scala index 26cbe38193..45e708acfa 100644 --- a/free/src/test/scala/cats/free/CoyonedaTests.scala +++ b/free/src/test/scala/cats/free/CoyonedaTests.scala @@ -2,7 +2,7 @@ package cats package free import cats.tests.CatsSuite -import cats.arrow.NaturalTransformation +import cats.arrow.FunctionK import cats.laws.discipline.{FunctorTests, SerializableTests} import org.scalacheck.Arbitrary @@ -27,7 +27,7 @@ class CoyonedaTests extends CatsSuite { test("transform and run is same as applying natural trans") { val nt = - new NaturalTransformation[Option, List] { + new FunctionK[Option, List] { def apply[A](fa: Option[A]): List[A] = fa.toList } val o = Option("hello") diff --git a/free/src/test/scala/cats/free/FreeApplicativeTests.scala b/free/src/test/scala/cats/free/FreeApplicativeTests.scala index de60d901ec..91096c92df 100644 --- a/free/src/test/scala/cats/free/FreeApplicativeTests.scala +++ b/free/src/test/scala/cats/free/FreeApplicativeTests.scala @@ -2,7 +2,7 @@ package cats package free import cats.tests.CatsSuite -import cats.arrow.NaturalTransformation +import cats.arrow.FunctionK import cats.laws.discipline.{CartesianTests, ApplicativeTests, SerializableTests} import cats.data.State @@ -18,7 +18,7 @@ class FreeApplicativeTests extends CatsSuite { implicit def freeApplicativeEq[S[_]: Applicative, A](implicit SA: Eq[S[A]]): Eq[FreeApplicative[S, A]] = new Eq[FreeApplicative[S, A]] { def eqv(a: FreeApplicative[S, A], b: FreeApplicative[S, A]): Boolean = { - val nt = NaturalTransformation.id[S] + val nt = FunctionK.id[S] SA.eqv(a.foldMap(nt), b.foldMap(nt)) } } @@ -44,7 +44,7 @@ class FreeApplicativeTests extends CatsSuite { val x = FreeApplicative.lift[Id, Int](1) val y = FreeApplicative.pure[Id, Int](2) val f = x.map(i => (j: Int) => i + j) - val nt = NaturalTransformation.id[Id] + val nt = FunctionK.id[Id] val r1 = y.ap(f) val r2 = r1.compile(nt) r1.foldMap(nt) should === (r2.foldMap(nt)) @@ -57,7 +57,7 @@ class FreeApplicativeTests extends CatsSuite { val r1 = y.ap(f) val r2 = r1.monad val nt = - new NaturalTransformation[Id, Id] { + new FunctionK[Id, Id] { def apply[A](fa: Id[A]): Id[A] = fa } r1.foldMap(nt) should === (r2.foldMap(nt)) @@ -75,7 +75,7 @@ class FreeApplicativeTests extends CatsSuite { test("FreeApplicative#analyze") { type G[A] = List[Int] - val countingNT = new NaturalTransformation[List, G] { + val countingNT = new FunctionK[List, G] { def apply[A](la: List[A]): G[A] = List(la.length) } @@ -97,7 +97,7 @@ class FreeApplicativeTests extends CatsSuite { type Tracked[A] = State[String, A] - val f: NaturalTransformation[Foo,Tracked] = new NaturalTransformation[Foo,Tracked] { + val f: FunctionK[Foo,Tracked] = new FunctionK[Foo,Tracked] { def apply[A](fa: Foo[A]): Tracked[A] = State[String, A]{ s0 => (s0 + fa.toString + ";", fa.getA) } @@ -120,7 +120,7 @@ class FreeApplicativeTests extends CatsSuite { val z = Apply[Dsl].map2(x, y)((_, _) => ()) - val asString: NaturalTransformation[Id,λ[α => String]] = new NaturalTransformation[Id,λ[α => String]] { + val asString: FunctionK[Id,λ[α => String]] = new FunctionK[Id,λ[α => String]] { def apply[A](a: A): String = a.toString } diff --git a/free/src/test/scala/cats/free/FreeTests.scala b/free/src/test/scala/cats/free/FreeTests.scala index 5291c60d5c..bf2574ae52 100644 --- a/free/src/test/scala/cats/free/FreeTests.scala +++ b/free/src/test/scala/cats/free/FreeTests.scala @@ -2,7 +2,7 @@ package cats package free import cats.tests.CatsSuite -import cats.arrow.NaturalTransformation +import cats.arrow.FunctionK import cats.data.Xor import cats.laws.discipline.{CartesianTests, MonadRecTests, SerializableTests} import cats.laws.discipline.arbitrary.function0Arbitrary @@ -20,7 +20,7 @@ class FreeTests extends CatsSuite { test("mapSuspension id"){ forAll { x: Free[List, Int] => - x.mapSuspension(NaturalTransformation.id[List]) should === (x) + x.mapSuspension(FunctionK.id[List]) should === (x) } } @@ -39,7 +39,7 @@ class FreeTests extends CatsSuite { test("mapSuspension consistent with foldMap"){ forAll { x: Free[List, Int] => val mapped = x.mapSuspension(headOptionU) - val folded = mapped.foldMap(NaturalTransformation.id[Option]) + val folded = mapped.foldMap(FunctionK.id[Option]) folded should === (x.foldMap(headOptionU)) } } @@ -64,7 +64,7 @@ class FreeTests extends CatsSuite { z <- if (j<10000) a(j) else Free.pure[FTestApi, Int](j) } yield z - def runner: NaturalTransformation[FTestApi,Id] = new NaturalTransformation[FTestApi,Id] { + def runner: FunctionK[FTestApi,Id] = new FunctionK[FTestApi,Id] { def apply[A](fa: FTestApi[A]): Id[A] = fa match { case TB(i) => i+1 } @@ -85,7 +85,7 @@ object FreeTests extends FreeTestsInstances { } sealed trait FreeTestsInstances { - val headOptionU: NaturalTransformation[List,Option] = new NaturalTransformation[List,Option] { + val headOptionU: FunctionK[List,Option] = new FunctionK[List,Option] { def apply[A](fa: List[A]): Option[A] = fa.headOption } diff --git a/free/src/test/scala/cats/free/InjectTests.scala b/free/src/test/scala/cats/free/InjectTests.scala index e957da6f00..ae922e63ba 100644 --- a/free/src/test/scala/cats/free/InjectTests.scala +++ b/free/src/test/scala/cats/free/InjectTests.scala @@ -1,7 +1,7 @@ package cats package free -import cats.arrow.NaturalTransformation +import cats.arrow.FunctionK import cats.tests.CatsSuite import cats.data.{Xor, Coproduct} import org.scalacheck._ @@ -40,19 +40,19 @@ class InjectTests extends CatsSuite { implicit def test2Arbitrary[A](implicit seqArb: Arbitrary[Int], intAArb : Arbitrary[Int => A]): Arbitrary[Test2[A]] = Arbitrary(for {s <- seqArb.arbitrary; f <- intAArb.arbitrary} yield Test2(s, f)) - object Test1Interpreter extends NaturalTransformation[Test1Algebra,Id] { + object Test1Interpreter extends FunctionK[Test1Algebra,Id] { override def apply[A](fa: Test1Algebra[A]): Id[A] = fa match { case Test1(k, h) => h(k) } } - object Test2Interpreter extends NaturalTransformation[Test2Algebra,Id] { + object Test2Interpreter extends FunctionK[Test2Algebra,Id] { override def apply[A](fa: Test2Algebra[A]): Id[A] = fa match { case Test2(k, h) => h(k) } } - val coProductInterpreter: NaturalTransformation[T,Id] = Test1Interpreter or Test2Interpreter + val coProductInterpreter: FunctionK[T,Id] = Test1Interpreter or Test2Interpreter val x: Free[T, Int] = Free.inject[Test1Algebra, T](Test1(1, identity)) diff --git a/tests/src/test/scala/cats/tests/NaturalTransformationTests.scala b/tests/src/test/scala/cats/tests/FunctionKTests.scala similarity index 77% rename from tests/src/test/scala/cats/tests/NaturalTransformationTests.scala rename to tests/src/test/scala/cats/tests/FunctionKTests.scala index 3c69bc8a57..15682de62a 100644 --- a/tests/src/test/scala/cats/tests/NaturalTransformationTests.scala +++ b/tests/src/test/scala/cats/tests/FunctionKTests.scala @@ -1,18 +1,18 @@ package cats package tests -import cats.arrow.NaturalTransformation +import cats.arrow.FunctionK import cats.data.Coproduct -class NaturalTransformationTests extends CatsSuite { +class FunctionKTests extends CatsSuite { val listToOption = - new NaturalTransformation[List, Option] { + new FunctionK[List, Option] { def apply[A](fa: List[A]): Option[A] = fa.headOption } val optionToList = - new NaturalTransformation[Option, List] { + new FunctionK[Option, List] { def apply[A](fa: Option[A]): List[A] = fa.toList } @@ -28,11 +28,11 @@ class NaturalTransformationTests extends CatsSuite { case class Test2[A](v : A) extends Test2Algebra[A] - object Test1NT extends NaturalTransformation[Test1Algebra,Id] { + object Test1NT extends FunctionK[Test1Algebra,Id] { override def apply[A](fa: Test1Algebra[A]): Id[A] = fa.v } - object Test2NT extends NaturalTransformation[Test2Algebra,Id] { + object Test2NT extends FunctionK[Test2Algebra,Id] { override def apply[A](fa: Test2Algebra[A]): Id[A] = fa.v } @@ -54,7 +54,7 @@ class NaturalTransformationTests extends CatsSuite { test("id is identity") { forAll { (list: List[Int]) => - NaturalTransformation.id[List].apply(list) should === (list) + FunctionK.id[List].apply(list) should === (list) } } diff --git a/tests/src/test/scala/cats/tests/KleisliTests.scala b/tests/src/test/scala/cats/tests/KleisliTests.scala index bcfd7f1ba5..1b831986b5 100644 --- a/tests/src/test/scala/cats/tests/KleisliTests.scala +++ b/tests/src/test/scala/cats/tests/KleisliTests.scala @@ -1,7 +1,7 @@ package cats package tests -import cats.arrow.{Arrow, Choice, Split, NaturalTransformation} +import cats.arrow.{Arrow, Choice, Split, FunctionK} import cats.data.{XorT, Kleisli, Reader} import cats.functor.{Contravariant, Strong} import cats.laws.discipline._ @@ -126,7 +126,7 @@ class KleisliTests extends CatsSuite { test("transform") { val opt = Kleisli { (x: Int) => Option(x.toDouble) } - val optToList = new NaturalTransformation[Option,List] { def apply[A](fa: Option[A]): List[A] = fa.toList } + val optToList = new FunctionK[Option,List] { def apply[A](fa: Option[A]): List[A] = fa.toList } val list = opt.transform(optToList) val is = 0.to(10).toList From 07de29206f92a0107fbf23f7f6f04e2eff543297 Mon Sep 17 00:00:00 2001 From: Tomas Mikula Date: Mon, 30 May 2016 20:32:04 -0400 Subject: [PATCH 185/185] Make Free.foldMap stack-safe. Fixes #721. The fix is possible by strengthening constraints on the target monad from Monad to MonadRec. --- free/src/main/scala/cats/free/Free.scala | 12 ++++++------ free/src/test/scala/cats/free/FreeTests.scala | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/free/src/main/scala/cats/free/Free.scala b/free/src/main/scala/cats/free/Free.scala index f34934daa8..93159b07b9 100644 --- a/free/src/main/scala/cats/free/Free.scala +++ b/free/src/main/scala/cats/free/Free.scala @@ -135,12 +135,12 @@ sealed abstract class Free[S[_], A] extends Product with Serializable { * Run to completion, mapping the suspension with the given transformation at each step and * accumulating into the monad `M`. */ - final def foldMap[M[_]](f: FunctionK[S,M])(implicit M: Monad[M]): M[A] = - step match { - case Pure(a) => M.pure(a) - case Suspend(s) => f(s) - case Gosub(c, g) => M.flatMap(c.foldMap(f))(cc => g(cc).foldMap(f)) - } + final def foldMap[M[_]](f: FunctionK[S,M])(implicit M: MonadRec[M]): M[A] = + M.tailRecM(this)(_.step match { + case Pure(a) => M.pure(Xor.right(a)) + case Suspend(sa) => M.map(f(sa))(Xor.right) + case Gosub(c, g) => M.map(c.foldMap(f))(cc => Xor.left(g(cc))) + }) /** * Compile your Free into another language by changing the suspension functor diff --git a/free/src/test/scala/cats/free/FreeTests.scala b/free/src/test/scala/cats/free/FreeTests.scala index bf2574ae52..09288b275c 100644 --- a/free/src/test/scala/cats/free/FreeTests.scala +++ b/free/src/test/scala/cats/free/FreeTests.scala @@ -51,7 +51,7 @@ class FreeTests extends CatsSuite { fa should === (Free.pure[Option, Int](n)) } - ignore("foldMap is stack safe") { + test("foldMap is stack safe") { trait FTestApi[A] case class TB(i: Int) extends FTestApi[Int]