Skip to content

Commit

Permalink
Improve Map.Extra
Browse files Browse the repository at this point in the history
  • Loading branch information
durban committed Dec 21, 2024
1 parent 30c533d commit ea4023d
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 17 deletions.
3 changes: 3 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,9 @@ lazy val data = crossProject(JVMPlatform, JSPlatform)
ProblemFilters.exclude[MissingClassProblem]("dev.tauri.choam.data.Ttrie$State"), // private
ProblemFilters.exclude[MissingClassProblem]("dev.tauri.choam.data.Ttrie$Value"), // private
ProblemFilters.exclude[MissingClassProblem]("dev.tauri.choam.data.Ttrie$Value$"), // private
ProblemFilters.exclude[ReversedMissingMethodProblem]("dev.tauri.choam.data.Map#Extra.keys"), // sealed
ProblemFilters.exclude[ReversedMissingMethodProblem]("dev.tauri.choam.data.Map#Extra.valuesUnsorted"), // sealed
ProblemFilters.exclude[ReversedMissingMethodProblem]("dev.tauri.choam.data.Map#Extra.items"), // sealed
),
)

Expand Down
14 changes: 13 additions & 1 deletion data/shared/src/main/scala/dev/tauri/choam/data/Map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package dev.tauri.choam
package data

import cats.kernel.{ Hash, Order }
import cats.data.Chain
import cats.effect.kernel.{ Ref => CatsRef }
import cats.effect.std.MapRef

