Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Aliases traverseVoid/sequenceVoid and their counterparts #4682

Merged
merged 9 commits into from
Dec 25, 2024
Merged
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
15 changes: 9 additions & 6 deletions bench/src/main/scala/cats/bench/TraverseBench.scala
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,10 @@ class TraverseBench {
}
}

// TODO: consider renaming to `traverseVoidList`
@Benchmark
def traverse_List(bh: Blackhole) = {
val result = listT.traverse_(list) { i =>
def traverse_List(bh: Blackhole): Unit = {
val result = listT.traverseVoid(list) { i =>
Eval.later {
Blackhole.consumeCPU(Work)
i * 2
Expand Down Expand Up @@ -155,9 +156,10 @@ class TraverseBench {
bh.consume(result.value)
}

// TODO: consider renaming to `traverseVoidVector`
@Benchmark
def traverse_Vector(bh: Blackhole) = {
val result = vectorT.traverse_(vector) { i =>
def traverse_Vector(bh: Blackhole): Unit = {
val result = vectorT.traverseVoid(vector) { i =>
Eval.later {
Blackhole.consumeCPU(Work)
i * 2
Expand Down Expand Up @@ -242,9 +244,10 @@ class TraverseBench {
bh.consume(result.value)
}

// TODO: consider renaming to `traverseVoidChain`
@Benchmark
def traverse_Chain(bh: Blackhole) = {
val result = chainT.traverse_(chain) { i =>
def traverse_Chain(bh: Blackhole): Unit = {
val result = chainT.traverseVoid(chain) { i =>
Eval.later {
Blackhole.consumeCPU(Work)
i * 2
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala-2.13+/cats/instances/arraySeq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ private[cats] object ArraySeqInstances {

}

override def traverse_[G[_], A, B](fa: ArraySeq[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
override def traverseVoid[G[_], A, B](fa: ArraySeq[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
G match {
case x: StackSafeMonad[G] => Traverse.traverse_Directly(fa)(f)(x)
case x: StackSafeMonad[G] => Traverse.traverseVoidDirectly(fa)(f)(x)
case _ =>
foldRight(fa, Eval.now(G.unit)) { (a, acc) =>
G.map2Eval(f(a), acc) { (_, _) =>
Expand Down
41 changes: 32 additions & 9 deletions core/src/main/scala/cats/Foldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -578,42 +578,58 @@ trait Foldable[F[_]] extends UnorderedFoldable[F] with FoldableNFunctions[F] { s
* scala> import cats.syntax.all._
* scala> def parseInt(s: String): Option[Int] = Either.catchOnly[NumberFormatException](s.toInt).toOption
* scala> val F = Foldable[List]
* scala> F.traverse_(List("333", "444"))(parseInt)
* scala> F.traverseVoid(List("333", "444"))(parseInt)
* res0: Option[Unit] = Some(())
* scala> F.traverse_(List("333", "zzz"))(parseInt)
* scala> F.traverseVoid(List("333", "zzz"))(parseInt)
* res1: Option[Unit] = None
* }}}
*
* This method is primarily useful when `G[_]` represents an action
* or effect, and the specific `A` aspect of `G[A]` is not otherwise
* needed.
*/
def traverse_[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
def traverseVoid[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
foldRight(fa, Always(G.unit)) { (a, acc) =>
G.map2Eval(f(a), acc) { (_, _) =>
()
}
}.value

/**
* Alias for `traverseVoid`.
*
* @deprecated this method should be considered as deprecated and replaced by `traverseVoid`.
*/
def traverse_[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
traverseVoid(fa)(f)

/**
* Sequence `F[G[A]]` using `Applicative[G]`.
*
* This is similar to `traverse_` except it operates on `F[G[A]]`
* This is similar to `traverseVoid` except it operates on `F[G[A]]`
* values, so no additional functions are needed.
*
* For example:
*
* {{{
* scala> import cats.syntax.all._
* scala> val F = Foldable[List]
* scala> F.sequence_(List(Option(1), Option(2), Option(3)))
* scala> F.sequenceVoid(List(Option(1), Option(2), Option(3)))
* res0: Option[Unit] = Some(())
* scala> F.sequence_(List(Option(1), None, Option(3)))
* scala> F.sequenceVoid(List(Option(1), None, Option(3)))
* res1: Option[Unit] = None
* }}}
*/
def sequenceVoid[G[_]: Applicative, A](fga: F[G[A]]): G[Unit] =
traverseVoid(fga)(identity)

/**
* Alias for `sequenceVoid`.
*
* @deprecated this method should be considered as deprecated and replaced by `sequenceVoid`.
*/
def sequence_[G[_]: Applicative, A](fga: F[G[A]]): G[Unit] =
traverse_(fga)(identity)
sequenceVoid(fga)

/**
* Fold implemented using the given `MonoidK[G]` instance.
Expand Down Expand Up @@ -1053,10 +1069,17 @@ object Foldable {
typeClassInstance.foldMapM[G, A, B](self)(f)(G, B)
def foldMapA[G[_], B](f: A => G[B])(implicit G: Applicative[G], B: Monoid[B]): G[B] =
typeClassInstance.foldMapA[G, A, B](self)(f)(G, B)
def traverseVoid[G[_], B](f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
typeClassInstance.traverseVoid[G, A, B](self)(f)(G)
def traverse_[G[_], B](f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
typeClassInstance.traverse_[G, A, B](self)(f)(G)
traverseVoid[G, B](f)
// TODO: looks like these two methods below duplicate the same named methods from `NestedFoldableOps`.
// Moreover, the other two methods take precedence, thereby these two are not in use whatsoever.
// Perhaps it makes sense to deprecate one pair of them either here or there.
def sequenceVoid[G[_], B](implicit ev$1: A <:< G[B], ev$2: Applicative[G]): G[Unit] =
typeClassInstance.sequenceVoid[G, B](self.asInstanceOf[F[G[B]]])
def sequence_[G[_], B](implicit ev$1: A <:< G[B], ev$2: Applicative[G]): G[Unit] =
typeClassInstance.sequence_[G, B](self.asInstanceOf[F[G[B]]])
sequenceVoid[G, B]
def foldK[G[_], B](implicit ev$1: A <:< G[B], G: MonoidK[G]): G[B] =
typeClassInstance.foldK[G, B](self.asInstanceOf[F[G[B]]])(G)
def find(f: A => Boolean): Option[A] = typeClassInstance.find[A](self)(f)
Expand Down
62 changes: 48 additions & 14 deletions core/src/main/scala/cats/Parallel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -260,25 +260,39 @@ object Parallel extends ParallelArityFunctions2 {
}

/**
* Like `Foldable[A].sequence_`, but uses the applicative instance
* Like `Foldable[A].sequenceVoid`, but uses the applicative instance
* corresponding to the Parallel instance instead.
*/
def parSequence_[T[_]: Foldable, M[_], A](tma: T[M[A]])(implicit P: Parallel[M]): M[Unit] = {
val fu: P.F[Unit] = Foldable[T].traverse_(tma)(P.parallel.apply(_))(P.applicative)
def parSequenceVoid[T[_]: Foldable, M[_], A](tma: T[M[A]])(implicit P: Parallel[M]): M[Unit] = {
val fu: P.F[Unit] = Foldable[T].traverseVoid(tma)(P.parallel.apply(_))(P.applicative)
P.sequential(fu)
}

/**
* Like `Foldable[A].traverse_`, but uses the applicative instance
* Alias for `parSequenceVoid`.
*
* @deprecated this method should be considered as deprecated and replaced by `parSequenceVoid`.
*/
def parSequence_[T[_]: Foldable, M[_], A](tma: T[M[A]])(implicit P: Parallel[M]): M[Unit] =
parSequenceVoid(tma)

/**
* Like `Foldable[A].traverseVoid`, but uses the applicative instance
* corresponding to the Parallel instance instead.
*/
def parTraverse_[T[_]: Foldable, M[_], A, B](
ta: T[A]
)(f: A => M[B])(implicit P: Parallel[M]): M[Unit] = {
val gtb: P.F[Unit] = Foldable[T].traverse_(ta)(a => P.parallel(f(a)))(P.applicative)
def parTraverseVoid[T[_]: Foldable, M[_], A, B](ta: T[A])(f: A => M[B])(implicit P: Parallel[M]): M[Unit] = {
val gtb: P.F[Unit] = Foldable[T].traverseVoid(ta)(a => P.parallel(f(a)))(P.applicative)
P.sequential(gtb)
}

/**
* Alias for `parTraverseVoid`.
*
* @deprecated this method should be considered as deprecated and replaced by `parTraverseVoid`.
*/
def parTraverse_[T[_]: Foldable, M[_], A, B](ta: T[A])(f: A => M[B])(implicit P: Parallel[M]): M[Unit] =
parTraverseVoid(ta)(f)

def parUnorderedTraverse[T[_]: UnorderedTraverse, M[_], F[_]: CommutativeApplicative, A, B](
ta: T[A]
)(f: A => M[B])(implicit P: Parallel.Aux[M, F]): M[T[B]] =
Expand Down Expand Up @@ -345,27 +359,47 @@ object Parallel extends ParallelArityFunctions2 {
}

/**
* Like `Reducible[A].nonEmptySequence_`, but uses the apply instance
* Like `Reducible[A].nonEmptySequenceVoid`, but uses the apply instance
* corresponding to the Parallel instance instead.
*/
def parNonEmptySequence_[T[_]: Reducible, M[_], A](
def parNonEmptySequenceVoid[T[_]: Reducible, M[_], A](
tma: T[M[A]]
)(implicit P: NonEmptyParallel[M]): M[Unit] = {
val fu: P.F[Unit] = Reducible[T].nonEmptyTraverse_(tma)(P.parallel.apply(_))(P.apply)
val fu: P.F[Unit] = Reducible[T].nonEmptyTraverseVoid(tma)(P.parallel.apply(_))(P.apply)
P.sequential(fu)
}

/**
* Like `Reducible[A].nonEmptyTraverse_`, but uses the apply instance
* Alias for `parNonEmptySequenceVoid`.
*
* @deprecated this method should be considered as deprecated and replaced by `parNonEmptySequenceVoid`.
*/
def parNonEmptySequence_[T[_]: Reducible, M[_], A](
tma: T[M[A]]
)(implicit P: NonEmptyParallel[M]): M[Unit] =
parNonEmptySequenceVoid[T, M, A](tma)

/**
* Like `Reducible[A].nonEmptyTraverseVoid`, but uses the apply instance
* corresponding to the Parallel instance instead.
*/
def parNonEmptyTraverse_[T[_]: Reducible, M[_], A, B](
def parNonEmptyTraverseVoid[T[_]: Reducible, M[_], A, B](
ta: T[A]
)(f: A => M[B])(implicit P: NonEmptyParallel[M]): M[Unit] = {
val gtb: P.F[Unit] = Reducible[T].nonEmptyTraverse_(ta)(a => P.parallel(f(a)))(P.apply)
val gtb: P.F[Unit] = Reducible[T].nonEmptyTraverseVoid(ta)(a => P.parallel(f(a)))(P.apply)
P.sequential(gtb)
}

/**
* Alias for `parNonEmptyTraverseVoid`.
*
* @deprecated this method should be considered as deprecated and replaced by `parNonEmptyTraverseVoid`.
*/
def parNonEmptyTraverse_[T[_]: Reducible, M[_], A, B](
ta: T[A]
)(f: A => M[B])(implicit P: NonEmptyParallel[M]): M[Unit] =
parNonEmptyTraverseVoid[T, M, A, B](ta)(f)

/**
* Like `Bitraverse[A].bitraverse`, but uses the applicative instance
* corresponding to the Parallel instance instead.
Expand Down
37 changes: 28 additions & 9 deletions core/src/main/scala/cats/Reducible.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

package cats

import cats.Foldable.Source
import cats.data.{Ior, NonEmptyList}

/**
Expand Down Expand Up @@ -202,31 +201,47 @@ trait Reducible[F[_]] extends Foldable[F] { self =>
* `A` values will be mapped into `G[B]` and combined using
* `Apply#map2`.
*
* This method is similar to [[Foldable.traverse_]]. There are two
* This method is similar to [[Foldable.traverseVoid]]. There are two
* main differences:
*
* 1. We only need an [[Apply]] instance for `G` here, since we
* don't need to call [[Applicative.pure]] for a starting value.
* 2. This performs a strict left-associative traversal and thus
* must always traverse the entire data structure. Prefer
* [[Foldable.traverse_]] if you have an [[Applicative]] instance
* [[Foldable.traverseVoid]] if you have an [[Applicative]] instance
* available for `G` and want to take advantage of short-circuiting
* the traversal.
*/
def nonEmptyTraverse_[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Apply[G]): G[Unit] = {
def nonEmptyTraverseVoid[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Apply[G]): G[Unit] = {
val f1 = f.andThen(G.void)
reduceRightTo(fa)(f1)((x, y) => G.map2Eval(f1(x), y)((_, b) => b)).value
}

/**
* Alias for `nonEmptyTraverseVoid`.
*
* @deprecated this method should be considered as deprecated and replaced by `nonEmptyTraverseVoid`.
*/
def nonEmptyTraverse_[G[_], A, B](fa: F[A])(f: A => G[B])(implicit G: Apply[G]): G[Unit] =
nonEmptyTraverseVoid(fa)(f)

/**
* Sequence `F[G[A]]` using `Apply[G]`.
*
* This method is similar to [[Foldable.sequence_]] but requires only
* This method is similar to [[Foldable.sequenceVoid]] but requires only
* an [[Apply]] instance for `G` instead of [[Applicative]]. See the
* [[nonEmptyTraverse_]] documentation for a description of the differences.
* [[nonEmptyTraverseVoid]] documentation for a description of the differences.
*/
def nonEmptySequenceVoid[G[_], A](fga: F[G[A]])(implicit G: Apply[G]): G[Unit] =
nonEmptyTraverseVoid(fga)(identity)

/**
* Alias for `nonEmptySequenceVoid`.
*
* @deprecated this method should be considered as deprecated and replaced by `nonEmptySequenceVoid`.
*/
def nonEmptySequence_[G[_], A](fga: F[G[A]])(implicit G: Apply[G]): G[Unit] =
nonEmptyTraverse_(fga)(identity)
nonEmptySequenceVoid(fga)

def toNonEmptyList[A](fa: F[A]): NonEmptyList[A] =
reduceRightTo(fa)(a => NonEmptyList(a, Nil)) { (a, lnel) =>
Expand Down Expand Up @@ -399,10 +414,14 @@ object Reducible {
typeClassInstance.reduceMapM[G, A, B](self)(f)(G, B)
def reduceRightTo[B](f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] =
typeClassInstance.reduceRightTo[A, B](self)(f)(g)
def nonEmptyTraverseVoid[G[_], B](f: A => G[B])(implicit G: Apply[G]): G[Unit] =
typeClassInstance.nonEmptyTraverseVoid[G, A, B](self)(f)
def nonEmptyTraverse_[G[_], B](f: A => G[B])(implicit G: Apply[G]): G[Unit] =
typeClassInstance.nonEmptyTraverse_[G, A, B](self)(f)(G)
nonEmptyTraverseVoid[G, B](f)
def nonEmptySequenceVoid[G[_], B](implicit ev$1: A <:< G[B], G: Apply[G]): G[Unit] =
typeClassInstance.nonEmptySequenceVoid[G, B](self.asInstanceOf[F[G[B]]])
def nonEmptySequence_[G[_], B](implicit ev$1: A <:< G[B], G: Apply[G]): G[Unit] =
typeClassInstance.nonEmptySequence_[G, B](self.asInstanceOf[F[G[B]]])(G)
nonEmptySequenceVoid[G, B]
def toNonEmptyList: NonEmptyList[A] = typeClassInstance.toNonEmptyList[A](self)
def minimum(implicit A: Order[A]): A = typeClassInstance.minimum[A](self)(A)
def maximum(implicit A: Order[A]): A = typeClassInstance.maximum[A](self)(A)
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/Traverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ object Traverse {
}
}

private[cats] def traverse_Directly[G[_], A, B](
private[cats] def traverseVoidDirectly[G[_], A, B](
fa: IterableOnce[A]
)(f: A => G[B])(implicit G: StackSafeMonad[G]): G[Unit] = {
val iter = fa.iterator
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/cats/data/Chain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1254,9 +1254,9 @@ sealed abstract private[data] class ChainInstances extends ChainInstances1 {
}(f)
}

override def traverse_[G[_], A, B](fa: Chain[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
override def traverseVoid[G[_], A, B](fa: Chain[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
G match {
case x: StackSafeMonad[G] => Traverse.traverse_Directly(fa.iterator)(f)(x)
case x: StackSafeMonad[G] => Traverse.traverseVoidDirectly(fa.iterator)(f)(x)
case _ =>
foldRight(fa, Eval.now(G.unit)) { (a, acc) =>
G.map2Eval(f(a), acc) { (_, _) =>
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/cats/instances/list.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ trait ListInstances extends cats.kernel.instances.ListInstances {
/**
* This avoids making a very deep stack by building a tree instead
*/
override def traverse_[G[_], A, B](fa: List[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] = {
override def traverseVoid[G[_], A, B](fa: List[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] = {
G match {
case x: StackSafeMonad[G] => Traverse.traverse_Directly(fa)(f)(x)
case x: StackSafeMonad[G] => Traverse.traverseVoidDirectly(fa)(f)(x)
case _ =>
// the cost of this is O(size log size)
// c(n) = n + 2 * c(n/2) = n + 2(n/2 log (n/2)) = n + n (logn - 1) = n log n
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/cats/instances/queue.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances {
}
}

override def traverse_[G[_], A, B](fa: Queue[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
override def traverseVoid[G[_], A, B](fa: Queue[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
G match {
case x: StackSafeMonad[G] => Traverse.traverse_Directly(fa)(f)(x)
case x: StackSafeMonad[G] => Traverse.traverseVoidDirectly(fa)(f)(x)
case _ =>
foldRight(fa, Eval.now(G.unit)) { (a, acc) =>
G.map2Eval(f(a), acc) { (_, _) =>
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/cats/instances/seq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ trait SeqInstances extends cats.kernel.instances.SeqInstances {
G.map(Chain.traverseViaChain(fa.toIndexedSeq)(f))(_.toList)
}

override def traverse_[G[_], A, B](fa: Seq[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
override def traverseVoid[G[_], A, B](fa: Seq[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] =
G match {
case x: StackSafeMonad[G] => Traverse.traverse_Directly(fa)(f)(x)
case x: StackSafeMonad[G] => Traverse.traverseVoidDirectly(fa)(f)(x)
case _ =>
foldRight(fa, Eval.now(G.unit)) { (a, acc) =>
G.map2Eval(f(a), acc) { (_, _) =>
Expand Down
4 changes: 2 additions & 2 deletions core/src/main/scala/cats/instances/vector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances {
/**
* This avoids making a very deep stack by building a tree instead
*/
override def traverse_[G[_], A, B](fa: Vector[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] = {
override def traverseVoid[G[_], A, B](fa: Vector[A])(f: A => G[B])(implicit G: Applicative[G]): G[Unit] = {
G match {
case x: StackSafeMonad[G] => Traverse.traverse_Directly(fa)(f)(x)
case x: StackSafeMonad[G] => Traverse.traverseVoidDirectly(fa)(f)(x)
case _ =>
// the cost of this is O(size)
// c(n) = 1 + 2 * c(n/2)
Expand Down
Loading
Loading