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
8 changes: 4 additions & 4 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ import Foldable.sentinel
*
* @see [[maximumOption]] for maximum instead of minimum.
*/
def minimumOption[A](fa: F[A])(implicit A: Order[A]): Option[A] =
override def minimumOption[A](fa: F[A])(implicit A: Order[A]): Option[A] =
reduceLeftOption(fa)(A.min)

/**
Expand All @@ -197,7 +197,7 @@ import Foldable.sentinel
*
* @see [[minimumOption]] for minimum instead of maximum.
*/
def maximumOption[A](fa: F[A])(implicit A: Order[A]): Option[A] =
override def maximumOption[A](fa: F[A])(implicit A: Order[A]): Option[A] =
reduceLeftOption(fa)(A.max)

/**
Expand All @@ -211,7 +211,7 @@ import Foldable.sentinel
*
* @see [[maximumByOption]] for maximum instead of minimum.
*/
def minimumByOption[A, B: Order](fa: F[A])(f: A => B): Option[A] =
override def minimumByOption[A, B: Order](fa: F[A])(f: A => B): Option[A] =
minimumOption(fa)(Order.by(f))

/**
Expand All @@ -225,7 +225,7 @@ import Foldable.sentinel
*
* @see [[minimumByOption]] for minimum instead of maximum.
*/
def maximumByOption[A, B: Order](fa: F[A])(f: A => B): Option[A] =
override def maximumByOption[A, B: Order](fa: F[A])(f: A => B): Option[A] =
maximumOption(fa)(Order.by(f))

/**
Expand Down
57 changes: 56 additions & 1 deletion core/src/main/scala/cats/UnorderedFoldable.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats

import cats.instances.long._
import cats.kernel.CommutativeMonoid
import cats.kernel.{CommutativeMonoid, CommutativeSemigroup}
import simulacrum.{noop, typeclass}

/**
Expand All @@ -14,6 +14,9 @@ import simulacrum.{noop, typeclass}
def unorderedFold[A: CommutativeMonoid](fa: F[A]): A =
unorderedFoldMap(fa)(identity)

def unorderedReduceOption[A](fa: F[A])(implicit A: CommutativeSemigroup[A]): Option[A] =
unorderedFoldMap(fa)(Some(_): Option[A])(cats.instances.option.catsKernelStdCommutativeMonoidForOption(A))

/**
* Returns true if there are no elements. Otherwise false.
*/
Expand Down Expand Up @@ -69,6 +72,50 @@ import simulacrum.{noop, typeclass}
@noop
def count[A](fa: F[A])(p: A => Boolean): Long =
unorderedFoldMap(fa)(a => if (p(a)) 1L else 0L)

/**
* Find the minimum `A` item in this structure according to the `Order[A]`.
*
* @return `None` if the structure is empty, otherwise the minimum element
* wrapped in a `Some`.
*
* @see [[maximumOption]] for maximum instead of minimum.
*/
def minimumOption[A: Order](fa: F[A]): Option[A] =
unorderedReduceOption(fa)(UnorderedFoldable.minCommutativeSemigroup)

/**
* Find the maximum `A` item in this structure according to the `Order[A]`.
*
* @return `None` if the structure is empty, otherwise the maximum element
* wrapped in a `Some`.
*
* @see [[minimumOption]] for minimum instead of maximum.
*/
def maximumOption[A: Order](fa: F[A]): Option[A] =
unorderedReduceOption(fa)(UnorderedFoldable.maxCommutativeSemigroup)

/**
* Find the minimum `A` item in this structure according to an `Order.by(f)`.
*
* @return `None` if the structure is empty, otherwise the minimum element
* wrapped in a `Some`.
*
* @see [[maximumByOption]] for maximum instead of minimum.
*/
def minimumByOption[A, B: Order](fa: F[A])(f: A => B): Option[A] =
minimumOption(fa)(Order.by(f))

/**
* Find the maximum `A` item in this structure according to an `Order.by(f)`.
*
* @return `None` if the structure is empty, otherwise the maximum element
* wrapped in a `Some`.
*
* @see [[minimumByOption]] for minimum instead of maximum.
*/
def maximumByOption[A, B: Order](fa: F[A])(f: A => B): Option[A] =
maximumOption(fa)(Order.by(f))
}

object UnorderedFoldable {
Expand All @@ -91,4 +138,12 @@ object UnorderedFoldable {
case false => Eval.False
}
}

private def minCommutativeSemigroup[A: Order]: CommutativeSemigroup[A] = new CommutativeSemigroup[A] {
override def combine(x: A, y: A): A = Order.min(x, y)
}

private def maxCommutativeSemigroup[A: Order]: CommutativeSemigroup[A] = new CommutativeSemigroup[A] {
override def combine(x: A, y: A): A = Order.max(x, y)
}
}
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 @@ -9,6 +9,9 @@ trait UnorderedFoldableLaws[F[_]] {
def unorderedFoldConsistentWithUnorderedFoldMap[A: CommutativeMonoid](fa: F[A]): IsEq[A] =
F.unorderedFoldMap(fa)(identity) <-> F.unorderedFold(fa)

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 @@ -18,11 +18,15 @@ trait UnorderedFoldableTests[F[_]] extends Laws {
A: CommutativeMonoid[A],
B: CommutativeMonoid[B],
EqFA: Eq[A],
EqFB: Eq[B]): RuleSet =
EqFB: Eq[B],
EqOptionA: Eq[Option[A]]): RuleSet =
new DefaultRuleSet(
name = "unorderedFoldable",
parent = None,
"unorderedFold consistent with unorderedFoldMap" -> forAll(laws.unorderedFoldConsistentWithUnorderedFoldMap[A] _),
"unorderedReduceOption consistent with unorderedFold" -> forAll(
laws.unorderedReduceOptionConsistentWithUnorderedFold[A] _
),
"forall consistent with exists" -> forAll(laws.forallConsistentWithExists[A] _),
"forall true if empty" -> forAll(laws.forallEmpty[A] _),
"nonEmpty reference" -> forAll(laws.nonEmptyRef[A] _),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ trait UnorderedTraverseTests[F[_]] extends UnorderedFoldableTests[F] {
EqB: Eq[B],
EqXYFC: Eq[X[Y[F[C]]]],
EqXFB: Eq[X[F[B]]],
EqYFB: Eq[Y[F[B]]]
EqYFB: Eq[Y[F[B]]],
EqOptionA: Eq[Option[A]]
): RuleSet = {
implicit def EqXFBYFB: Eq[(X[F[B]], Y[F[B]])] = new Eq[(X[F[B]], Y[F[B]])] {
override def eqv(x: (X[F[B]], Y[F[B]]), y: (X[F[B]], Y[F[B]])): Boolean =
Expand Down