Skip to content

Commit

Permalink
Add SortedMap instances
Browse files Browse the repository at this point in the history
  • Loading branch information
Luka Jacobowitz committed Oct 16, 2017
1 parent c121f4f commit 24fb152
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 0 deletions.
1 change: 1 addition & 0 deletions core/src/main/scala/cats/instances/all.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ trait AllInstances
with QueueInstances
with SemigroupInstances
with SetInstances
with SortedMapInstances
with StreamInstances
with StringInstances
with SymbolInstances
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala/cats/instances/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package object instances {
object list extends ListInstances
object long extends LongInstances
object map extends MapInstances
object sortedMap extends SortedMapInstances
object monoid extends MonoidInstances
object option extends OptionInstances
object order extends OrderInstances
Expand Down
173 changes: 173 additions & 0 deletions core/src/main/scala/cats/instances/sortedMap.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package cats.instances

import cats.{Always, Applicative, Eval, FlatMap, Foldable, Monoid, Show, Traverse}
import cats.kernel._
import cats.kernel.instances.StaticMethods

import scala.annotation.tailrec
import scala.collection.immutable.SortedMap
import scala.collection.mutable

trait SortedMapInstances extends SortedMapInstances1 {

implicit def catsKernelStdHashForSortedMap[K: Hash: Order, V: Hash]: Hash[SortedMap[K, V]] =
new SortedMapHash[K, V]

implicit def catsKernelStdMonoidForSortedMap[K: Order, V: Semigroup]: Monoid[SortedMap[K, V]] =
new SortedMapMonoid[K, V]

implicit def catsStdShowForSortedMap[A: Order, B](implicit showA: Show[A], showB: Show[B]): Show[SortedMap[A, B]] =
new Show[SortedMap[A, B]] {
def show(m: SortedMap[A, B]): String =
m.iterator
.map { case (a, b) => showA.show(a) + " -> " + showB.show(b) }
.mkString("SortedMap(", ", ", ")")
}


// scalastyle:off method.length
implicit def catsStdInstancesForSortedMap[K: Order]: Traverse[SortedMap[K, ?]] with FlatMap[SortedMap[K, ?]] =
new Traverse[SortedMap[K, ?]] with FlatMap[SortedMap[K, ?]] {

implicit val orderingK: Ordering[K] = Order[K].toOrdering

def traverse[G[_], A, B](fa: SortedMap[K, A])(f: A => G[B])(implicit G: Applicative[G]): G[SortedMap[K, B]] = {
val gba: Eval[G[SortedMap[K, B]]] = Always(G.pure(SortedMap.empty(Order[K].toOrdering)))
Foldable.iterateRight(fa.iterator, gba){ (kv, lbuf) =>
G.map2Eval(f(kv._2), lbuf)({ (b, buf) => buf + (kv._1 -> b)})
}.value
}

def flatMap[A, B](fa: SortedMap[K,A])(f: A => SortedMap[K,B]): SortedMap[K,B] =
fa.flatMap { case (k, a) => f(a).get(k).map((k, _)) }

override def map[A, B](fa: SortedMap[K, A])(f: A => B): SortedMap[K, B] =
fa.map { case (k, a) => (k, f(a)) }

override def map2[A, B, Z](fa: SortedMap[K, A], fb: SortedMap[K, B])(f: (A, B) => Z): SortedMap[K, Z] =
if (fb.isEmpty) SortedMap.empty(Order[K].toOrdering) // do O(1) work if fb is empty
else fa.flatMap { case (k, a) => fb.get(k).map(b => (k, f(a, b))) }

override def map2Eval[A, B, Z](fa: SortedMap[K, A], fb: Eval[SortedMap[K, B]])(f: (A, B) => Z): Eval[SortedMap[K, Z]] =
if (fa.isEmpty) Eval.now(SortedMap.empty(Order[K].toOrdering)) // no need to evaluate fb
else fb.map(fb => map2(fa, fb)(f))

override def ap[A, B](ff: SortedMap[K, A => B])(fa: SortedMap[K, A]): SortedMap[K, B] =
fa.flatMap { case (k, a) => ff.get(k).map(f => (k, f(a))) }(scala.collection.breakOut)

override def ap2[A, B, Z](f: SortedMap[K, (A, B) => Z])(fa: SortedMap[K, A], fb: SortedMap[K, B]): SortedMap[K, Z] =
f.flatMap { case (k, f) =>
for { a <- fa.get(k); b <- fb.get(k) } yield (k, f(a, b))
}

def foldLeft[A, B](fa: SortedMap[K, A], b: B)(f: (B, A) => B): B =
fa.foldLeft(b) { case (x, (k, a)) => f(x, a)}

def foldRight[A, B](fa: SortedMap[K, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
Foldable.iterateRight(fa.values.iterator, lb)(f)

def tailRecM[A, B](a: A)(f: A => SortedMap[K, Either[A, B]]): SortedMap[K, B] = {
val bldr = SortedMap.newBuilder[K, B](Order[K].toOrdering)

@tailrec def descend(k: K, either: Either[A, B]): Unit =
either match {
case Left(a) =>
f(a).get(k) match {
case Some(x) => descend(k, x)
case None => ()
}
case Right(b) =>
bldr += ((k, b))
()
}

f(a).foreach { case (k, a) => descend(k, a) }
bldr.result
}

override def size[A](fa: SortedMap[K, A]): Long = fa.size.toLong

override def get[A](fa: SortedMap[K, A])(idx: Long): Option[A] =
if (idx < 0L || Int.MaxValue < idx) None
else {
val n = idx.toInt
if (n >= fa.size) None
else Some(fa.valuesIterator.drop(n).next)
}

override def isEmpty[A](fa: SortedMap[K, A]): Boolean = fa.isEmpty

override def fold[A](fa: SortedMap[K, A])(implicit A: Monoid[A]): A =
A.combineAll(fa.values)

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

}

trait SortedMapInstances1 {
implicit def catsKernelStdEqForSortedMap[K: Order, V: Eq]: Eq[SortedMap[K, V]] =
new SortedMapEq[K, V]
}

class SortedMapHash[K, V](implicit V: Hash[V], O: Order[K]) extends SortedMapEq[K, V]()(V, O) with Hash[SortedMap[K, V]] {
// adapted from [[scala.util.hashing.MurmurHash3]],
// but modified standard `Any#hashCode` to `ev.hash`.
import scala.util.hashing.MurmurHash3._
def hash(x: SortedMap[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.product2Hash(k.hashCode(), V.hash(v))
a += h
b ^= h
if (h != 0) c *= h
n += 1
}
var h = mapSeed
h = mix(h, a)
h = mix(h, b)
h = mixLast(h, c)
finalizeHash(h, n)
}
}

class SortedMapEq[K, V](implicit V: Eq[V], O: Order[K]) extends Eq[SortedMap[K, V]] {
def eqv(x: SortedMap[K, V], y: SortedMap[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 SortedMapMonoid[K, V](implicit V: Semigroup[V], O: Order[K]) extends Monoid[SortedMap[K, V]] {

def empty: SortedMap[K, V] = SortedMap.empty(O.toOrdering)

def combine(xs: SortedMap[K, V], ys: SortedMap[K, V]): SortedMap[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: TraversableOnce[SortedMap[K, V]]): SortedMap[K, V] = {
val acc = mutable.SortedMap.empty[K, V](O.toOrdering)
xss.foreach { m =>
val it = m.iterator
while (it.hasNext) {
val (k, v) = it.next
acc(k) = Semigroup.maybeCombine(acc.get(k), v)
}
}
SortedMap.empty[K, V](O.toOrdering) ++ acc
}
}

0 comments on commit 24fb152

Please sign in to comment.