Set theory foundation


Purpose

This page explains the mathematical foundations of Reactive Entity Sets. Understanding these concepts provides deeper insight into why RES works the way it does and prepares you for advanced features like Views.


ReactiveEntitySet as a mathematical set

A ReactiveEntitySet can be understood as a mathematical set S where each element is a pair of (Entity ID, Data).

S = { (id₁, data₁), (id₂, data₂), ..., (idₙ, dataₙ) }

Key properties

Property Description
Uniqueness Each entity ID appears at most once
Membership An entity is either in the set or not (no partial membership)
Data association Each member has exactly one associated data value

Operations and closure

Closure property

A set family S is closed under an operation O if applying O to any set in S produces another set in S.

∀O ∈ Operations, ∀S ∈ S: O(S) ∈ S

This property ensures that operations on ReactiveEntitySet produce predictable, well-defined results.

Operations on ReactiveEntitySet

Operation Input Output Maintains closure
Register S, (id, data) S ∪ {(id, data)} Yes
Unregister S, id S \ {(id, _)} Yes
SetData S, id, data’ S with updated data Yes
Filter (View) S, predicate S’ ⊆ S Yes

All operations maintain closure. The result is always a valid ReactiveEntitySet (or subset thereof).


View theory

Views are subsets of a ReactiveEntitySet defined by a predicate. There are two fundamental types.

flowchart TB
    subgraph Parent["ReactiveEntitySet S"]
        direction TB
        E1["(id:1, HP:80)"]
        E2["(id:2, HP:25)"]
        E3["(id:3, HP:100)"]
        E4["(id:4, HP:15)"]
    end

    subgraph Static["Static View R(c₀)"]
        direction TB
        S1["Fixed predicate: HP < 30"]
        S2["id:2, id:4"]
    end

    subgraph Dynamic["Dynamic View R(c₁)"]
        direction TB
        D1["Context-aware predicate"]
        D2["P(data, ctx)"]
    end

    Parent --> Static
    Parent --> Dynamic

    style Static fill:#e6f3ff
    style Dynamic fill:#fff3e6

Static view R(c₀)

A static view depends only on the entity data.

R(c₀) = { (id, data) ∈ S | P(data) = true }

Where P is a predicate function: P: TData → bool

Example

// All enemies with health below 30
var lowHealthView = enemies.CreateView(state => state.Health < 30);

Characteristics

  • Predicate is fixed at creation time
  • View membership updates automatically when entity data changes
  • No external context required

Dynamic view R(c₁)

A dynamic view depends on both entity data and an external context.

R(c₁) = { (id, data) ∈ S | P(data, context) = true }

Where P is a predicate function: P: (TData, TContext) → bool

Example (conceptual)

Dynamic/context-dependent views are a planned extension and are not implemented yet. The following is a conceptual example:

// All enemies within 10 units of a given position
// (Planned API)

Characteristics

  • Predicate requires context to evaluate
  • Entity data changes trigger automatic re-evaluation
  • Context changes require explicit re-evaluation

The “Reactive” in views

Both R(c₀) and R(c₁) are Reactive with respect to entity state changes.

Entity state change → Predicate evaluation → View membership update → Notification

The difference is not about reactivity, but about whether the predicate requires external context.


Structural closure

Definition

Structural closure ensures that operations on a View cannot “escape” to the parent set.

View V = σ[P](S)

V is deterministically a subset of S
Operations on V stay within V
No path exists to access elements outside V

Safe API design

This principle guides API design.

var view = enemies.CreateView(state => state.Health < 30);

// ✅ Access within view
view.EntityIds
view.Count
view.ForEach(...)

// ❌ No backflow to parent set (API not provided)
// view.ParentSet  ← This should not exist

By not exposing a path back to the parent set, code operating on a View cannot accidentally access entities outside its scope.


Performance implications

Traditional approach

Query → Traverse all entities → Filter → Result
Cost: O(n) per query

Reactive view approach

Entity state change
  → Evaluate view predicate
  → Add/remove from view
  → Notify subscribers

Cost: O(v) per change, where v = number of views

Formal definitions

For those interested in the precise mathematical formulation.

ReactiveEntitySet

ReactiveEntitySet<TData> = (S, Σ, δ, notify)

Where:
  S: Set of (id, data) pairs
  Σ: Set of operations {Register, Unregister, SetData}
  δ: S × Σ → S (transition function)
  notify: S × Σ → Events (notification function)

Static view R(c₀)

R(c₀) = σ[P](S) = { e ∈ S | P(e.data) }

Where:
  P: TData → bool (predicate)
  σ: Selection operator

Dynamic view R(c₁)

R(c₁)(ctx) = σ[P(_, ctx)](S) = { e ∈ S | P(e.data, ctx) }

Where:
  P: (TData, TContext) → bool (parameterized predicate)
  ctx: TContext (evaluation context)

Summary

Concept Description
Set semantics RES is a mathematical set with unique IDs
Closure All operations produce valid sets
Static View R(c₀) Filter by data only
Dynamic View R(c₁) Filter by data + context
Reactive property Views auto-update on entity changes
Structural closure Views cannot escape to parent set

Implementation status

Views are implemented and available in v2.2.0 (unreleased).


Next steps


This site uses Just the Docs, a documentation theme for Jekyll.