Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added a new foldRight lazy law, move forallLazy and existLazy laws #2817

Merged
merged 8 commits into from
May 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 16 additions & 12 deletions core/src/main/scala/cats/UnorderedFoldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ 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.
*
* 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.
Expand All @@ -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] }

}
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/data/NonEmptyChain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/scala/cats/instances/map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions core/src/main/scala/cats/instances/set.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]] {
Expand Down
10 changes: 10 additions & 0 deletions laws/src/main/scala/cats/laws/FoldableLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

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

We use += here but not below. Any reason for the difference?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no real reason except this one is written by me and the other ones are from before. Just changed the other ones to be more consistent.

Eval.now("not empty")
}
.value
i == (if (F.isEmpty(fa)) 0 else 1)
}

def leftFoldConsistentWithFoldMap[A, B](
fa: F[A],
f: A => B
Expand Down
4 changes: 2 additions & 2 deletions laws/src/main/scala/cats/laws/UnorderedFoldableLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
3 changes: 1 addition & 2 deletions laws/src/main/scala/cats/laws/discipline/FoldableTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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] _),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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] _)
)
}

Expand Down
5 changes: 4 additions & 1 deletion tests/src/test/scala/cats/tests/UnorderedFoldableSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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") {
Expand Down