Skip to content

Commit

Permalink
Choosing the travaerseViaChain implementation for Map.unorderedTraverse
Browse files Browse the repository at this point in the history
  • Loading branch information
TonioGela committed Jun 20, 2023
1 parent e42746a commit 5c69f83
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@ import cats.data.Chain
@BenchmarkMode(Array(Mode.AverageTime))
@OutputTimeUnit(TimeUnit.MICROSECONDS)
class UnorderedTraverseMapBench {
// These benchmarks were written to choose the fastest implementation
// for Map.unorderedTraverse, some results are available here:
// https://github.com/typelevel/cats/pull/4463#issuecomment-1599612154

val instance = UnorderedTraverse[Map[Int, *]]

val xs1: Map[Int, Int] = (1 to 1_000).map(x => (x, x)).toMap
val xs2: Map[Int, Int] = (1 to 1_000_000).map(x => (x, x)).toMap
val xs1: Map[Int, Int] = (1 to 1000).map(x => (x, x)).toMap
val xs2: Map[Int, Int] = (1 to 1000000).map(x => (x, x)).toMap

def unorderedTraverseViaTree[G[_], A, B](
fa: Map[Int, A]
Expand Down
25 changes: 6 additions & 19 deletions core/src/main/scala/cats/instances/map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import cats.kernel.CommutativeMonoid
import scala.annotation.tailrec
import cats.arrow.Compose

import cats.data.Ior
import cats.data.{Chain, Ior}

trait MapInstances extends cats.kernel.instances.MapInstances {

Expand All @@ -42,24 +42,11 @@ trait MapInstances extends cats.kernel.instances.MapInstances {
def unorderedTraverse[G[_], A, B](
fa: Map[K, A]
)(f: A => G[B])(implicit G: CommutativeApplicative[G]): G[Map[K, B]] = {
// This stack-safe implementation was copied and adapted from List.traverse_
def runHalf(size: Int, fa: Map[K, A]): Eval[G[Map[K, B]]] =
if (size > 1) {
val leftSize = size / 2
val rightSize = size - leftSize
val (leftL, rightL) = fa.splitAt(leftSize)
runHalf(leftSize, leftL).flatMap { left =>
val right = runHalf(rightSize, rightL)
G.map2Eval(left, right) { (lm, rm) => lm ++ rm }
}
} else {
val (k, a) = fa.head
Eval.always(G.map(f(a))(b => Map(k -> b)))
}

val len = fa.size
if (len == 0) G.pure(Map.empty)
else runHalf(len, fa).value
if (fa.isEmpty) G.pure(Map.empty[K, B])
else
G.map(Chain.traverseViaChain(fa.toIndexedSeq) { case (k, a) =>
G.map(f(a))((k, _))
})(_.iterator.toMap)
}

override def map[A, B](fa: Map[K, A])(f: A => B): Map[K, B] =
Expand Down

0 comments on commit 5c69f83

Please sign in to comment.