All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cats.kernel.instances.MapInstances.scala Maven / Gradle / Ivy

The newest version!
package cats.kernel
package instances

import scala.collection.mutable
import compat.scalaVersionSpecific._

@suppressUnusedImportWarningForScalaVersionSpecific
trait MapInstances extends MapInstances1 {
  implicit def catsKernelStdHashForMap[K: Hash, V: Hash]: Hash[Map[K, V]] =
    new MapHash[K, V]

  implicit def catsKernelStdCommutativeMonoidForMap[K, V: CommutativeSemigroup]: CommutativeMonoid[Map[K, V]] =
    new MapMonoid[K, V] with CommutativeMonoid[Map[K, V]]
}

private[instances] trait MapInstances1 {
  implicit def catsKernelStdEqForMap[K, V: Eq]: Eq[Map[K, V]] =
    new MapEq[K, V]
  implicit def catsKernelStdMonoidForMap[K, V: Semigroup]: Monoid[Map[K, V]] =
    new MapMonoid[K, V]
}

class MapHash[K, V](implicit V: Hash[V]) extends MapEq[K, V]()(V) with Hash[Map[K, V]] {
  // adapted from [[scala.util.hashing.MurmurHash3]],
  // but modified standard `Any#hashCode` to `ev.hash`.
  import scala.util.hashing.MurmurHash3._
  def hash(x: Map[K, V]): Int = {
    var a, b, n = 0
    var c = 1
    x.foreach {
      case (k, v) =>
        // use the default hash on keys because that's what Scala's Map does
        val h = StaticMethods.product2HashWithPrefix(k.hashCode(), V.hash(v), "Tuple2")
        a += h
        b ^= h
        c = StaticMethods.updateUnorderedHashC(c, h)
        n += 1
    }
    var h = mapSeed
    h = mix(h, a)
    h = mix(h, b)
    h = mixLast(h, c)
    finalizeHash(h, n)
  }
}

class MapEq[K, V](implicit V: Eq[V]) extends Eq[Map[K, V]] {
  def eqv(x: Map[K, V], y: Map[K, V]): Boolean =
    if (x eq y) true
    else
      x.size == y.size && x.forall {
        case (k, v1) =>
          y.get(k) match {
            case Some(v2) => V.eqv(v1, v2)
            case None     => false
          }
      }
}

class MapMonoid[K, V](implicit V: Semigroup[V]) extends Monoid[Map[K, V]] {

  def empty: Map[K, V] = Map.empty

  def combine(xs: Map[K, V], ys: Map[K, V]): Map[K, V] =
    if (xs.size <= ys.size) {
      xs.foldLeft(ys) {
        case (my, (k, x)) =>
          my.updated(k, Semigroup.maybeCombine(x, my.get(k)))
      }
    } else {
      ys.foldLeft(xs) {
        case (mx, (k, y)) =>
          mx.updated(k, Semigroup.maybeCombine(mx.get(k), y))
      }
    }

  override def combineAll(xss: IterableOnce[Map[K, V]]): Map[K, V] = {
    val acc = mutable.Map.empty[K, V]
    xss.iterator.foreach { m =>
      val it = m.iterator
      while (it.hasNext) {
        val (k, v) = it.next
        acc(k) = Semigroup.maybeCombine(acc.get(k), v)
      }
    }
    StaticMethods.wrapMutableMap(acc)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy