lattice/or_map

An observed-remove map (OR-Map) CRDT.

Keys are tracked using an OR-Set with add-wins semantics: concurrent update and remove of the same key resolves in favor of the update. Each value is itself a CRDT (specified by CrdtSpec at construction), enabling nested convergent data structures.

Example

import lattice/crdt
import lattice/g_counter
import lattice/or_map

let map = or_map.new("node-a", crdt.GCounterSpec)
  |> or_map.update("score", fn(c) {
    let assert crdt.CrdtGCounter(gc) = c
    crdt.CrdtGCounter(g_counter.increment(gc, 10))
  })

Types

An OR-Map (observed-remove map) CRDT.

Keys are tracked using an ORSet(String) which provides add-wins semantics: if an update and a remove of the same key happen concurrently on different replicas, the update wins after merging.

Values are stored as the Crdt tagged union so they can be merged per-key using type-specific logic. The crdt_spec field records which CRDT type is used for values, enabling auto-creation of default values for new keys.

pub type ORMap {
  ORMap(
    replica_id: String,
    crdt_spec: crdt.CrdtSpec,
    key_set: or_set.ORSet(String),
    values: dict.Dict(String, crdt.Crdt),
  )
}

Constructors

Values

pub fn from_json(
  json_string: String,
) -> Result(ORMap, json.DecodeError)

Decode an ORMap from a JSON string produced by to_json.

Returns Error if the string is not valid JSON, does not match the expected format, or contains an unknown crdt_spec string.

pub fn get(map: ORMap, key: String) -> Result(crdt.Crdt, Nil)

Get the CRDT value at key.

Returns Ok(crdt) if the key is active in the OR-Set. Returns Error(Nil) if the key has never been added, or has been removed and not re-added.

pub fn keys(map: ORMap) -> List(String)

Return the list of all active keys (those present in the OR-Set).

Order is not guaranteed.

pub fn merge(a: ORMap, b: ORMap) -> ORMap

Merge two OR-Maps.

The OR-Set key trackers are merged with add-wins semantics: if a key was concurrently updated on one replica and removed on another, the key survives in the merged result. CRDT values are merged per-key using crdt.merge for type-specific convergence.

Merge is commutative, associative, and idempotent (a valid CRDT join).

pub fn new(replica_id: String, crdt_spec: crdt.CrdtSpec) -> ORMap

Create a new empty OR-Map for the given replica with the specified CRDT type.

The crdt_spec determines what type of CRDT is auto-created when update is called on a key that does not yet exist in the map.

pub fn remove(map: ORMap, key: String) -> ORMap

Remove a key from the OR-Map.

Removes the key from the OR-Set (marking it inactive). The underlying CRDT value is retained in the values dict so it can participate in per-key merge if the key is concurrently re-added on another replica.

pub fn to_json(map: ORMap) -> json.Json

Encode an ORMap as a self-describing JSON value.

The nested OR-Set (key_set) and CRDT values are double-encoded as JSON strings so they can be decoded using the existing from_json APIs.

Format: {"type": "or_map", "v": 1, "state": {"replica_id": "...", "crdt_spec": "...", "key_set": "...", "values": [...]}}

The encoded value can be restored with from_json.

pub fn update(
  map: ORMap,
  key: String,
  f: fn(crdt.Crdt) -> crdt.Crdt,
) -> ORMap

Apply a function to the CRDT value at key, auto-creating it if absent.

If the key does not exist, a default value is created from crdt_spec and passed to f. The key is added to the OR-Set, marking it active. The return value of f replaces (or sets) the value for that key.

pub fn values(map: ORMap) -> List(crdt.Crdt)

Return the CRDT values for all active keys.

Order is not guaranteed and does not correspond to the order of keys.

Search Document