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

Add combineAllOption to Foldable #2380

Merged
merged 11 commits into from
Nov 14, 2019
2 changes: 2 additions & 0 deletions alleycats-core/src/main/scala/alleycats/std/iterable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ trait IterableInstances {

override def foldMap[A, B](fa: Iterable[A])(f: A => B)(implicit B: Monoid[B]): B =
B.combineAll(fa.iterator.map(f))

override def iterator[A](fa: Iterable[A]): Iterator[A] = fa.iterator
}
}
2 changes: 2 additions & 0 deletions alleycats-core/src/main/scala/alleycats/std/map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ trait MapInstances {

override def toList[A](fa: Map[K, A]): List[A] = fa.values.toList

override def iterator[A](fa: Map[K, A]): Iterator[A] = fa.values.iterator

override def collectFirst[A, B](fa: Map[K, A])(pf: PartialFunction[A, B]): Option[B] =
fa.collectFirst(new PartialFunction[(K, A), B] {
override def isDefinedAt(x: (K, A)) = pf.isDefinedAt(x._2)
Expand Down
2 changes: 2 additions & 0 deletions alleycats-core/src/main/scala/alleycats/std/set.scala
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ trait SetInstances {

override def toList[A](fa: Set[A]): List[A] = fa.toList

override def iterator[A](fa: Set[A]): Iterator[A] = fa.iterator

override def reduceLeftOption[A](fa: Set[A])(f: (A, A) => A): Option[A] =
fa.reduceLeftOption(f)

Expand Down
12 changes: 12 additions & 0 deletions core/src/main/scala-2.12/cats/compat/Foldable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cats
package compat

private[cats] object Foldable {

def iterator[F[_], A](fa: F[A])(F: Foldable[F]): Iterator[A] =
F.foldRight[A, Stream[A]](fa, Eval.now(Stream.empty)) { (a, eb) =>
eb.map(Stream.cons(a, _))
}
.value
.iterator
}
3 changes: 2 additions & 1 deletion core/src/main/scala-2.12/cats/compat/Vector.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package cats.compat
package cats
package compat
Copy link
Contributor

Choose a reason for hiding this comment

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

This is nitpicky but I don't think we should change these, especially since there are no other changes in this file.


private[cats] object Vector {
def zipWith[A, B, C](fa: Vector[A], fb: Vector[B])(f: (A, B) => C): Vector[C] =
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala-2.12/cats/instances/stream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances {

override def toList[A](fa: Stream[A]): List[A] = fa.toList

override def iterator[A](fa: Stream[A]): Iterator[A] = fa.toIterator

override def reduceLeftOption[A](fa: Stream[A])(f: (A, A) => A): Option[A] =
fa.reduceLeftOption(f)

Expand Down
12 changes: 12 additions & 0 deletions core/src/main/scala-2.13+/cats/compat/Foldable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cats
package compat

private[cats] object Foldable {

def iterator[F[_], A](fa: F[A])(F: Foldable[F]): Iterator[A] =
F.foldRight[A, LazyList[A]](fa, Eval.now(LazyList.empty)) { (a, eb) =>
eb.map(LazyList.cons(a, _))
}
.value
.iterator
}
3 changes: 2 additions & 1 deletion core/src/main/scala-2.13+/cats/compat/Vector.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package cats.compat
package cats
package compat

private[cats] object Vector {
def zipWith[A, B, C](fa: Vector[A], fb: Vector[B])(f: (A, B) => C): Vector[C] =
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala-2.13+/cats/instances/stream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances {

override def toList[A](fa: Stream[A]): List[A] = fa.toList

override def iterator[A](fa: Stream[A]): Iterator[A] = fa.iterator

override def reduceLeftOption[A](fa: Stream[A])(f: (A, A) => A): Option[A] =
fa.reduceLeftOption(f)

Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ import Foldable.sentinel
*/
def combineAll[A: Monoid](fa: F[A]): A = fold(fa)

def combineAllOption[A](fa: F[A])(implicit ev: Semigroup[A]): Option[A] =
if (isEmpty(fa)) None else ev.combineAllOption(iterator(fa))

def iterator[A](fa: F[A]): Iterator[A] =
cats.compat.Foldable.iterator(fa)(self)

/**
* Fold implemented by mapping `A` values into `B` and then
* combining them using the given `Monoid[B]` instance.
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/data/Validated.scala
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,9 @@ sealed abstract private[data] class ValidatedInstances2 {
case _ => Nil
}

override def iterator[A](fa: Validated[E, A]): Iterator[A] =
toList(fa).iterator

override def isEmpty[A](fa: Validated[E, A]): Boolean = fa.isInvalid
}
// scalastyle:off method.length
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/instances/either.scala
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ trait EitherInstances extends cats.kernel.instances.EitherInstances {
override def toList[B](fab: Either[A, B]): List[B] =
fab.fold(_ => Nil, _ :: Nil)

override def iterator[B](fa: Either[A, B]): Iterator[B] =
toList(fa).iterator

override def isEmpty[B](fab: Either[A, B]): Boolean =
fab.isLeft

Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala/cats/instances/list.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ trait ListInstances extends cats.kernel.instances.ListInstances {

override def toList[A](fa: List[A]): List[A] = fa

override def iterator[A](fa: List[A]): Iterator[A] = fa.iterator

override def reduceLeftOption[A](fa: List[A])(f: (A, A) => A): Option[A] =
fa.reduceLeftOption(f)

Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala/cats/instances/option.scala
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ trait OptionInstances extends cats.kernel.instances.OptionInstances {

override def toList[A](fa: Option[A]): List[A] = fa.toList

override def iterator[A](fa: Option[A]): Iterator[A] = fa.iterator

override def filter_[A](fa: Option[A])(p: A => Boolean): List[A] =
fa.filter(p).toList

Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala/cats/instances/queue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances {

override def toList[A](fa: Queue[A]): List[A] = fa.toList

override def iterator[A](fa: Queue[A]): Iterator[A] = fa.iterator

override def reduceLeftOption[A](fa: Queue[A])(f: (A, A) => A): Option[A] =
fa.reduceLeftOption(f)

Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala/cats/instances/sortedMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ trait SortedMapInstances extends SortedMapInstances2 {

override def toList[A](fa: SortedMap[K, A]): List[A] = fa.values.toList

override def iterator[A](fa: SortedMap[K, A]): Iterator[A] = fa.values.iterator

override def collectFirst[A, B](fa: SortedMap[K, A])(pf: PartialFunction[A, B]): Option[B] =
fa.collectFirst(new PartialFunction[(K, A), B] {
override def isDefinedAt(x: (K, A)) = pf.isDefinedAt(x._2)
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala/cats/instances/sortedSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ trait SortedSetInstances extends SortedSetInstances1 {

override def toList[A](fa: SortedSet[A]): List[A] = fa.toList

override def iterator[A](fa: SortedSet[A]): Iterator[A] = fa.iterator

override def reduceLeftOption[A](fa: SortedSet[A])(f: (A, A) => A): Option[A] =
fa.reduceLeftOption(f)

Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala/cats/instances/try.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ trait TryInstances extends TryInstances1 {
case Success(a) => a :: Nil
}

override def iterator[A](fa: Try[A]): Iterator[A] = toList(fa).iterator

override def isEmpty[A](fa: Try[A]): Boolean = fa.isFailure
}
// scalastyle:on method.length
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala/cats/instances/vector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances {

override def toList[A](fa: Vector[A]): List[A] = fa.toList

override def iterator[A](fa: Vector[A]): Iterator[A] = fa.iterator

override def reduceLeftOption[A](fa: Vector[A])(f: (A, A) => A): Option[A] =
fa.reduceLeftOption(f)

Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala/cats/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ package object cats {
override def get[A](fa: Id[A])(idx: Long): Option[A] =
if (idx == 0L) Some(fa) else None
override def isEmpty[A](fa: Id[A]): Boolean = false
override def iterator[A](fa: Id[A]): Iterator[A] = Some(fa).iterator
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a big deal but you can just write Iterator(fa).

Copy link
Contributor

Choose a reason for hiding this comment

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

I think Iterator.single(a) is even better because it doesn’t use varargs.

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class NonEmptyStreamSuite extends CatsSuite {
}

class ReducibleNonEmptyStreamSuite extends ReducibleSuite[NonEmptyStream]("NonEmptyStream") {
def iterator[T](nes: NonEmptyStream[T]): Iterator[T] =
override def iterator[T](nes: NonEmptyStream[T]): Iterator[T] =
(nes.head #:: nes.tail).iterator

def range(start: Long, endInclusive: Long): NonEmptyStream[Long] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class NonEmptyLazyListSuite extends CatsSuite {
}

class ReducibleNonEmptyLazyListSuite extends ReducibleSuite[NonEmptyLazyList]("NonEmptyLazyList") {
def iterator[T](nel: NonEmptyLazyList[T]): Iterator[T] = nel.toLazyList.iterator
override def iterator[T](nel: NonEmptyLazyList[T]): Iterator[T] = nel.toLazyList.iterator

def range(start: Long, endInclusive: Long): NonEmptyLazyList[Long] =
NonEmptyLazyList(start, (start + 1L).to(endInclusive): _*)
Expand Down
81 changes: 31 additions & 50 deletions tests/src/test/scala/cats/tests/FoldableSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import cats.laws.discipline.arbitrary._
import kernel.compat.scalaVersionSpecific._

@suppressUnusedImportWarningForScalaVersionSpecific
abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit ArbFInt: Arbitrary[F[Int]],
ArbFString: Arbitrary[F[String]])
extends CatsSuite {
abstract class FoldableSuite[F[_]](name: String)(
implicit
F: Foldable[F],
ArbFInt: Arbitrary[F[Int]],
ArbFString: Arbitrary[F[String]]
) extends CatsSuite {

def iterator[T](fa: F[T]): Iterator[T]
def iterator[T](fa: F[T]): Iterator[T] = F.iterator(fa)

test(s"Foldable[$name].size/get") {
forAll { (fa: F[Int], n: Int) =>
Expand Down Expand Up @@ -214,6 +217,13 @@ abstract class FoldableSuite[F[_]: Foldable](name: String)(implicit ArbFInt: Arb
}
}

test(s"Foldable[$name].combineAllOption") {
forAll { (fa: F[Int]) =>
val list = fa.toList
fa.combineAllOption should ===(list.combineAllOption)
}
}

test(s"Foldable[$name].intercalate") {
forAll { (fa: F[String], a: String) =>
fa.intercalate(a) should ===(fa.toList.mkString(a))
Expand Down Expand Up @@ -466,73 +476,44 @@ class FoldableSuiteAdditional extends CatsSuite with ScalaVersionSpecificFoldabl
}
}

class FoldableListSuite extends FoldableSuite[List]("list") {
def iterator[T](list: List[T]): Iterator[T] = list.iterator
}

class FoldableVectorSuite extends FoldableSuite[Vector]("vector") {
def iterator[T](vector: Vector[T]): Iterator[T] = vector.iterator
}

class FoldableSortedSetSuite extends FoldableSuite[SortedSet]("sortedSet") {
def iterator[T](set: SortedSet[T]): Iterator[T] = set.iterator
}

class FoldableStreamSuite extends FoldableSuite[Stream]("lazyList") {
def iterator[T](list: Stream[T]): Iterator[T] = list.iterator
}

class FoldableSortedMapSuite extends FoldableSuite[SortedMap[Int, *]]("sortedMap") {
def iterator[T](map: SortedMap[Int, T]): Iterator[T] = map.valuesIterator
}

class FoldableOptionSuite extends FoldableSuite[Option]("option") {
def iterator[T](option: Option[T]): Iterator[T] = option.iterator
}

class FoldableEitherSuite extends FoldableSuite[Either[Int, *]]("either") {
def iterator[T](either: Either[Int, T]): Iterator[T] = either.toOption.iterator
}

class FoldableValidatedSuite extends FoldableSuite[Validated[String, *]]("validated") {
def iterator[T](validated: Validated[String, T]): Iterator[T] = validated.toOption.iterator
}

class FoldableTrySuite extends FoldableSuite[Try]("try") {
def iterator[T](tryt: Try[T]): Iterator[T] = tryt.toOption.iterator
}
class FoldableListSuite extends FoldableSuite[List]("list")
class FoldableVectorSuite extends FoldableSuite[Vector]("vector")
class FoldableSortedSetSuite extends FoldableSuite[SortedSet]("sortedSet")
class FoldableStreamSuite extends FoldableSuite[Stream]("lazyList")
class FoldableSortedMapSuite extends FoldableSuite[SortedMap[Int, *]]("sortedMap")
class FoldableOptionSuite extends FoldableSuite[Option]("option")
class FoldableEitherSuite extends FoldableSuite[Either[Int, *]]("either")
class FoldableValidatedSuite extends FoldableSuite[Validated[String, *]]("validated")
class FoldableTrySuite extends FoldableSuite[Try]("try")
class FoldableIdSuite extends FoldableSuite[Id[*]]("id")

class FoldableEitherKSuite extends FoldableSuite[EitherK[Option, Option, *]]("eitherK") {
def iterator[T](eitherK: EitherK[Option, Option, T]) = eitherK.run.bimap(_.iterator, _.iterator).merge
override def iterator[T](eitherK: EitherK[Option, Option, T]) = eitherK.run.bimap(_.iterator, _.iterator).merge
}

class FoldableIorSuite extends FoldableSuite[Int Ior *]("ior") {
def iterator[T](ior: Int Ior T) =
override def iterator[T](ior: Int Ior T) =
ior.fold(_ => None.iterator, b => Some(b).iterator, (_, b) => Some(b).iterator)
}

class FoldableIdSuite extends FoldableSuite[Id[*]]("id") {
def iterator[T](id: Id[T]) = Some(id).iterator
}

class FoldableIdTSuite extends FoldableSuite[IdT[Option, *]]("idT") {
def iterator[T](idT: IdT[Option, T]) = idT.value.iterator
override def iterator[T](idT: IdT[Option, T]) = idT.value.iterator
}

class FoldableConstSuite extends FoldableSuite[Const[Int, *]]("const") {
def iterator[T](const: Const[Int, T]) = None.iterator
override def iterator[T](const: Const[Int, T]) = None.iterator
}

class FoldableTuple2Suite extends FoldableSuite[(Int, *)]("tuple2") {
def iterator[T](tuple: (Int, T)) = Some(tuple._2).iterator
override def iterator[T](tuple: (Int, T)) = Some(tuple._2).iterator
}

class FoldableOneAndSuite extends FoldableSuite[OneAnd[List, *]]("oneAnd") {
def iterator[T](oneAnd: OneAnd[List, T]) = (oneAnd.head :: oneAnd.tail).iterator
override def iterator[T](oneAnd: OneAnd[List, T]) = (oneAnd.head :: oneAnd.tail).iterator
}

class FoldableComposedSuite extends FoldableSuite[Nested[List, Option, *]]("nested") {
def iterator[T](nested: Nested[List, Option, T]) =
override def iterator[T](nested: Nested[List, Option, T]) =
nested.value.collect {
case Some(t) => t
}.iterator
Expand Down
2 changes: 1 addition & 1 deletion tests/src/test/scala/cats/tests/NonEmptyChainSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class NonEmptyChainSuite extends CatsSuite {
}

class ReducibleNonEmptyChainSuite extends ReducibleSuite[NonEmptyChain]("NonEmptyChain") {
def iterator[T](nel: NonEmptyChain[T]): Iterator[T] = nel.toChain.iterator
override def iterator[T](nel: NonEmptyChain[T]): Iterator[T] = nel.toChain.iterator

def range(start: Long, endInclusive: Long): NonEmptyChain[Long] =
NonEmptyChain(start, (start + 1L).to(endInclusive): _*)
Expand Down
2 changes: 1 addition & 1 deletion tests/src/test/scala/cats/tests/NonEmptyListSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ class DeprecatedNonEmptyListSuite extends CatsSuite {
}

class ReducibleNonEmptyListSuite extends ReducibleSuite[NonEmptyList]("NonEmptyList") {
def iterator[T](nel: NonEmptyList[T]): Iterator[T] = nel.toList.iterator
override def iterator[T](nel: NonEmptyList[T]): Iterator[T] = nel.toList.iterator

def range(start: Long, endInclusive: Long): NonEmptyList[Long] = {
// if we inline this we get a bewildering implicit numeric widening
Expand Down
2 changes: 1 addition & 1 deletion tests/src/test/scala/cats/tests/NonEmptyVectorSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ class NonEmptyVectorSuite extends CatsSuite {
}

class ReducibleNonEmptyVectorSuite extends ReducibleSuite[NonEmptyVector]("NonEmptyVector") {
def iterator[T](nel: NonEmptyVector[T]): Iterator[T] = nel.toVector.iterator
override def iterator[T](nel: NonEmptyVector[T]): Iterator[T] = nel.toVector.iterator

def range(start: Long, endInclusive: Long): NonEmptyVector[Long] = {
// if we inline this we get a bewildering implicit numeric widening
Expand Down