----- ### Projektplan: Atome und das "Builder/Snapshot"-Pattern * **Datum:** 09. Oktober 2025 * **Uhrzeit:** 16:07 #### Motivation Die bisherige Diskussion hat zwei grundlegende Anforderungen an die Zustandsverwaltung herausgearbeitet: 1. **Allgemeine Sicherheit:** Für generische Datenstrukturen (Maps, Vektoren) bieten persistente Datenstrukturen durch ihre Immutabilität und lock-freie Lesbarkeit die höchste Sicherheit und Flexibilität bei konkurrierendem Zugriff. 2. **Spezialisierte Performance:** Für bestimmte Anwendungsfälle (z.B. große, append-only Time-Series) ist die Performance einer In-Place-Mutation überlegen, aber das Teilen des Zustands zwischen Threads erfordert eine sichere Synchronisation. Die Motivation ist daher, ein **standardisiertes, generisches Muster** zu definieren, das die rohe Performance einer mutierbaren "Arbeitskopie" mit der Sicherheit von unveränderlichen, lesbaren "Snapshots" für die konkurrierende Analyse kombiniert. #### Ziel 1. Etablierung des **"Builder/Snapshot"-Patterns** als idiomatisches Kernkonzept der Sprache für den Umgang mit hoch-performantem, geteiltem, mutablem Zustand. 2. Definition von **generischen Basis-Interfaces** (`IBuilder`, `IImmutable`), die dieses Pattern im Typensystem verankern. 3. Nahtlose Integration dieses Patterns mit den bestehenden `atom`- (für geteilten Zustand) und `var`- (für lokalen Zustand) Konstrukten. #### Ergebnis Das Ergebnis ist ein klares, zweigleisiges Modell für die Zustandsverwaltung, das auf dem generischen "Builder/Snapshot"-Pattern aufbaut. **1. Das generische Pattern: `IBuilder` / `IImmutable`** Wir definieren zwei konzeptionelle Basis-Interfaces, die den Vertrag des Patterns abbilden: * `IImmutable`: Ein **Marker-Interface**, das einen sicheren, unveränderlichen "Produkt"- oder Snapshot-Typ kennzeichnet. * `IBuilder`: Ein Interface, das einen "Builder" kennzeichnet. Es definiert die Fähigkeit, ein `IImmutable`-Produkt zu erzeugen. **2. Die Spezialisierung (Beispiel: `Series`-Interfaces)** Konkrete Datenstrukturen wie die `Series` spezialisieren diese generischen Rollen. ```delphi // --- Generische Basis-Interfaces --- // Kennzeichnet ein sicheres, unveränderliches Lese-Objekt (Snapshot). IImmutable = interface(IInterface) end; // Kennzeichnet ein mutierbares Builder-Objekt, das Snapshots von sich erzeugen kann. IBuilder = interface(IInterface) function CreateSnapshot: IImmutable; end; // --- Spezialisierte Interfaces für die 'Series' --- // ISeries ist die spezialisierte, lesbare Form von IImmutable. ISeries = interface(IImmutable) function GetCount: Int64; function GetItems(Idx: Integer): TScalar; property Count: Int64 read GetCount; property Items[Idx: Integer]: TScalar read GetItems; default; end; // IWriteableSeries ist der spezialisierte Builder. // Wichtig: Er implementiert den IBuilder-Vertrag, indem er eine CreateSnapshot-Methode // anbietet, die den spezialisierten Snapshot-Typ ISeries zurückgibt. IWriteableSeries = interface(IBuilder) function GetCount: Int64; property Count: Int64 read GetCount; procedure Add(const Item: TScalar.TValue; Lookback: Int64 = -1); function CreateSnapshot: ISeries; // Covarianter Return-Type zum Basis-Interface end; ``` **3. Die Anwendungsbeispiele in der Sprache** Das Pattern wird auf zwei Arten verwendet, je nach Kontext (`atom` oder `var`). Die RTL-Funktionen arbeiten mit den generischen `IBuilder`- und `IImmutable`-Konzepten. **Beispiel 1: Geteilter Zustand (via `atom`)** Der `atom` schützt den `IBuilder` vor konkurrierenden Schreibzugriffen. ```lisp (* * Szenario: Ein Ticker-Modul (Thread A) empfängt Live-Daten, * während eine UI (Thread B) die Daten zur Analyse anfordert. *) ; Ein globaler 'atom' wird erstellt. Er hält die einzige Instanz des Builders. (atom live-ticker (create-series-builder)) ; -- Thread A (Produzent) -- ; Modifiziert den Builder sicher über eine generische, synchronisierte RTL-Funktion, ; die auf jedem IBuilder in einem Atom funktioniert. (builder-update! live-ticker series-add new-tick-value) ; -- Thread B (Konsument) -- ; Fordert einen sicheren, unveränderlichen Snapshot an. ; Die generische 'snapshot'-Funktion ruft intern 'CreateSnapshot' auf. (let chart-data (snapshot live-ticker)) ; 'chart-data' ist nun eine sichere ISeries (IImmutable), die ohne Locks gelesen werden kann. (calculate-indicators chart-data) ``` **Beispiel 2: Lokaler Zustand (via `var`)** Dies ist der "Maschinenraum"-Modus für maximale Single-Thread-Performance. ```lisp (* * Szenario: Innerhalb einer Funktion wird eine komplexe Datenreihe * temporär aufgebaut, um ein einmaliges Ergebnis zu berechnen. *) (do ; Erzeuge einen lokalen, unsynchronisierten Builder. (var local-builder (create-series-builder)) ; Befülle den Builder direkt und ohne Lock-Overhead über typspezifische, ; unsynchronisierte RTL-Funktionen. (repeat 1000000 (series-add! local-builder (get-next-value))) ; Erzeuge einen finalen Snapshot für die Weiterverarbeitung. (let final-data (series-create-snapshot local-builder)) ; Gib das Ergebnis der Analyse zurück. (run-analysis-on final-data)) ``` #### Todo * [ ] Generische `IBuilder`- und `IImmutable`-Interfaces in Delphi definieren. * [ ] Finale `ISeries`- und `IWriteableSeries`-Interfaces (als Spezialisierungen) implementieren. * [ ] Eine konkrete `TSeries`-Klasse implementieren, die `IWriteableSeries` implementiert. * [ ] Die generischen, synchronisierten RTL-Funktionen `(builder-update! ...)` und `(snapshot ...)` für `atom`s erstellen, die auf `IBuilder` operieren. * [ ] Die spezifischen, unsynchronisierten RTL-Funktionen `(series-add! ...)` und `(series-create-snapshot ...)` für die `TSeries`-Implementierung erstellen. * [ ] Unit-Tests für beide Anwendungsfälle schreiben.