diff --git a/core/src/main/scala/cats/UnorderedFoldable.scala b/core/src/main/scala/cats/UnorderedFoldable.scala index a6a88d215a..4336908d1f 100644 --- a/core/src/main/scala/cats/UnorderedFoldable.scala +++ b/core/src/main/scala/cats/UnorderedFoldable.scala @@ -29,7 +29,7 @@ import cats.instances.long._ * If there are no elements, the result is `false`. */ def exists[A](fa: F[A])(p: A => Boolean): Boolean = - unorderedFoldMap(fa)(a => Eval.later(p(a)))(UnorderedFoldable.commutativeMonoidEval(UnorderedFoldable.orMonoid)).value + unorderedFoldMap(fa)(a => Eval.later(p(a)))(UnorderedFoldable.orEvalMonoid).value /** * Check whether all elements satisfy the predicate. @@ -37,7 +37,7 @@ import cats.instances.long._ * If there are no elements, the result is `true`. */ def forall[A](fa: F[A])(p: A => Boolean): Boolean = - unorderedFoldMap(fa)(a => Eval.later(p(a)))(UnorderedFoldable.commutativeMonoidEval(UnorderedFoldable.andMonoid)).value + unorderedFoldMap(fa)(a => Eval.later(p(a)))(UnorderedFoldable.andEvalMonoid).value /** * The size of this UnorderedFoldable. @@ -51,19 +51,23 @@ import cats.instances.long._ } object UnorderedFoldable { - private val orMonoid: CommutativeMonoid[Boolean] = new CommutativeMonoid[Boolean] { - val empty: Boolean = false + private val orEvalMonoid: CommutativeMonoid[Eval[Boolean]] = new CommutativeMonoid[Eval[Boolean]] { + val empty: Eval[Boolean] = Eval.False - def combine(x: Boolean, y: Boolean): Boolean = x || y + def combine(lx: Eval[Boolean], ly: Eval[Boolean]): Eval[Boolean] = + lx.flatMap { + case true => Eval.True + case false => ly + } } - private val andMonoid: CommutativeMonoid[Boolean] = new CommutativeMonoid[Boolean] { - val empty: Boolean = true + private val andEvalMonoid: CommutativeMonoid[Eval[Boolean]] = new CommutativeMonoid[Eval[Boolean]] { + val empty: Eval[Boolean] = Eval.True - def combine(x: Boolean, y: Boolean): Boolean = x && y + def combine(lx: Eval[Boolean], ly: Eval[Boolean]): Eval[Boolean] = + lx.flatMap { + case true => ly + case false => Eval.False + } } - - private def commutativeMonoidEval[A: CommutativeMonoid]: CommutativeMonoid[Eval[A]] = - new EvalMonoid[A] with CommutativeMonoid[Eval[A]] { val algebra = Monoid[A] } - } diff --git a/core/src/main/scala/cats/data/NonEmptyChain.scala b/core/src/main/scala/cats/data/NonEmptyChain.scala index c1ae0faf8b..035ae7e097 100644 --- a/core/src/main/scala/cats/data/NonEmptyChain.scala +++ b/core/src/main/scala/cats/data/NonEmptyChain.scala @@ -452,7 +452,7 @@ sealed abstract private[data] class NonEmptyChainInstances extends NonEmptyChain fa.foldLeft(b)(f) override def foldRight[A, B](fa: NonEmptyChain[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - fa.foldRight(lb)(f) + Foldable[Chain].foldRight(fa.toChain, lb)(f) override def foldMap[A, B](fa: NonEmptyChain[A])(f: A => B)(implicit B: Monoid[B]): B = B.combineAll(fa.toChain.iterator.map(f)) diff --git a/core/src/main/scala/cats/instances/map.scala b/core/src/main/scala/cats/instances/map.scala index e5669de7c6..ff7dc0c264 100644 --- a/core/src/main/scala/cats/instances/map.scala +++ b/core/src/main/scala/cats/instances/map.scala @@ -84,6 +84,10 @@ trait MapInstances extends cats.kernel.instances.MapInstances { override def unorderedFold[A](fa: Map[K, A])(implicit A: CommutativeMonoid[A]): A = A.combineAll(fa.values) + override def forall[A](fa: Map[K, A])(p: A => Boolean): Boolean = fa.forall(pair => p(pair._2)) + + override def exists[A](fa: Map[K, A])(p: A => Boolean): Boolean = fa.exists(pair => p(pair._2)) + } // scalastyle:on method.length diff --git a/core/src/main/scala/cats/instances/set.scala b/core/src/main/scala/cats/instances/set.scala index ba62b96b63..6abb316fce 100644 --- a/core/src/main/scala/cats/instances/set.scala +++ b/core/src/main/scala/cats/instances/set.scala @@ -32,7 +32,11 @@ trait SetInstances extends cats.kernel.instances.SetInstances { override def forall[A](fa: Set[A])(p: A => Boolean): Boolean = fa.forall(p) + override def exists[A](fa: Set[A])(p: A => Boolean): Boolean = + fa.exists(p) + override def isEmpty[A](fa: Set[A]): Boolean = fa.isEmpty + } implicit def catsStdShowForSet[A: Show]: Show[Set[A]] = new Show[Set[A]] { diff --git a/laws/src/main/scala/cats/laws/FoldableLaws.scala b/laws/src/main/scala/cats/laws/FoldableLaws.scala index 64cb6e83ca..b959ec4be0 100644 --- a/laws/src/main/scala/cats/laws/FoldableLaws.scala +++ b/laws/src/main/scala/cats/laws/FoldableLaws.scala @@ -8,6 +8,16 @@ import scala.collection.mutable trait FoldableLaws[F[_]] extends UnorderedFoldableLaws[F] { implicit def F: Foldable[F] + def foldRightLazy[A](fa: F[A]): Boolean = { + var i = 0 + F.foldRight(fa, Eval.now("empty")) { (_, _) => + i += 1 + Eval.now("not empty") + } + .value + i == (if (F.isEmpty(fa)) 0 else 1) + } + def leftFoldConsistentWithFoldMap[A, B]( fa: F[A], f: A => B diff --git a/laws/src/main/scala/cats/laws/UnorderedFoldableLaws.scala b/laws/src/main/scala/cats/laws/UnorderedFoldableLaws.scala index 7df29a0c3e..45b765c7ee 100644 --- a/laws/src/main/scala/cats/laws/UnorderedFoldableLaws.scala +++ b/laws/src/main/scala/cats/laws/UnorderedFoldableLaws.scala @@ -24,7 +24,7 @@ trait UnorderedFoldableLaws[F[_]] { def existsLazy[A](fa: F[A]): Boolean = { var i = 0 F.exists(fa) { _ => - i = i + 1 + i += 1 true } i == (if (F.isEmpty(fa)) 0 else 1) @@ -33,7 +33,7 @@ trait UnorderedFoldableLaws[F[_]] { def forallLazy[A](fa: F[A]): Boolean = { var i = 0 F.forall(fa) { _ => - i = i + 1 + i += 1 false } i == (if (F.isEmpty(fa)) 0 else 1) diff --git a/laws/src/main/scala/cats/laws/discipline/FoldableTests.scala b/laws/src/main/scala/cats/laws/discipline/FoldableTests.scala index 65d10b2081..2ef720cef2 100644 --- a/laws/src/main/scala/cats/laws/discipline/FoldableTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/FoldableTests.scala @@ -27,10 +27,9 @@ trait FoldableTests[F[_]] extends UnorderedFoldableTests[F] { parent = Some(unorderedFoldable[A, B]), "foldLeft consistent with foldMap" -> forAll(laws.leftFoldConsistentWithFoldMap[A, B] _), "foldRight consistent with foldMap" -> forAll(laws.rightFoldConsistentWithFoldMap[A, B] _), + "foldRight is lazy" -> forAll(laws.foldRightLazy[A] _), "ordered constistency" -> forAll(laws.orderedConsistency[A] _), "exists consistent with find" -> forAll(laws.existsConsistentWithFind[A] _), - "exists is lazy" -> forAll(laws.existsLazy[A] _), - "forall is lazy" -> forAll(laws.forallLazy[A] _), "foldM identity" -> forAll(laws.foldMIdentity[A, B] _), "reduceLeftOption consistent with reduceLeftToOption" -> forAll(laws.reduceLeftOptionConsistentWithReduceLeftToOption[A] _), diff --git a/laws/src/main/scala/cats/laws/discipline/UnorderedFoldableTests.scala b/laws/src/main/scala/cats/laws/discipline/UnorderedFoldableTests.scala index 321e84a5f0..2ec35220de 100644 --- a/laws/src/main/scala/cats/laws/discipline/UnorderedFoldableTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/UnorderedFoldableTests.scala @@ -25,7 +25,9 @@ trait UnorderedFoldableTests[F[_]] extends Laws { "unorderedFold consistent with unorderedFoldMap" -> forAll(laws.unorderedFoldConsistentWithUnorderedFoldMap[A] _), "forall consistent with exists" -> forAll(laws.forallConsistentWithExists[A] _), "forall true if empty" -> forAll(laws.forallEmpty[A] _), - "nonEmpty reference" -> forAll(laws.nonEmptyRef[A] _) + "nonEmpty reference" -> forAll(laws.nonEmptyRef[A] _), + "exists is lazy" -> forAll(laws.existsLazy[A] _), + "forall is lazy" -> forAll(laws.forallLazy[A] _) ) } diff --git a/tests/src/test/scala/cats/tests/UnorderedFoldableSuite.scala b/tests/src/test/scala/cats/tests/UnorderedFoldableSuite.scala index 3970fdc441..e6e85b6282 100644 --- a/tests/src/test/scala/cats/tests/UnorderedFoldableSuite.scala +++ b/tests/src/test/scala/cats/tests/UnorderedFoldableSuite.scala @@ -4,8 +4,10 @@ package tests import org.scalacheck.Arbitrary import cats.instances.all._ import cats.kernel.CommutativeMonoid +import cats.laws.discipline.UnorderedFoldableTests -sealed abstract class UnorderedFoldableSuite[F[_]](name: String)(implicit ArbFString: Arbitrary[F[String]]) +sealed abstract class UnorderedFoldableSuite[F[_]](name: String)(implicit ArbFString: Arbitrary[F[String]], + ArbFInt: Arbitrary[F[Int]]) extends CatsSuite { def iterator[T](fa: F[T]): Iterator[T] @@ -42,6 +44,7 @@ sealed abstract class UnorderedFoldableSuite[F[_]](name: String)(implicit ArbFSt fa.count(Function.const(true)) should ===(fa.size) } } + checkAll("F[Int]", UnorderedFoldableTests[F](instance).unorderedFoldable[Int, Int]) } final class UnorderedFoldableSetSuite extends UnorderedFoldableSuite[Set]("set") {