Skip to content

Commit b37a131

Browse files
committed
Add AtomicMap docs
1 parent eb53283 commit b37a131

File tree

1 file changed

+78
-0
lines changed

1 file changed

+78
-0
lines changed

docs/std/atomic-map.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
id: atomic-map
3+
title: Atomic Map
4+
---
5+
6+
A total map from `K` to `AtomicCell[F, V]`.
7+
8+
```scala mdoc:silent
9+
import cats.effect.std.AtomicCell
10+
11+
trait AtomicMap[F[_], K, V] {
12+
/**
13+
* Access the AtomicCell for the given key.
14+
*/
15+
def apply(key: K): AtomicCell[F, V]
16+
}
17+
```
18+
19+
It is conceptually similar to a `AtomicMap[F, Map[K, V]]`, but with better ergonomics when
20+
working on a per key basis. Note, however, that it does not support atomic updates to
21+
multiple keys.
22+
23+
Additionally, it also provide less contention: since all operations are performed on
24+
individual key-value pairs, the pairs can be sharded by key. Thus, multiple concurrent
25+
updates may be executed independently to each other, as long as their keys belong to
26+
different shards.
27+
28+
## Using `AtomicMap`
29+
30+
You can think of a `AtomicMap` like a `MapRef` that supports effectual updates by locking the underlying `Ref`.
31+
32+
```scala mdoc:reset:silent
33+
import cats.effect.IO
34+
import cats.effect.std.AtomicMap
35+
36+
trait State
37+
trait Key
38+
39+
class Service(am: AtomicMap[IO, Key, State]) {
40+
def modify(key: Key)(f: State => IO[State]): IO[Unit] =
41+
am(key).evalUpdate(f)
42+
}
43+
```
44+
45+
### Example
46+
47+
Imagine a parking tower,
48+
where users have access to specific floors,
49+
and getting a parking space involves an effectual operation _(e.g. a database call)_.
50+
In that case, it may be better to block than repeat the operation,
51+
but without blocking operations on different floors.
52+
53+
```scala mdoc:reset:silent
54+
import cats.effect.IO
55+
import cats.effect.std.AtomicMap
56+
57+
trait Car
58+
trait Floor
59+
trait ParkingSpace
60+
61+
class ParkingTowerService(state: AtomicMap[IO, Floor, List[ParkingSpace]]) {
62+
// Tries to park the given Car in the solicited Floor.
63+
// Returns either the assigned ParkingSpace, or None if this Floor is full.
64+
def parkCarInFloor(floor: Floor, car: Car): IO[Option[ParkingSpace]] =
65+
state(key = floor).evalModify {
66+
case firstFreeParkingSpace :: remainingParkingSpaces =>
67+
markParkingSpaceAsUsed(parkingSpace = firstFreeParkingSpace, car).as(
68+
remainingParkingSpaces -> Some(firstFreeParkingSpace)
69+
)
70+
71+
case Nil =>
72+
IO.pure(List.empty -> None)
73+
}
74+
75+
private def markParkingSpaceAsUsed(parkingSpace: ParkingSpace, car: Car): IO[Unit] =
76+
???
77+
}
78+
```

0 commit comments

Comments
 (0)