Expand Down Expand Up @@ -51,8 +52,19 @@ sealed trait Map[K, V] { self =>
object Map extends MapPlatform {

sealed trait Extra[K, V] extends Map[K, V] {

// TODO: type Snapshot
// TODO: def snapshot: Axn[Snapshot]

def clear: Axn[Unit]
def values(implicit V: Order[V]): Axn[Vector[V]]

def values(implicit V: Order[V]): Axn[Vector[V]] // TODO:0.5: remove this

def keys: Axn[Chain[K]]

def valuesUnsorted: Axn[Chain[V]] // TODO:0.5: rename to `values`

def items: Axn[Chain[(K, V)]]
}

private[data] trait UnsealedMap[K, V] extends Map[K, V]
Expand Down
19 changes: 19 additions & 0 deletions data/shared/src/main/scala/dev/tauri/choam/data/SimpleMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package data
import scala.collection.immutable.{ Map => ScalaMap }

import cats.kernel.{ Hash, Order }
import cats.data.Chain
import cats.collections.HashMap

private final class SimpleMap[K, V] private (
Expand Down Expand Up @@ -98,6 +99,24 @@ private final class SimpleMap[K, V] private (
}
}

final override def keys: Axn[Chain[K]] = {
repr.get.map { hm =>
Chain.fromIterableOnce(hm.keysIterator)
}
}

final override def valuesUnsorted: Axn[Chain[V]] = {
repr.get.map { hm =>
Chain.fromIterableOnce(hm.valuesIterator)
}
}

final override def items: Axn[Chain[(K, V)]] = {
repr.get.map { hm =>
Chain.fromIterableOnce(hm.iterator)
}
}

override def refLike(key: K, default: V): RefLike[V] = new RefLike[V] {

final def get: Axn[V] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package data
import scala.collection.immutable.{ Map => ScalaMap }

import cats.kernel.Order
import cats.data.Chain
import cats.collections.AvlMap
import dev.tauri.choam.Rxn
import dev.tauri.choam.RefLike
Expand Down Expand Up @@ -92,11 +93,41 @@ private final class SimpleOrderedMap[K, V] private (

final override def values(implicit V: Order[V]): Axn[Vector[V]] = {
repr.get.map { am =>
val vb = Vector.newBuilder[V]
am.foldLeft(()) { (_, kv) =>
vb += kv._2
}
vb.result()
val b = scala.collection.mutable.ArrayBuffer.newBuilder[V]
b.sizeHint(am.set.size)
am.foldLeft(b) { (b, kv) =>
b += kv._2
}.result().sortInPlace()(V.toOrdering).toVector
}
}

final override def keys: Axn[Chain[K]] = {
repr.get.map { m =>
Chain.fromSeq(
m.foldLeft(Vector.newBuilder[K]) { (vb, kv) =>
vb.addOne(kv._1)
}.result()
)
}
}

final override def valuesUnsorted: Axn[Chain[V]] = {
repr.get.map { m =>
Chain.fromSeq(
m.foldLeft(Vector.newBuilder[V]) { (vb, kv) =>
vb.addOne(kv._2)
}.result()
)
}
}

final override def items: Axn[Chain[(K, V)]] = {
repr.get.map { m =>
Chain.fromSeq(
m.foldLeft(Vector.newBuilder[(K, V)]) { (vb, kv) =>
vb.addOne(kv)
}.result()
)
}
}

Expand Down
70 changes: 59 additions & 11 deletions data/shared/src/test/scala/dev/tauri/choam/data/mapSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package data
import scala.collection.immutable.{ Set => ScalaSet }

import cats.kernel.{ Hash, Order }
import cats.data.Chain
import cats.effect.SyncIO

import org.scalacheck.effect.PropF
Expand All @@ -36,16 +37,23 @@ final class MapSpec_SimpleOrdered_ThreadConfinedMcas_SyncIO
with SpecThreadConfinedMcas
with MapSpecSimpleOrdered[SyncIO]

trait MapSpecSimpleHash[F[_]] extends MapSpec[F] { this: McasImplSpec =>

override type MyMap[K, V] = Map.Extra[K, V]
trait MapSpecSimpleHash[F[_]] extends MapSpecSimple[F] { this: McasImplSpec =>

def mkEmptyMap[K : Hash : Order, V]: F[Map.Extra[K, V]] =
Map.simpleHashMap[K, V].run[F]
}

// TODO: these should run for simpleOrderedMap too
trait MapSpecSimpleOrdered[F[_]] extends MapSpecSimple[F] { this: McasImplSpec =>

test("Map.Extra should perform clear correctly") {
def mkEmptyMap[K : Hash : Order, V]: F[Map.Extra[K, V]] =
Map.simpleOrderedMap[K, V].run[F]
}

trait MapSpecSimple[F[_]] extends MapSpec[F] { this: McasImplSpec =>

override type MyMap[K, V] = Map.Extra[K, V]

test("Map.Extra should perform `clear` correctly") {
for {
m <- mkEmptyMap[Int, String]
_ <- (Rxn.pure(42 -> "foo") >>> m.put).run[F]
Expand All @@ -61,7 +69,7 @@ trait MapSpecSimpleHash[F[_]] extends MapSpec[F] { this: McasImplSpec =>
} yield ()
}

test("Map.Extra should perform values correctly") {
test("Map.Extra should perform `values` correctly") {
for {
m <- mkEmptyMap[Int, String]
_ <- assertResultF(m.values.run[F], Vector.empty)
Expand All @@ -76,14 +84,54 @@ trait MapSpecSimpleHash[F[_]] extends MapSpec[F] { this: McasImplSpec =>
_ <- assertEqualsF(v2, Vector("abc", "xyz"))
} yield ()
}
}

trait MapSpecSimpleOrdered[F[_]] extends MapSpec[F] { this: McasImplSpec =>
test("Map.Extra should perform `keys` correctly") {
for {
m <- mkEmptyMap[Int, String]
_ <- assertResultF(m.keys.run[F], Chain.empty)
_ <- (Rxn.pure(42 -> "foo") >>> m.put).run[F]
_ <- (Rxn.pure(99 -> "bar") >>> m.put).run[F]
v1 <- m.keys.run[F]
_ <- assertEqualsF(v1.iterator.toSet, ScalaSet(42, 99))
_ <- (Rxn.pure(99 -> "xyz") >>> m.put).run[F]
_ <- (Rxn.pure(128 -> "abc") >>> m.put).run[F]
_ <- m.del[F](42)
v2 <- m.keys.run[F]
_ <- assertEqualsF(v2.iterator.toSet, ScalaSet(99, 128))
} yield ()
}

override type MyMap[K, V] = Map.Extra[K, V]
test("Map.Extra should perform `valuesUnsorted` correctly") {
for {
m <- mkEmptyMap[Int, String]
_ <- assertResultF(m.valuesUnsorted.run[F], Chain.empty)
_ <- (Rxn.pure(42 -> "foo") >>> m.put).run[F]
_ <- (Rxn.pure(99 -> "bar") >>> m.put).run[F]
v1 <- m.valuesUnsorted.run[F]
_ <- assertEqualsF(v1.iterator.toSet, ScalaSet("bar", "foo"))
_ <- (Rxn.pure(99 -> "xyz") >>> m.put).run[F]
_ <- (Rxn.pure(128 -> "abc") >>> m.put).run[F]
_ <- m.del[F](42)
v2 <- m.valuesUnsorted.run[F]
_ <- assertEqualsF(v2.iterator.toSet, ScalaSet("abc", "xyz"))
} yield ()
}

def mkEmptyMap[K : Hash : Order, V]: F[Map.Extra[K, V]] =
Map.simpleOrderedMap[K, V].run[F]
test("Map.Extra should perform `items` correctly") {
for {
m <- mkEmptyMap[Int, String]
_ <- assertResultF(m.items.run[F], Chain.empty)
_ <- (Rxn.pure(42 -> "foo") >>> m.put).run[F]
_ <- (Rxn.pure(99 -> "bar") >>> m.put).run[F]
v1 <- m.items.run[F]
_ <- assertEqualsF(v1.iterator.toSet, ScalaSet(42 -> "foo", 99 -> "bar"))
_ <- (Rxn.pure(99 -> "xyz") >>> m.put).run[F]
_ <- (Rxn.pure(128 -> "abc") >>> m.put).run[F]
_ <- m.del[F](42)
v2 <- m.items.run[F]
_ <- assertEqualsF(v2.iterator.toSet, ScalaSet(128 -> "abc", 99 -> "xyz"))
} yield ()
}
}

trait MapSpec[F[_]]
Expand Down

0 comments on commit ea4023d

Please sign in to comment.