Skip to content
Open
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
12 changes: 11 additions & 1 deletion core/src/main/scala/cats/UnorderedFoldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

package cats

import cats.kernel.CommutativeMonoid
import cats.kernel.{CommutativeMonoid, CommutativeSemigroup}
import scala.collection.immutable.{Queue, Seq, SortedMap, SortedSet}
import scala.util.Try

Expand All @@ -35,6 +35,16 @@ trait UnorderedFoldable[F[_]] extends Serializable {
def unorderedFold[A: CommutativeMonoid](fa: F[A]): A =
unorderedFoldMap(fa)(identity)

/**
* Reduce this unordered structure by combining its elements with a [[CommutativeSemigroup]] instance.
*
* If there are no elements, the result is `None`.
*/
def unorderedReduceOption[A: CommutativeSemigroup](fa: F[A]): Option[A] = {
val reducer = CommutativeMonoid[Option[A]]
unorderedFoldMap(fa)(a => Some(a): Option[A])(using reducer)
}

/**
* Fold in a [[CommutativeApplicative]] context by mapping the `A` values to `G[B]`. combining
* the `B` values using the given `CommutativeMonoid[B]` instance.
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala/cats/syntax/unorderedFoldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
package cats
package syntax

import cats.kernel.CommutativeSemigroup

trait UnorderedFoldableSyntax extends UnorderedFoldable.ToUnorderedFoldableOps {
implicit final def catsSyntaxUnorderedFoldableOps[F[_]: UnorderedFoldable, A](fa: F[A]): UnorderedFoldableOps[F, A] =
new UnorderedFoldableOps[F, A](fa)
Expand Down Expand Up @@ -66,4 +68,7 @@ final class UnorderedFoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {
*/
def count(p: A => Boolean)(implicit F: UnorderedFoldable[F]): Long =
F.count(fa)(p)

def unorderedReduceOption(implicit A: CommutativeSemigroup[A], F: UnorderedFoldable[F]): Option[A] =
F.unorderedReduceOption(fa)
}
3 changes: 3 additions & 0 deletions laws/src/main/scala/cats/laws/UnorderedFoldableLaws.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ trait UnorderedFoldableLaws[F[_]] {
def unorderedFoldMapAIdentity[A, B: CommutativeMonoid](fa: F[A], f: A => B): IsEq[B] =
F.unorderedFoldMapA[Id, A, B](fa)(f) <-> F.unorderedFoldMap(fa)(f)

def unorderedReduceOptionConsistentWithUnorderedFold[A: CommutativeMonoid](fa: F[A]): IsEq[Option[A]] =
F.unorderedReduceOption(fa) <-> (if (F.isEmpty(fa)) None else Some(F.unorderedFold(fa)))

def forallConsistentWithExists[A](fa: F[A], p: A => Boolean): Boolean =
if (F.forall(fa)(p)) {
val negationExists = F.exists(fa)(a => !p(a))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import Prop.*
import org.typelevel.discipline.Laws
import cats.kernel.CommutativeMonoid
import cats.instances.boolean.*
import cats.instances.option.*

trait UnorderedFoldableTests[F[_]] extends Laws {
def laws: UnorderedFoldableLaws[F]
Expand All @@ -53,7 +54,10 @@ trait UnorderedFoldableTests[F[_]] extends Laws {
"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] _)
"contains all elements from itself" -> forAll(laws.containsAllElementsFromItself[A] _),
"unorderedReduceOption consistent with unorderedFold" -> forAll(
laws.unorderedReduceOptionConsistentWithUnorderedFold[A] _
)
)
}

Expand Down
Loading