lattice_maps/or_map

Generation-aware observed-remove maps.

Concurrent updates join in one generation, with add-wins membership. Updating after an observed removal creates a fresh generation. The newest generation wins, even if it is removed; older values cannot return. Sparse synchronization needs a baseline or eventual delivery of all required deltas. An isolated later Sequence delta can temporarily have missing origins.

Types

A map with one recursive child schema.

Examples

let documents: or_map.ORMap(Int) =
  or_map.new(replica_id.new("A"), crdt.OrMapSpec(crdt.SequenceSpec))
or_map.keys(documents) // -> []
pub type ORMap(a) =
  crdt.ORMap(a)

A sparse generation-qualified change, not a map snapshot.

Examples

let map = or_map.new(replica_id.new("A"), crdt.LwwRegisterSpec(0))
let delta: or_map.ORMapDelta(Int) = or_map.empty_delta(map)
or_map.apply_delta(map, delta) // -> Ok(map)
pub type ORMapDelta(a) =
  crdt.ORMapDelta(a)

Values

pub fn apply_delta(
  map: crdt.ORMap(a),
  delta: crdt.ORMapDelta(a),
) -> Result(crdt.ORMap(a), crdt.MergeError)

Apply only touched keys, selecting generations before child changes.

Missing Sequence origins can produce an incomplete view until the required baseline or earlier deltas arrive. Bind the receiving map before editing it.

Examples

let source = or_map.new(replica_id.new("A"), crdt.GCounterSpec)
let assert Ok(#(_, delta)) =
  or_map.update_with_delta(source, "score", fn(value) { value })
let receiver = or_map.new(replica_id.new("B"), crdt.GCounterSpec)
let assert Ok(receiver) = or_map.apply_delta(receiver, delta)
or_map.keys(receiver) // -> ["score"]
pub fn bind(
  map: crdt.ORMap(a),
  replica: replica_id.ReplicaId,
) -> crdt.ORMap(a)

Bind a loaded or received map to the local writer without rewriting history.

Child callbacks receive identities scoped by key and generation. Keep each independent writer’s logical replica ID distinct.

Examples

let source = or_map.new(replica_id.new("A"), crdt.LwwRegisterSpec(""))
let snapshot = source |> or_map.to_json |> json.to_string
let assert Ok(loaded) = or_map.from_json(snapshot)
let local = or_map.bind(loaded, replica_id.new("B"))
or_map.replica_id(local) // -> replica_id.new("B")
pub fn delta_from_json(
  input: String,
) -> Result(crdt.ORMapDelta(String), json.DecodeError)

Decode a modern String delta. Legacy map deltas are intentionally rejected.

Examples

let map = or_map.new(replica_id.new("A"), crdt.LwwRegisterSpec(""))
let delta = or_map.empty_delta(map)
let encoded = delta |> or_map.delta_to_json |> json.to_string
let assert Ok(decoded) = or_map.delta_from_json(encoded)
or_map.apply_delta(map, decoded) // -> Ok(map)
pub fn delta_from_json_with(
  input: String,
  decoder: decode.Decoder(a),
) -> Result(crdt.ORMapDelta(a), json.DecodeError)

Decode a generic sparse delta.

Examples

let map = or_map.new(replica_id.new("A"), crdt.LwwRegisterSpec(42))
let delta = or_map.empty_delta(map)
let encoded = or_map.delta_to_json_with(delta, json.int) |> json.to_string
let assert Ok(decoded) = or_map.delta_from_json_with(encoded, decode.int)
or_map.apply_delta(map, decoded) // -> Ok(map)
pub fn delta_to_json(delta: crdt.ORMapDelta(String)) -> json.Json

Encode a String-payload sparse delta (version 2).

Examples

let map = or_map.new(replica_id.new("A"), crdt.LwwRegisterSpec(""))
let delta = or_map.empty_delta(map)
let encoded = delta |> or_map.delta_to_json |> json.to_string
or_map.delta_from_json(encoded) // -> Ok(delta)
pub fn delta_to_json_with(
  delta: crdt.ORMapDelta(a),
  encode: fn(a) -> json.Json,
) -> json.Json

Encode a generic sparse delta.

Examples

let map = or_map.new(replica_id.new("A"), crdt.LwwRegisterSpec(42))
let assert Ok(#(_, delta)) = or_map.update_delta(map, "answer", fn(_, _) {
  Ok(crdt.NoChange(crdt.LwwRegisterSpec(42)))
})
let encoded = or_map.delta_to_json_with(delta, json.int) |> json.to_string
or_map.delta_from_json_with(encoded, decode.int) // -> Ok(delta)
pub fn empty_delta(map: crdt.ORMap(a)) -> crdt.ORMapDelta(a)

