Skip to content

Commit 090d638

Browse files
authored
Merge pull request #3829 from BalmungSan/mapref-docs
Improve MapRef docs
2 parents bd02450 + 33c93b1 commit 090d638

3 files changed

Lines changed: 73 additions & 3 deletions

File tree

docs/std/mapref.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
id: mapref
3+
title: MapRef
4+
---
5+
6+
A total map from a key to a `Ref` of its value.
7+
8+
```scala mdoc:silent
9+
import cats.effect.Ref
10+
11+
trait MapRef[F[_], K, V] {
12+
13+
/**
14+
* Access the reference for this Key
15+
*/
16+
def apply(k: K): Ref[F, V]
17+
}
18+
```
19+
20+
It is conceptually similar to a `Ref[F, Map[K, V]]`,
21+
but with better ergonomics when working on a per key basis.
22+
Note, however, that it does not support atomic updates to multiple keys.
23+
24+
Additionally, some implementations also provide less contention:
25+
since all operations are performed on individual key-value pairs,
26+
the pairs can be sharded by key.
27+
Thus, multiple concurrent updates may be executed independently to each other,
28+
as long as their keys belong to different shards.
29+
30+
### In-Memory database
31+
32+
This is probably one of the most common uses of this datatype.
33+
34+
```scala mdoc:reset:silent
35+
//> using lib "org.typelevel::cats-effect:3.5.1"
36+
37+
import cats.effect.IO
38+
import cats.effect.std.MapRef
39+
40+
trait DatabaseClient[F[_], Id, Data] {
41+
def getDataById(id: Id): F[Option[Data]]
42+
def upsertData(id: Id, data: Data): F[Unit]
43+
}
44+
45+
object DatabaseClient {
46+
def inMemory[Id, Data]: IO[DatabaseClient[IO, Id, Data]] =
47+
MapRef.ofShardedImmutableMap[IO, Id, Data](
48+
shardCount = 5 // Arbitrary number of shards just for demonstration.
49+
).map { mapRef =>
50+
new DatabaseClient[IO, Id, Data] {
51+
override def getDataById(id: Id): IO[Option[Data]] =
52+
mapRef(id).get
53+
54+
override def upsertData(id: Id, data: Data): IO[Unit] =
55+
mapRef(id).update(_ => Some(data))
56+
}
57+
}
58+
}
59+
```

kernel/shared/src/main/scala/cats/effect/kernel/Ref.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ import cats.syntax.all._
3434
* mutable data as `AtomicReference#compareAndSet` and friends are dependent upon object
3535
* reference equality.
3636
*
37-
* See also `cats.effect.std.AtomicCell` class from `cats-effect-std` for an alternative.
37+
* See also `cats.effect.std.AtomicCell` class from `cats-effect-std` for an alternative that
38+
* ensures exclusive access and effectual updates.
39+
*
40+
* If your contents are an immutable `Map[K, V]`, and all your operations are per-key, consider
41+
* using `cats.effect.std.MapRef`.
3842
*/
3943
abstract class Ref[F[_], A] extends RefSource[F, A] with RefSink[F, A] {
4044

std/shared/src/main/scala/cats/effect/std/MapRef.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,15 @@ import java.util.concurrent.ConcurrentHashMap
2626
import java.util.concurrent.atomic.AtomicBoolean
2727

2828
/**
29-
* This is a total map from K to Ref[F, V]. This allows us to use the Ref API backed by a
30-
* ConcurrentHashMap or similar.
29+
* This is a total map from `K` to `Ref[F, V]`.
30+
*
31+
* It is conceptually similar to a `Ref[F, Map[K, V]]`, but with better ergonomics when working
32+
* on a per key basis. Note, however, that it does not support atomic updates to multiple keys.
33+
*
34+
* Additionally, some implementations also provide less contention: since all operations are
35+
* performed on individual key-value pairs, the pairs can be sharded by key. Thus, multiple
36+
* concurrent updates may be executed independently to each other, as long as their keys belong
37+
* to different shards.
3138
*/
3239
trait MapRef[F[_], K, V] extends Function1[K, Ref[F, V]] {
3340

0 commit comments

Comments
 (0)