Skip to content

Commit

Permalink
Merge pull request #4183 from BalmungSan/fix-contains
Browse files Browse the repository at this point in the history
Move contain_ to UnorderedFoldable
  • Loading branch information
armanbilge authored Apr 14, 2022
2 parents 3e43f35 + 32ec37c commit fe40bc2
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 51 deletions.
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/UnorderedFoldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ import simulacrum.{noop, typeclass}
def unorderedFold[A: CommutativeMonoid](fa: F[A]): A =
unorderedFoldMap(fa)(identity)

/**
* Tests if `fa` contains `v` using the `Eq` instance for `A`
*/
def contains_[A](fa: F[A], v: A)(implicit ev: Eq[A]): Boolean =
exists(fa)(a => ev.eqv(a, v))

/**
* Returns true if there are no elements. Otherwise false.
*/
Expand Down
105 changes: 56 additions & 49 deletions core/src/main/scala/cats/syntax/foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ final class NestedFoldableOps[F[_], G[_], A](private val fga: F[G[A]]) extends A
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> import cats.syntax.all._
*
* scala> val l: List[Set[Int]] = List(Set(1, 2), Set(2, 3), Set(3, 4))
* scala> l.foldK
Expand All @@ -70,29 +70,15 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {
def foldA[G[_], B](implicit F: Foldable[F], ev: A <:< G[B], G: Applicative[G], B: Monoid[B]): G[B] =
F.foldA[G, B](fa.asInstanceOf[F[G[B]]])

/**
* test if `F[A]` contains an `A`, named contains_ to avoid conflict with existing contains which uses universal equality
*
* Example:
* {{{
* scala> import cats.implicits._
*
* scala> val l: List[Int] = List(1, 2, 3, 4)
* scala> l.contains_(1)
* res0: Boolean = true
* scala> l.contains_(5)
* res1: Boolean = false
* }}}
*/
def contains_(v: A)(implicit ev: Eq[A], F: Foldable[F]): Boolean =
F.exists(fa)(ev.eqv(_, v))
private[syntax] def contains_(v: A, eq: Eq[A], F: Foldable[F]): Boolean =
F.contains_(fa, v)(eq)

/**
* Intercalate with a prefix and a suffix
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> import cats.syntax.all._
*
* scala> val l: List[String] = List("1", "2", "3")
* scala> l.foldSmash("List(", ",", ")")
Expand All @@ -109,7 +95,7 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> import cats.syntax.all._
*
* scala> val l: List[Int] = List(1, 2, 3)
* scala> l.mkString_("L[", ";", "]")
Expand Down Expand Up @@ -139,7 +125,7 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {
*
* For example:
* {{{
* scala> import cats.implicits._
* scala> import cats.syntax.all._
* scala> def parseInt(s: String): Either[String, Int] = Either.catchOnly[NumberFormatException](s.toInt).leftMap(_.getMessage)
* scala> val keys1 = List("1", "2", "4", "5")
* scala> val map1 = Map(4 -> "Four", 5 -> "Five")
Expand Down Expand Up @@ -170,7 +156,7 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {
*
* For example:
* {{{
* scala> import cats.implicits._
* scala> import cats.syntax.all._
* scala> val list = List(1,2,3,4)
* scala> list.findM(n => (n >= 2).asRight[String])
* res0: Either[String,Option[Int]] = Right(Some(2))
Expand All @@ -185,31 +171,34 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {
* res3: Either[String,Option[Int]] = Left(error)
* }}}
*/
def findM[G[_]](p: A => G[Boolean])(implicit F: Foldable[F], G: Monad[G]): G[Option[A]] = F.findM[G, A](fa)(p)
def findM[G[_]](p: A => G[Boolean])(implicit F: Foldable[F], G: Monad[G]): G[Option[A]] =
F.findM[G, A](fa)(p)

/**
* Tear down a subset of this structure using a `PartialFunction`.
* {{{
* scala> import cats.implicits._
* scala> import cats.syntax.all._
* scala> val xs = List(1, 2, 3, 4)
* scala> xs.collectFold { case n if n % 2 == 0 => n }
* res0: Int = 6
* }}}
*/
def collectFold[M](f: PartialFunction[A, M])(implicit F: Foldable[F], M: Monoid[M]): M = F.collectFold[A, M](fa)(f)
def collectFold[M](f: PartialFunction[A, M])(implicit F: Foldable[F], M: Monoid[M]): M =
F.collectFold[A, M](fa)(f)

/**
* Tear down a subset of this structure using a `A => Option[M]`.
* {{{
* scala> import cats.implicits._
* scala> import cats.syntax.all._
* scala> val xs = List(1, 2, 3, 4)
* scala> def f(n: Int): Option[Int] = if (n % 2 == 0) Some(n) else None
* scala> xs.collectFoldSome(f)
* res0: Int = 6
* }}}
*/
@deprecated("Use collectFoldSome", "2.1.0-RC1")
def collectSomeFold[M](f: A => Option[M])(implicit F: Foldable[F], M: Monoid[M]): M = F.collectFoldSome[A, M](fa)(f)
def collectSomeFold[M](f: A => Option[M])(implicit F: Foldable[F], M: Monoid[M]): M =
F.collectFoldSome[A, M](fa)(f)
}