Return an empty transport batch.

Examples

let map = or_map.new(replica_id.new("A"), crdt.TextSpec)
or_map.apply_delta(map, or_map.empty_delta(map)) // -> Ok(map)
pub fn from_json(
  input: String,
) -> Result(crdt.ORMap(String), json.DecodeError)

Decode a modern String snapshot. Call bind before editing as another writer.

Examples

let source = or_map.new(replica_id.new("A"), crdt.LwwRegisterSpec(""))
let encoded = source |> or_map.to_json |> json.to_string
let assert Ok(loaded) = or_map.from_json(encoded)
let local = or_map.bind(loaded, replica_id.new("B"))
or_map.replica_id(local) // -> replica_id.new("B")
pub fn from_json_with(
  input: String,
  decoder: decode.Decoder(a),
) -> Result(crdt.ORMap(a), json.DecodeError)

Decode a generic modern snapshot, including recursive configured defaults.

Legacy map snapshots require import_legacy instead.

Examples

let map = or_map.new(replica_id.new("A"), crdt.LwwRegisterSpec(42))
let encoded = or_map.to_json_with(map, json.int) |> json.to_string
let assert Ok(loaded) = or_map.from_json_with(encoded, decode.int)
or_map.spec(loaded) // -> crdt.LwwRegisterSpec(42)
pub fn get(
  map: crdt.ORMap(a),
  key: String,
) -> Result(crdt.Crdt(a), Nil)

Get an active child bound to this map’s local editing scope.

Examples

let map = or_map.new(replica_id.new("A"), crdt.GCounterSpec)
let assert Ok(map) = or_map.update(map, "score", fn(value) { value })
let assert Ok(crdt.CrdtGCounter(counter)) = or_map.get(map, "score")
g_counter.value(counter) // -> 0
pub fn import_legacy(
  input: String,
  spec: crdt.CrdtSpec(a),
  decoder: decode.Decoder(a),
  replica: replica_id.ReplicaId,
) -> Result(crdt.ORMap(a), json.DecodeError)

Import a coordinated legacy baseline into Initial generations.

Supply the agreed schema/default and a fresh editing identity if old allocation history is missing. Distribute a modern snapshot; do not mix old map deltas.

Examples

Convert an agreed legacy register-map baseline to a modern snapshot:

import gleam/dynamic/decode
import gleam/result
import lattice_core/replica_id
import lattice_maps/crdt
import lattice_maps/or_map

pub fn migrate(legacy_snapshot: String) {
  use map <- result.try(or_map.import_legacy(
    legacy_snapshot, crdt.LwwRegisterSpec(""), decode.string,
    replica_id.new("cutover-writer"),
  ))
  Ok(or_map.to_json(map))
}
pub fn keys(map: crdt.ORMap(a)) -> List(String)

Return active keys, in unspecified order.

Examples

let map = or_map.new(replica_id.new("A"), crdt.TextSpec)
let assert Ok(map) = or_map.update(map, "body", fn(value) { value })
or_map.keys(map) // -> ["body"]
pub fn merge(
  a: crdt.ORMap(a),
  b: crdt.ORMap(a),
) -> Result(crdt.ORMap(a), crdt.MergeError)

Merge into the left map’s local identity.

Examples

let a = or_map.new(replica_id.new("A"), crdt.GCounterSpec)
let b = or_map.new(replica_id.new("B"), crdt.GCounterSpec)
let assert Ok(merged) = or_map.merge(a, b)
or_map.replica_id(merged) // -> replica_id.new("A")
pub fn merge_as(
  a: crdt.ORMap(a),
  b: crdt.ORMap(a),
  replica: replica_id.ReplicaId,
) -> Result(crdt.ORMap(a), crdt.MergeError)

