Skip to content

Commit fa6d79e

Browse files
committed
Move contains to UnorderedFoldable
1 parent a49ef0d commit fa6d79e

File tree

6 files changed

+101
-51
lines changed

6 files changed

+101
-51
lines changed

core/src/main/scala/cats/UnorderedFoldable.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ import simulacrum.{noop, typeclass}
3636
def unorderedFold[A: CommutativeMonoid](fa: F[A]): A =
3737
unorderedFoldMap(fa)(identity)
3838

39+
/**
40+
* Tests if `F[A]` contains an `A` using the `Eq` instance for `A`
41+
*/
42+
def contains_[A](fa: F[A], v: A)(implicit ev: Eq[A]): Boolean =
43+
exists(fa)(a => ev.eqv(a, v))
44+
3945
/**
4046
* Returns true if there are no elements. Otherwise false.
4147
*/

core/src/main/scala/cats/syntax/foldable.scala

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ final class NestedFoldableOps[F[_], G[_], A](private val fga: F[G[A]]) extends A
5050
*
5151
* Example:
5252
* {{{
53-
* scala> import cats.implicits._
53+
* scala> import cats.syntax.all._
5454
*
5555
* scala> val l: List[Set[Int]] = List(Set(1, 2), Set(2, 3), Set(3, 4))
5656
* scala> l.foldK
@@ -70,29 +70,15 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {
7070
def foldA[G[_], B](implicit F: Foldable[F], ev: A <:< G[B], G: Applicative[G], B: Monoid[B]): G[B] =
7171
F.foldA[G, B](fa.asInstanceOf[F[G[B]]])
7272

73-
/**
74-
* test if `F[A]` contains an `A`, named contains_ to avoid conflict with existing contains which uses universal equality
75-
*
76-
* Example:
77-
* {{{
78-
* scala> import cats.implicits._
79-
*
80-
* scala> val l: List[Int] = List(1, 2, 3, 4)
81-
* scala> l.contains_(1)
82-
* res0: Boolean = true
83-
* scala> l.contains_(5)
84-
* res1: Boolean = false
85-
* }}}
86-
*/
87-
def contains_(v: A)(implicit ev: Eq[A], F: Foldable[F]): Boolean =
88-
F.exists(fa)(ev.eqv(_, v))
73+
private[syntax] def contains_(v: A, eq: Eq[A], F: Foldable[F]): Boolean =
74+
F.contains_(fa, v)(eq)
8975

9076
/**
9177
* Intercalate with a prefix and a suffix
9278
*
9379
* Example:
9480
* {{{
95-
* scala> import cats.implicits._
81+
* scala> import cats.syntax.all._
9682
*
9783
* scala> val l: List[String] = List("1", "2", "3")
9884
* scala> l.foldSmash("List(", ",", ")")
@@ -109,7 +95,7 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {
10995
*
11096
* Example:
11197
* {{{
112-
* scala> import cats.implicits._
98+
* scala> import cats.syntax.all._
11399
*
114100
* scala> val l: List[Int] = List(1, 2, 3)
115101
* scala> l.mkString_("L[", ";", "]")
@@ -139,7 +125,7 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {
139125
*
140126
* For example:
141127
* {{{
142-
* scala> import cats.implicits._
128+
* scala> import cats.syntax.all._
143129
* scala> def parseInt(s: String): Either[String, Int] = Either.catchOnly[NumberFormatException](s.toInt).leftMap(_.getMessage)
144130
* scala> val keys1 = List("1", "2", "4", "5")
145131
* scala> val map1 = Map(4 -> "Four", 5 -> "Five")
@@ -170,7 +156,7 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {
170156
*
171157
* For example:
172158
* {{{
173-
* scala> import cats.implicits._
159+
* scala> import cats.syntax.all._
174160
* scala> val list = List(1,2,3,4)
175161
* scala> list.findM(n => (n >= 2).asRight[String])
176162
* res0: Either[String,Option[Int]] = Right(Some(2))
@@ -185,31 +171,34 @@ final class FoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {
185171
* res3: Either[String,Option[Int]] = Left(error)
186172
* }}}
187173
*/
188-
def findM[G[_]](p: A => G[Boolean])(implicit F: Foldable[F], G: Monad[G]): G[Option[A]] = F.findM[G, A](fa)(p)
174+
def findM[G[_]](p: A => G[Boolean])(implicit F: Foldable[F], G: Monad[G]): G[Option[A]] =
175+
F.findM[G, A](fa)(p)
189176

190177
/**
191178
* Tear down a subset of this structure using a `PartialFunction`.
192179
* {{{
193-
* scala> import cats.implicits._
180+
* scala> import cats.syntax.all._
194181
* scala> val xs = List(1, 2, 3, 4)
195182
* scala> xs.collectFold { case n if n % 2 == 0 => n }
196183
* res0: Int = 6
197184
* }}}
198185
*/
199-
def collectFold[M](f: PartialFunction[A, M])(implicit F: Foldable[F], M: Monoid[M]): M = F.collectFold[A, M](fa)(f)
186+
def collectFold[M](f: PartialFunction[A, M])(implicit F: Foldable[F], M: Monoid[M]): M =
187+
F.collectFold[A, M](fa)(f)
200188

201189
/**
202190
* Tear down a subset of this structure using a `A => Option[M]`.
203191
* {{{
204-
* scala> import cats.implicits._
192+
* scala> import cats.syntax.all._
205193
* scala> val xs = List(1, 2, 3, 4)
206194
* scala> def f(n: Int): Option[Int] = if (n % 2 == 0) Some(n) else None
207195
* scala> xs.collectFoldSome(f)
208196
* res0: Int = 6
209197
* }}}
210198
*/
211199
@deprecated("Use collectFoldSome", "2.1.0-RC1")
212-
def collectSomeFold[M](f: A => Option[M])(implicit F: Foldable[F], M: Monoid[M]): M = F.collectFoldSome[A, M](fa)(f)
200+
def collectSomeFold[M](f: A => Option[M])(implicit F: Foldable[F], M: Monoid[M]): M =
201+
F.collectFoldSome[A, M](fa)(f)
213202
}
214203

215204
final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
@@ -221,7 +210,7 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
221210
*
222211
* Example:
223212
* {{{
224-
* scala> import cats.implicits._
213+
* scala> import cats.syntax.all._
225214
*
226215
* scala> val l: List[Int] = List(1, 2, 3)
227216
* scala> l.mkString_(",")
@@ -239,21 +228,22 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
239228
* combining them using the `MonoidK[G]` instance.
240229
*
241230
* {{{
242-
* scala> import cats._, cats.implicits._
231+
* scala> import cats._, cats.syntax.all._
243232
* scala> val f: Int => Endo[String] = i => (s => s + i)
244233
* scala> val x: Endo[String] = List(1, 2, 3).foldMapK(f)
245234
* scala> val a = x("foo")
246235
* a: String = "foo321"
247236
* }}}
248237
*/
249-
def foldMapK[G[_], B](f: A => G[B])(implicit F: Foldable[F], G: MonoidK[G]): G[B] = F.foldMapK(fa)(f)
238+
def foldMapK[G[_], B](f: A => G[B])(implicit F: Foldable[F], G: MonoidK[G]): G[B] =
239+
F.foldMapK(fa)(f)
250240

251241
/**
252242
* Separate this Foldable into a Tuple by an effectful separating function `A => H[B, C]` for some `Bifoldable[H]`
253243
* Equivalent to `Functor#map` over `Alternative#separate`
254244
*
255245
* {{{
256-
* scala> import cats.implicits._, cats.data.Const
246+
* scala> import cats.syntax.all._, cats.data.Const
257247
* scala> val list = List(1,2,3,4)
258248
* scala> list.partitionBifold(a => (a, "value " + a.toString))
259249
* res0: (List[Int], List[String]) = (List(1, 2, 3, 4),List(value 1, value 2, value 3, value 4))
@@ -272,7 +262,7 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
272262
* Equivalent to `Traverse#traverse` over `Alternative#separate`
273263
*
274264
* {{{
275-
* scala> import cats.implicits._, cats.data.Const
265+
* scala> import cats.syntax.all._, cats.data.Const
276266
* scala> val list = List(1,2,3,4)
277267
* `Const`'s second parameter is never instantiated, so we can use an impossible type:
278268
* scala> list.partitionBifoldM(a => Option(Const[Int, Nothing with Any](a)))
@@ -289,7 +279,7 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
289279
* Equivalent to `Traverse#traverse` over `Alternative#separate`
290280
*
291281
* {{{
292-
* scala> import cats.implicits._, cats.Eval
282+
* scala> import cats.syntax.all._, cats.Eval
293283
* scala> val list = List(1,2,3,4)
294284
* 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)))
295285
* Since `Eval.now` yields a lazy computation, we need to force it to inspect the result:
@@ -305,23 +295,40 @@ final class FoldableOps0[F[_], A](private val fa: F[A]) extends AnyVal {
305295
)(implicit A: Alternative[F], F: Foldable[F], M: Monad[G]): G[(F[B], F[C])] =
306296
F.partitionEitherM[G, A, B, C](fa)(f)(A, M)
307297

308-
def sliding2(implicit F: Foldable[F]): List[(A, A)] = F.sliding2(fa)
309-
def sliding3(implicit F: Foldable[F]): List[(A, A, A)] = F.sliding3(fa)
310-
def sliding4(implicit F: Foldable[F]): List[(A, A, A, A)] = F.sliding4(fa)
311-
def sliding5(implicit F: Foldable[F]): List[(A, A, A, A, A)] = F.sliding5(fa)
312-
def sliding6(implicit F: Foldable[F]): List[(A, A, A, A, A, A)] = F.sliding6(fa)
313-
def sliding7(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A)] = F.sliding7(fa)
314-
def sliding8(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A)] = F.sliding8(fa)
315-
def sliding9(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A)] = F.sliding9(fa)
316-
def sliding10(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A)] = F.sliding10(fa)
317-
def sliding11(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A)] = F.sliding11(fa)
318-
def sliding12(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding12(fa)
319-
def sliding13(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding13(fa)
320-
def sliding14(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A)] = F.sliding14(fa)
321-
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)
322-
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)
323-
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)
324-
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)
298+
def sliding2(implicit F: Foldable[F]): List[(A, A)] =
299+
F.sliding2(fa)
300+
def sliding3(implicit F: Foldable[F]): List[(A, A, A)] =
301+
F.sliding3(fa)
302+
def sliding4(implicit F: Foldable[F]): List[(A, A, A, A)] =
303+
F.sliding4(fa)
304+
def sliding5(implicit F: Foldable[F]): List[(A, A, A, A, A)] =
305+
F.sliding5(fa)
306+
def sliding6(implicit F: Foldable[F]): List[(A, A, A, A, A, A)] =
307+
F.sliding6(fa)
308+
def sliding7(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A)] =
309+
F.sliding7(fa)
310+
def sliding8(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A)] =
311+
F.sliding8(fa)
312+
def sliding9(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A)] =
313+
F.sliding9(fa)
314+
def sliding10(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A)] =
315+
F.sliding10(fa)
316+
def sliding11(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A)] =
317+
F.sliding11(fa)
318+
def sliding12(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A)] =
319+
F.sliding12(fa)
320+
def sliding13(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A)] =
321+
F.sliding13(fa)
322+
def sliding14(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A)] =
323+
F.sliding14(fa)
324+
def sliding15(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] =
325+
F.sliding15(fa)
326+
def sliding16(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] =
327+
F.sliding16(fa)
328+
def sliding17(implicit F: Foldable[F]): List[(A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A)] =
329+
F.sliding17(fa)
330+
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)] =
331+
F.sliding18(fa)
325332
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)] =
326333
F.sliding19(fa)
327334
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)] =

core/src/main/scala/cats/syntax/unorderedFoldable.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,30 @@ trait UnorderedFoldableSyntax extends UnorderedFoldable.ToUnorderedFoldableOps {
2929

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

32+
/**
33+
* Tests if `F[A]` contains an `A` using the `Eq` instance for `A`,
34+
* named contains_ to avoid conflict with existing contains which uses universal equality
35+
*
36+
* Example:
37+
* {{{
38+
* scala> import cats.syntax.all._
39+
*
40+
* scala> val l: List[Int] = List(1, 2, 3, 4)
41+
* scala> l.contains_(1)
42+
* res0: Boolean = true
43+
* scala> l.contains_(5)
44+
* res1: Boolean = false
45+
* }}}
46+
*/
47+
def contains_(v: A)(implicit ev: Eq[A], F: UnorderedFoldable[F]): Boolean =
48+
F.contains_(fa, v)
49+
3250
/**
3351
* Count the number of elements in the structure that satisfy the given predicate.
3452
*
3553
* For example:
3654
* {{{
37-
* scala> import cats.implicits._
55+
* scala> import cats.syntax.all._
3856
* scala> val map1 = Map[Int, String]()
3957
* scala> val p1: String => Boolean = _.length > 0
4058
* scala> map1.count(p1)

laws/src/main/scala/cats/laws/UnorderedFoldableLaws.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ trait UnorderedFoldableLaws[F[_]] {
6969
def nonEmptyRef[A](fa: F[A]): IsEq[Boolean] =
7070
F.nonEmpty(fa) <-> !F.isEmpty(fa)
7171

72+
def containsConsistentWithExists[A](fa: F[A], v: A)(implicit eq: Eq[A]): IsEq[Boolean] =
73+
F.contains_(fa, v) <-> F.exists(fa)(a => eq.eqv(a, v))
74+
75+
def containsConsistentWithForall[A](fa: F[A], v: A)(implicit eq: Eq[A]): IsEq[Boolean] =
76+
!F.contains_(fa, v) <-> F.forall(fa)(a => eq.neqv(a, v))
77+
78+
def mustContainAllElementsFromItself[A](fa: F[A])(implicit eq: Eq[A]): Boolean =
79+
F.forall(fa)(a => F.contains_(fa, a))
7280
}
7381

7482
object UnorderedFoldableLaws {

laws/src/main/scala/cats/laws/discipline/UnorderedFoldableTests.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ trait UnorderedFoldableTests[F[_]] extends Laws {
4949
"forall true if empty" -> forAll(laws.forallEmpty[A] _),
5050
"nonEmpty reference" -> forAll(laws.nonEmptyRef[A] _),
5151
"exists is lazy" -> forAll(laws.existsLazy[A] _),
52-
"forall is lazy" -> forAll(laws.forallLazy[A] _)
52+
"forall is lazy" -> forAll(laws.forallLazy[A] _),
53+
"contains consitent with exists" -> forAll(laws.containsConsistentWithExists[A] _),
54+
"contains consistent with forall" -> forAll(laws.containsConsistentWithForall[A] _),
55+
"contain all elements from itself" -> forAll(laws.mustContainAllElementsFromItself[A] _)
5356
)
5457
}
5558

tests/shared/src/test/scala/cats/tests/UnorderedFoldableSuite.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ sealed abstract class UnorderedFoldableSuite[F[_]](name: String)(implicit
6868
assert(fa.count(Function.const(true)) === (fa.size))
6969
}
7070
}
71+
72+
test(s"UnorderedFoldable[$name].contains") {
73+
forAll { (fa: F[String], v: String) =>
74+
implicit val F: UnorderedFoldable[F] = instance
75+
assert(fa.contains_(v) === (iterator(fa).toSet.contains(v)))
76+
}
77+
}
78+
7179
checkAll("F[Int]", UnorderedFoldableTests[F](instance).unorderedFoldable[Int, Int])
7280
}
7381

0 commit comments

Comments
 (0)