final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
Expand All @@ -221,7 +210,7 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
*
* Example:
* {{{
* scala> import cats.implicits._
* scala> import cats.syntax.all._
*
* scala> val l: List[Int] = List(1, 2, 3)
* scala> l.mkString_(",")
Expand All @@ -239,21 +228,22 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
* combining them using the `MonoidK[G]` instance.
*
* {{{
* scala> import cats._, cats.implicits._
* scala> import cats._, cats.syntax.all._
* scala> val f: Int => Endo[String] = i => (s => s + i)
* scala> val x: Endo[String] = List(1, 2, 3).foldMapK(f)
* scala> val a = x("foo")
* a: String = "foo321"
* }}}
*/
def foldMapK[G[_], B](f: A => G[B])(implicit F: Foldable[F], G: MonoidK[G]): G[B] = F.foldMapK(fa)(f)
def foldMapK[G[_], B](f: A => G[B])(implicit F: Foldable[F], G: MonoidK[G]): G[B] =
F.foldMapK(fa)(f)

/**
* Separate this Foldable into a Tuple by an effectful separating function `A => H[B, C]` for some `Bifoldable[H]`
* Equivalent to `Functor#map` over `Alternative#separate`
*
* {{{
* scala> import cats.implicits._, cats.data.Const
* scala> import cats.syntax.all._, cats.data.Const
* scala> val list = List(1,2,3,4)
* scala> list.partitionBifold(a => (a, "value " + a.toString))
* res0: (List[Int], List[String]) = (List(1, 2, 3, 4),List(value 1, value 2, value 3, value 4))
Expand All @@ -272,7 +262,7 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
* Equivalent to `Traverse#traverse` over `Alternative#separate`
*
* {{{
* scala> import cats.implicits._, cats.data.Const
* scala> import cats.syntax.all._, cats.data.Const
* scala> val list = List(1,2,3,4)
* `Const`'s second parameter is never instantiated, so we can use an impossible type:
* scala> list.partitionBifoldM(a => Option(Const[Int, Nothing with Any](a)))
Expand All @@ -289,7 +279,7 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
* Equivalent to `Traverse#traverse` over `Alternative#separate`
*
* {{{
* scala> import cats.implicits._, cats.Eval
* scala> import cats.syntax.all._, cats.Eval
* scala> val list = List(1,2,3,4)
* scala> val partitioned1 = list.partitionEitherM(a => if (a % 2 == 0) Eval.now(Either.left[String, Int](a.toString)) else Eval.now(Either.right[String, Int](a)))
* Since `Eval.now` yields a lazy computation, we need to force it to inspect the result:
Expand All @@ -305,23 +295,40 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
)(implicit A: Alternative[F], F: Foldable[F], M: Monad[G]): G[(F[B], F[C])] =
F.partitionEitherM[G, A, B, C](fa)(f)(A, M)

def sliding2(implicit F: Foldable[F]): List[(A, A)] = F.sliding2(fa)
def sliding3(implicit F: Foldable[F]): List[(A, A, A)] = F.sliding3(fa)
def sliding4(implicit F: Foldable[F]): List[(A, A, A, A)] = F.sliding4(fa)
def sliding5(implicit F: Foldable[F]): List[(A, A, A, A, A)] = F.sliding5(fa)
def sliding6(implicit F: Foldable[F]): List[(A, A, A, A, A, A)] = F.sliding6(fa)
def sliding7(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A)] = F.sliding7(fa)
def sliding8(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A)] = F.sliding8(fa)
def sliding9(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A)] = F.sliding9(fa)
def sliding10(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A)] = F.sliding10(fa)
def sliding11(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A)] = F.sliding11(fa)
def sliding12(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding12(fa)
def sliding13(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding13(fa)
def sliding14(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding14(fa)
def sliding15(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding15(fa)
def sliding16(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding16(fa)
def sliding17(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding17(fa)
def sliding18(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding18(fa)
def sliding2(implicit F: Foldable[F]): List[(A, A)] =
F.sliding2(fa)
def sliding3(implicit F: Foldable[F]): List[(A, A, A)] =
F.sliding3(fa)
def sliding4(implicit F: Foldable[F]): List[(A, A, A, A)] =
F.sliding4(fa)
def sliding5(implicit F: Foldable[F]): List[(A, A, A, A, A)] =
F.sliding5(fa)
def sliding6(implicit F: Foldable[F]): List[(A, A, A, A, A, A)] =
F.sliding6(fa)
def sliding7(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A)] =
F.sliding7(fa)
def sliding8(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A)] =
F.sliding8(fa)
def sliding9(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A)] =
F.sliding9(fa)
def sliding10(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A)] =
F.sliding10(fa)
def sliding11(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A)] =
F.sliding11(fa)
def sliding12(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A)] =
F.sliding12(fa)
def sliding13(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A)] =
F.sliding13(fa)
def sliding14(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A)] =
F.sliding14(fa)
def sliding15(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] =
F.sliding15(fa)
def sliding16(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] =
F.sliding16(fa)
def sliding17(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] =
F.sliding17(fa)
def sliding18(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] =
F.sliding18(fa)
def sliding19(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] =
F.sliding19(fa)
def sliding20(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] =
Expand Down
20 changes: 19 additions & 1 deletion core/src/main/scala/cats/syntax/unorderedFoldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,30 @@ trait UnorderedFoldableSyntax extends UnorderedFoldable.ToUnorderedFoldableOps {

final class UnorderedFoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {

/**
* Tests if this `F[A]` contains `v` using the `Eq` instance for `A`,
* named contains_ to avoid conflict with existing contains which uses universal equality.
*
* Example:
* {{{
* scala> import cats.syntax.all._
*
* scala> val l: List[Int] = List(1, 2, 3, 4)
* scala> l.contains_(1)
* res0: Boolean = true
* scala> l.contains_(5)
* res1: Boolean = false
* }}}
*/
def contains_(v: A)(implicit ev: Eq[A], F: UnorderedFoldable[F]): Boolean =
F.contains_(fa, v)

/**
* Count the number of elements in the structure that satisfy the given predicate.
*
* For example:
* {{{
* scala> import cats.implicits._
* scala> import cats.syntax.all._
* scala> val map1 = Map[Int, String]()
* scala> val p1: String => Boolean = _.length > 0
* scala> map1.count(p1)
Expand Down
8 changes: 8 additions & 0 deletions laws/src/main/scala/cats/laws/UnorderedFoldableLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ trait UnorderedFoldableLaws[F[_]] {
def nonEmptyRef[A](fa: F[A]): IsEq[Boolean] =
F.nonEmpty(fa) <-> !F.isEmpty(fa)

def containsConsistentWithExists[A](fa: F[A], v: A)(implicit eq: Eq[A]): IsEq[Boolean] =
F.contains_(fa, v) <-> F.exists(fa)(a => eq.eqv(a, v))

def containsConsistentWithForall[A](fa: F[A], v: A)(implicit eq: Eq[A]): IsEq[Boolean] =
!F.contains_(fa, v) <-> F.forall(fa)(a => eq.neqv(a, v))

def containsAllElementsFromItself[A](fa: F[A])(implicit eq: Eq[A]): Boolean =
F.forall(fa)(a => F.contains_(fa, a))
}

object UnorderedFoldableLaws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ trait UnorderedFoldableTests[F[_]] extends Laws {
"forall true if empty" -> forAll(laws.forallEmpty[A] _),
"nonEmpty reference" -> forAll(laws.nonEmptyRef[A] _),
"exists is lazy" -> forAll(laws.existsLazy[A] _),
"forall is lazy" -> forAll(laws.forallLazy[A] _)
"forall is lazy" -> forAll(laws.forallLazy[A] _),
"contains consistent with exists" -> forAll(laws.containsConsistentWithExists[A] _),
"contains consistent with forall" -> forAll(laws.containsConsistentWithForall[A] _),
"contains all elements from itself" -> forAll(laws.containsAllElementsFromItself[A] _)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ sealed abstract class UnorderedFoldableSuite[F[_]](name: String)(implicit
assert(fa.count(Function.const(true)) === (fa.size))
}
}

test(s"UnorderedFoldable[$name].contains") {
forAll { (fa: F[String], v: String) =>
implicit val F: UnorderedFoldable[F] = instance
assert(fa.contains_(v) === (iterator(fa).contains(v)))
}
}

checkAll("F[Int]", UnorderedFoldableTests[F](instance).unorderedFoldable[Int, Int])
}

Expand Down

0 comments on commit fe40bc2

Please sign in to comment.