Merge into an explicit receiving identity, including remote-only children.

Examples

let a = or_map.new(replica_id.new("A"), crdt.SequenceSpec)
let b = or_map.new(replica_id.new("B"), crdt.SequenceSpec)
let local = replica_id.new("C")
let assert Ok(merged) = or_map.merge_as(a, b, local)
or_map.replica_id(merged) // -> local
pub fn merge_deltas(
  a: crdt.ORMapDelta(a),
  b: crdt.ORMapDelta(a),
) -> Result(crdt.ORMapDelta(a), crdt.MergeError)

Combine sparse changes without turning nested deltas into snapshots.

Examples

let map = or_map.new(replica_id.new("A"), crdt.TextSpec)
let assert Ok(#(first, a)) =
  or_map.update_with_delta(map, "first", fn(value) { value })
let assert Ok(#(updated, b)) =
  or_map.update_with_delta(first, "second", fn(value) { value })
let assert Ok(batch) = or_map.merge_deltas(a, b)
or_map.apply_delta(map, batch) // -> Ok(updated)
pub fn new(
  replica: replica_id.ReplicaId,
  spec: crdt.CrdtSpec(a),
) -> crdt.ORMap(a)

Create an empty map.

Examples

or_map.new(replica_id.new("A"), crdt.OrMapSpec(crdt.TextSpec))
pub fn prune(
  map: crdt.ORMap(a),
  stable: version_vector.VersionVector,
) -> crdt.ORMap(a)

Prune stable membership tombstones, but retain floors and current leaf history.

Outer stability never compacts inner Sequence/Text history or forwardings. The vector must cover the namespaced membership tags in map deltas, not the logical writer’s unrelated leaf clocks. Each key/generation is a causal scope.

Examples

let map = or_map.new(replica_id.new("A"), crdt.TextSpec)
// An empty frontier makes no events stable.
or_map.prune(map, version_vector.new()) // -> map
pub fn remove(map: crdt.ORMap(a), key: String) -> crdt.ORMap(a)

Remove observed membership; keep current-generation child history.

Examples

let map = or_map.new(replica_id.new("A"), crdt.TextSpec)
let assert Ok(map) = or_map.update(map, "body", fn(value) { value })
or_map.remove(map, "body") |> or_map.get("body") // -> Error(Nil)
pub fn remove_with_delta(
  map: crdt.ORMap(a),
  key: String,
) -> #(crdt.ORMap(a), crdt.ORMapDelta(a))

Remove observed membership and return a generation-bearing removal delta.

Examples

let map = or_map.new(replica_id.new("A"), crdt.TextSpec)
let assert Ok(map) = or_map.update(map, "body", fn(value) { value })
let #(removed, delta) = or_map.remove_with_delta(map, "body")
or_map.apply_delta(map, delta) // -> Ok(removed)
pub fn replica_id(map: crdt.ORMap(a)) -> replica_id.ReplicaId

Return the local editing identity.

Examples

let local = replica_id.new("A")
let map = or_map.new(local, crdt.TextSpec)
or_map.replica_id(map) // -> local
pub fn spec(map: crdt.ORMap(a)) -> crdt.CrdtSpec(a)

Return the complete child schema.

Examples

let schema = crdt.OrMapSpec(crdt.LwwRegisterSpec(42))
let map = or_map.new(replica_id.new("A"), schema)
or_map.spec(map) // -> schema
pub fn to_json(map: crdt.ORMap(String)) -> json.Json

Encode a String-payload modern snapshot (version 3).

Examples

let map = or_map.new(replica_id.new("A"), crdt.LwwRegisterSpec(""))
let encoded = map |> or_map.to_json |> json.to_string
or_map.from_json(encoded) // -> Ok(map)
pub fn to_json_with(
  map: crdt.ORMap(a),
  encode: fn(a) -> json.Json,
) -> json.Json

Encode a generic modern snapshot.

The encoder handles payloads and configured register defaults at every depth. Text remains a concrete String/grapheme type.

Examples

let map = or_map.new(
  replica_id.new("A"), crdt.OrMapSpec(crdt.LwwRegisterSpec(42)),
)
let encoded = or_map.to_json_with(map, json.int) |> json.to_string
or_map.from_json_with(encoded, decode.int) // -> Ok(map)
pub fn update(
  map: crdt.ORMap(a),
  key: String,
  callback: fn(crdt.Crdt(a)) -> crdt.Crdt(a),
) -> Result(crdt.ORMap(a), crdt.MergeError)

Join the callback’s full child state within the current generation.

Removal/re-add, rather than a non-monotone callback, resets a child. Use update_delta when a leaf operation can return a sparse delta, or when a new LWWRegister write needs the callback’s explicit author identity.

Examples

let map = or_map.new(replica_id.new("A"), crdt.GSetSpec)
let assert Ok(map) = or_map.update(map, "tags", fn(value) {
  let assert crdt.CrdtGSet(tags) = value
  crdt.CrdtGSet(g_set.add(tags, "reviewed"))
})
or_map.keys(map) // -> ["tags"]
pub fn update_delta(
  map: crdt.ORMap(a),
  key: String,
  callback: fn(crdt.Crdt(a), crdt.EditContext) -> Result(
    crdt.CrdtDelta(a),
    e,
  ),
) -> Result(
  #(crdt.ORMap(a), crdt.ORMapDelta(a)),
  crdt.UpdateError(e),
)

Run a sparse callback once, apply its delta, and return the resulting map/change.

Errors are atomic. NoChange still refreshes membership, so it is an add-wins update. The callback context supplies the scoped author for new LWWRegister writes. Nested maps return crdt.OrMapChange(child_delta).

Examples

Author a register write with the supplied scope, not the old write’s author:

import lattice_registers/lww_register

let map = or_map.new(replica_id.new("A"), crdt.LwwRegisterSpec(""))
let assert Ok(#(updated, delta)) =
  or_map.update_delta(map, "title", fn(value, context) {
    let assert crdt.CrdtLwwRegister(register) = value
    let #(_, change) = lww_register.set_with_delta(
      register, "New title", lww_register.timestamp(register) + 1,
      context.replica_id,
    )
    Ok(crdt.StateDelta(crdt.CrdtLwwRegister(change)))
  })
or_map.apply_delta(map, delta) // -> Ok(updated)

Keep an inner ORMap change sparse through the outer map:

import gleam/result
import lattice_text/text

let map: or_map.ORMap(String) =
  or_map.new(replica_id.new("A"), crdt.OrMapSpec(crdt.TextSpec))
let assert Ok(#(updated, delta)) =
  or_map.update_delta(map, "document", fn(value, _) {
    let assert crdt.CrdtOrMap(document) = value
    use #(_, change) <- result.try(
      or_map.update_delta(document, "body", fn(value, _) {
        let assert crdt.CrdtText(body) = value
        use #(_, change) <- result.try(text.append_with_delta(body, "Hello"))
        Ok(crdt.StateDelta(crdt.CrdtText(change)))
      }),
    )
    Ok(crdt.OrMapChange(change))
  })
or_map.apply_delta(map, delta) // -> Ok(updated)
pub fn update_with_delta(
  map: crdt.ORMap(a),
  key: String,
  callback: fn(crdt.Crdt(a)) -> crdt.Crdt(a),
) -> Result(#(crdt.ORMap(a), crdt.ORMapDelta(a)), crdt.MergeError)

Run a full-value callback once and return the touched-key state delta.

The delta carries the complete returned child state. For large Sequence/Text children, use update_delta instead.

Examples

let map = or_map.new(replica_id.new("A"), crdt.GSetSpec)
let assert Ok(#(updated, delta)) =
  or_map.update_with_delta(map, "tags", fn(value) {
    let assert crdt.CrdtGSet(tags) = value
    crdt.CrdtGSet(g_set.add(tags, "ready"))
  })
or_map.apply_delta(map, delta) // -> Ok(updated)
pub fn values(map: crdt.ORMap(a)) -> List(crdt.Crdt(a))

Return active, locally bound children, in unspecified order.

Examples

let map = or_map.new(replica_id.new("A"), crdt.TextSpec)
or_map.values(map) // -> []
Search Document