ORSet

@Serializable
class ORSet<E> : Quilted<ORSet<E>>

An observed-remove (add-wins) set of E. Concurrent add and remove of the same element resolve in favour of the add: a remove only cancels the adds it has actually witnessed (the Dots currently on the element), so an add the remover never saw survives. This is the usable form of the dots + causal context machinery — e.g. a presence set of who is currently online.

Built as a thin wrapper over Causal<DotMap<E, DotSet>>: each element key maps to the set of dots that added it; it is present iff that set is non-empty.

Immutable, and every mutator returns the change rather than a new set: add and remove hand back a Patch holding just the element they touched, which is what belongs on the wire. piece is the causal merge — it absorbs a patch, and it is also how a caller who wants the resulting whole set gets one: set.piece(set.add(replica, element)).

Absorbing a patch is a join over the whole set, so dropping many elements one at a time pays a join per element. removeAll is the bulk sibling of remove that pays one for the batch.

Samples

val a = ReplicaId("A")
val b = ReplicaId("B")

// Two peers have converged: "alice" is present on both, added by B.
var alpha = ORSet.empty<String>().piece { it.add(b, "alice") }
var bravo = alpha

// A re-adds "alice" and puts only the change on the wire. The delta names A's new dot
// *and* B's older one, which the re-add supersedes — so both peers drop the old dot.
val readd = alpha.add(a, "alice")
alpha = alpha.piece(readd)
bravo = bravo.piece(readd)
check(alpha == bravo)

// A concurrent add beats a concurrent remove: B's re-add mints a dot A's remove never saw.
val concurrent = alpha.add(b, "alice")
check(alpha.piece(alpha.remove("alice")).piece(concurrent).contains("alice"))

// A remove lands everywhere, because both peers agree on which dot is live. Had the delta
// above kept quiet about B's dot, it would still be alive on bravo — and "alice" would come
// back from the dead there.
val forget = alpha.remove("alice")
alpha = alpha.piece(forget)
bravo = bravo.piece(forget)
check(!alpha.contains("alice"))
check(!bravo.contains("alice"))

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard
val elements: Set<E>

The elements currently present.

Functions

Link copied to clipboard
fun add(replica: ReplicaId, element: E): Patch<ORSet<E>>

Add element on behalf of replica, minting a fresh dot — and return the change, one element and a short causal note, rather than the whole new set.

Link copied to clipboard
open fun causalDots(): Set<Dot>

The causal Dots this state has delivered — (author, author-seq) per op.

Link copied to clipboard

The per-author high-water of dots this state delivered and has since compacted away without retaining their identities.

Link copied to clipboard
fun contains(element: E): Boolean

True if element is currently present.

Link copied to clipboard
open operator override fun equals(other: Any?): Boolean
Link copied to clipboard
open override fun hashCode(): Int
Link copied to clipboard
open override fun piece(other: ORSet<E>): ORSet<E>

The causal merge of two replicas of this set.

Link copied to clipboard
fun remove(element: E): Patch<ORSet<E>>

Remove element — and return the change: the dots currently on it, retired, and nothing else. Absorbing that patch drops the element; the retired dots stay witnessed, so the removal propagates on merge. To hold the resulting set locally, set.piece(set.remove(…)).

Link copied to clipboard
fun removeAll(elements: Set<E>): Patch<ORSet<E>>

Remove every element of elements at once — and return the change: the dots currently on those elements, retired, and nothing else.

Link copied to clipboard
open override fun toString(): String