MapRef
MapRef
is used internally as a small wrapper around an immutable Map
inside of a cats.effect.concurrent.Ref
.
You can use it as a map of concurrent values that you can access across your application.
import cats.effect.IO
import dev.rpeters.fs2.es.data.MapRef
//Create a concurrent MapRef
val example = MapRef[IO].empty[String, String].flatMap { map =>
for {
_ <- map.add("key" -> "value") //Upsert a value to the map
res1 <- map.get("key")
_ <- map.del("key")
res2 <- map.get("key")
} yield (res1, res2)
}
example.unsafeRunSync()
// res0: (Option[String], Option[String]) = (Some(value = "value"), None)
It has a couple handy extra operators besides just add/get/del operations that you might find useful.
MapRef#modify
allows you to atomically modify the contents of an entry by-key and return a result value:
val exampleModify = MapRef[IO].of(Map("key" -> "value")).flatMap { map =>
for {
resFromModify <- map.modify("key")(v => s"$v but modified" -> "result")
resFromGet <- map.get("key")
} yield (resFromModify, resFromGet)
}
exampleModify.unsafeRunSync()
// res1: (Option[String], Option[String]) = (
// Some(value = "result"),
// Some(value = "value but modified")
// )
As well as MapRef#upsertOpt
that conditionally either modifies or upserts a value for a given key:
val exampleUpsertOpt = MapRef[IO].of(Map("key" -> "value")).flatMap { map =>
//A helper function for either modifying or inserting a new value
def upsertFunc(optV: Option[String]): (String, String) = optV match {
case Some(_) => "value exists" -> "value exists result"
case None => "new value" -> "new value result"
}
for {
upsertExisting <- map.upsertOpt("key")(upsertFunc)
upsertNew <- map.upsertOpt("newKey")(upsertFunc)
resExisting <- map.get("key")
resNew <- map.get("newKey")
} yield (upsertExisting, upsertNew, resExisting, resNew)
}
exampleUpsertOpt.unsafeRunSync()
// res2: (String, String, Option[String], Option[String]) = (
// "value exists result",
// "new value result",
// Some(value = "value exists"),
// Some(value = "new value")
// )