Standard CRDTs from Shapiro et al.'s catalog, assembled from crdtlib's combinators.
Principal combinator
Derived combinator
Primitive CRDT
Primitives
Primitive CRDTs are not built from other CRDTs. They come in two families, and both use the identity as their interpretation.
AC functions
Any associative and commutative function is a CRDT: the state is a value, and each operation is merged into it. This is a relaxation of a state-based CRDT's merge, which must also be idempotent. Counters, grow-only counters, maxima of a join-semilattice and set unions are all instances.
Keeps every write that has not been overwritten by a causally later one: a new write removes the writes with strictly smaller timestamps, but not concurrent ones. It needs timestamps, so it is a CRDTₜ. An associative, commutative merge has no notion of timestamps, so this family cannot be reduced to the first one.
defmv_regₜ(σ:Type)[PartialOrderτ][DecidableLTτ][DecidableEqτ][LinearOrderσ]:CRDTₜτσ(Finset(Eventτσ))(Finset(Eventτσ))whereeffectes:=-- unfortunately this does not improve the complexity because the filter must go through all elements in the worst case-- the maximum number of elements in the set is equal to the number of replicas in the network-- insert e (Std.HashSet.filter (λ x ↦ ¬ x.t < e.t) s)inserte(s.filterλx↦¬x.t<e.t)interpret:=id
Registers
LWW Register
join (Lamport σ)
ωLamport σσLamport σγLamport σ
Values tagged with a Lamport time, ordered by time and then by value. Since the order is total, it is a join-semilattice, and keeping the maximum means the last write wins, with ties broken the same way everywhere.
A last-writer-wins register for when timestamps are only partially ordered. Concurrent writes cannot be resolved on arrival, so the state is that of a multi-value register, and the tie is broken when the register is read, by picking the largest value.
A live set and a tombstone set, both grow-only. An element is present if it is in the live set but not in the tombstone set, so once removed, it can never be added again.
Adds and removes are timestamped events, kept in two G-Sets. An element is present if one of its adds has no strictly later remove. When an add and a remove of the same element are concurrent, neither is later, so the add wins.
Every add tags its element with a unique token. A remove names the tagged pairs its replica has seen, so it only cancels adds that causally precede it, and a concurrent add survives. Structurally, it is a 2P-Set over tagged elements.
The paper's running example, the add-remove partial order. Inserting elem between prev and next adds the element to a set of nodes and two edges to a graph; removing it adds it to a tombstone set. The list is read by a traversal from ⊥ that emits a node once all its predecessors have been visited, and uses the order on elements to break ties between concurrent insertions at the same place.
The edges come from po_edges. The library also has polist, which tracks nodes with a PN-Set instead of a node set and a tombstone set. It allocates no separate tombstone set, and the JSON tree in the paper uses it.
Pairs each position in the list with a value from any CRDT c, kept by associate, so entries can be edited in place. The collaborative game in the paper uses it for a threaded chat, with an MV-LWW register of strings at each position.