Zum Inhalt

Jade/JDL — Gesprächszusammenfassung: Proto, Speicher, Kontrollfluss

Stand: 17.09.2026 Art: Arbeitsnotiz, nicht normativ. Sie hält nur fest, was im Gespräch geklärt oder ausgearbeitet wurde. Quellen: Online-Specs auf specs.iowling.at (abgerufen 16./17.09.2026), dazu die Projektdateien.

Legende

Marker Bedeutung
[E] von Elias entschieden
[K] Konsens der Review-Runde (Grok, ChatGPT, Claude)
[V] Vorschlag, ausgearbeitet, nicht ausdrücklich bestätigt
[B] Befund in den Specs (Widerspruch, veraltete Stelle, Lücke)
[O] offen

Überblick

Das Gespräch lief in dieser Reihenfolge:

  1. Review-Runde zu „ExecProto“ mit Grok und ChatGPT. Ergebnis: keine zweite Funktionsrepräsentation. Der Blick verlagerte sich auf den Trusted Core.
  2. Cancellation. Die Absicht ist klar, die Mechanik offen.
  3. Nursery. Wofür sie zuständig ist.
  4. Einordnung von Jade: Architektur, Zielgruppen, Syntax, Domänen.
  5. Speichermodell. Fassung 3 ist maßgebend. Dazu Lücken im Trusted Core, Vorschläge und Aufwand.
  6. Wertsemantik und Identität. Normale Werte verhalten sich wie Zahlen, Identität gibt es nur für Ressourcen und Strong/Weak.
  7. Übergabearten an Parametern (val/var). Offen.
  8. Review des Addendums.
  9. Semantic Views → Kernform.
  10. Kontrollfluss: with als Körper, Ausstiege, Fehler über Grenzen.
  11. Pattern Matching: Markierung neuer Namen, Verengung, _.
  12. provide. Zwischenstand.
  13. Syntax: Punkt-Kurzform und :=.
  14. Gesammelte offene Entscheidungen.

1. Review-Runde: Braucht Jade ein „ExecProto“?

Ausgangsfrage. Ein Briefing fragte, ob Jade über dem vorhandenen Proto noch eine zweite, stärker „laufzeitfertige“ Funktionsrepräsentation braucht. Der Proto enthält Bytecode, Const-Pool, Registerlayout und Debug-Info und ist unveränderlich und geteilt. Grok war dafür, ChatGPT skeptisch, Claude war die dritte Instanz.

Ergebnis [K]: kein ExecProto als eigene Entität

Warum. Nur zwei Datensorten haben wirklich eine andere Lebensdauer oder Identität als der Proto:

  1. die abgebildeten Referenzen nach dem Laden (Lebensdauer: die Plugin-Generation);
  2. optionale Cache-Slots für Quickening (veränderlich).

Code, Registerlayout, Unwind-Info, Ownership-Tabelle und Debug-Info sind in Artefakt- und Ladeform identisch. Eine zweite Vollkopie würde neun Zehntel kopieren, um ein Zehntel zu ändern.

Was stattdessen gilt [K]:

Artefakt-Proto        persistent · contentHash · relokierbare Referenzen
    ↓ Laden (Referenzen abbilden)
Geladener Proto       gehört zur Generation, nach Commit unveränderlich, Laufzeit-Ids
    + Link-Ergebnis   gebunden an die Dependency-Generationen
    + Cache-Slots     optional, VM-intern, veränderlich, lockfrei lesbar
ContextInstance       äußerste Evidence-Records
Task → Frames         proto · pc · caller · Registerfile (inkl. Evidence-Register)

Es gibt kein Sidecar, keinen Epoch-Mechanismus und kein Generationsfeld im Frame.

Befunde der Runde

  • [B] Service-Bindings sind schon gelöst, durch Evidence Passing (07 L-2, A-6). Der Handler wird als versteckter Parameter übergeben, ein Service-Aufruf ist ein indirekter Call. Beide anderen Modelle hatten eine Laufzeitsuche angenommen: Grok eine Binding-Referenz im ExecProto, ChatGPT ein Context-Sidecar. Beide haben das zurückgezogen.
  • [B] Cleanup-Metadaten sind schon beschlossen. Die Unwind-Info (Effect-Rows G-1 bis G-7) und die Fault Ownership Map (Fehlermodell F-12) gehören in den Proto. Das Briefing hatte das übersehen.
  • [B] Der Proto ist heute nicht laufzeitfertig. Der Const-Pool enthält TypeId, einen lauf-lokalen Arena-Index, und Felder per SymbolId (06 §4.6). Über die Artefaktgrenze dürfen aber keine Ids gehen („Wie Jade ausführt“ §8 und §11). Eine Ladeform existiert also zwingend.
  • [B] Das Frames/Protos-Dokument ist nur ein Leitfaden, nicht normativ, und enthält noch Reste aus der D-Zeit. Option B in §10.2 verändert den angeblich unveränderlichen Proto.
  • [B] Widersprüche zwischen Specs:
  • JadeValue heißt „getaggt“ (05 §3.2) und „ungetaggt“ (11 §2).
  • Das Registerlayout-Beispiel in 11 §4 ignoriert Alignment.
  • GetFieldK (06 §6.2) widerspricht 05 §9.2, wonach Extended-Befehle vor der Verifikation verschwinden.
  • TailCall (05 §6.6): Call plus Return begrenzt den Stack nicht.
  • [B] Namenskollisionen. „Materializer“ ist in der CompilerDB schon vergeben, „Linken“ bezeichnet in der Plugin-Spec den Build-Schritt. Der Ladeschritt heißt deshalb „Laden, Referenzen abbilden“.
  • [B] Quellenhinweis. Claude hatte zuerst eine veraltete Plugin-Spec gelesen (0.3.0). Aktuell ist 0.3.7 vom 16.09.2026. Die Aussagen zu Hold, Drain und Fingerprint gelten weiter.
  • Neu relevant: P-30 (die Generation gehört dem Loader), P-O20 (offen: wem die Layoutdaten zur Laufzeit gehören), P-22 (Replacement Compare View).
  • osCaps ist gestrichen. Admission heißt jetzt: Der Loader prüft konkrete Resource-Voraussetzungen vorab.

Einzelergebnisse

Thema Ergebnis Status
Lebensdauer von Plugin-Code Normsatz: Code einer Generation wird erst freigegeben, wenn Ring 0 nachweist, dass weder ein Frame noch ein Funktionswert noch ein nativer Aufruf darauf zeigt. [K]
Zusatz: Calls innerhalb einer Generation tragen dafür keine Laufzeitkosten. [V]
Mechanismus: Frame-Scan mit Handshake oder Grenzzähler an Generationswechseln. [O]
Statische Referenzen beim Reload bleiben an ihre Generation gebunden und werden nie dynamisch umgebogen [V]
Layout beim Reload Das Layout exportierter Value-Typen ist ABI und gehört in die Kompatibilitätsprüfung (P-22 oder Fingerprint). Sonst droht Speicherkorruption beim Hot-Reload. [K]
Evidence beim Reload Bereits erzeugte Evidence wird nicht an Ort und Stelle umgeschaltet (nur mit Indirektion plus ABI-Prüfung). Neue Evidence entsteht bei Context-Aktivierung, Context-Ableitung und Handler-Installation. Plugin §14.3 („keine neue Service-Auflösung“) evidence-konform umformulieren. [V]
Actors und langlebige Dienste beim Reload über Drain-Policy (Context-Governance) plus Cancellation [K]
Upvalues CloseUp (Lua-Erbe, „≥ baseReg“) passt nicht zu Frames als Byte-Puffer. Normiert wird eine stabile Capture-Zelle; kein Maschinenzeiger in einen Frame überlebt einen Safepoint. [K]
Frame-Speicher Nur normieren: Frames gehören der Task; Ring 0 kann alle Frames durchlaufen; Erschöpfung ist ein Trap. Der Rest ist Implementierungsdetail. [V]
Loader Darf prüfen, Referenzen abbilden, Strukturen anlegen und Befehle eins zu eins umkodieren. Darf nicht: Superinstruktionen bilden, Register umordnen, Cleanup rekonstruieren. Faustregel: Braucht das Loader-Ergebnis einen eigenen Verifier, ist der Loader ein zweiter Compiler. [V]
Quickening verändert den Proto nie; Cache-Slots sind ohne Lock konsistent lesbar [V]
Isolation durch Repräsentation trägt nicht als Sicherheitsargument: Autorität liegt an Handles, und Handles sind Daten. B-5 verlangt ohnehin eine Prüfung in Ring 0. [V]
Tail Calls Jade muss festlegen: keine Garantie, oder TailCall in den Core [O]

2. Cancellation

Absicht [E]. Cancellation ist ein gewollter Abbruch, kein Fehler. Auch beim vorzeitigen Ende werden die regulären Ressourcenpflichten erfüllt: normales Cleanup, nicht der Trap-Pfad. Das entspricht der Tabelle im Fehlermodell (§2.7).

Befund [B]. Die Mechanik ist in den Specs nicht entschieden. Die Runtime-Spec (03 §3) zeigt sogar ein Modell, in dem die Warteoperation die Cancellation als Wert meldet: ReceiveOutcome.Cancelled, dazu ein Worker-Loop mit | Cancelled => break. Scheduler §3.6 erlaubt die Übersetzung in einen Wert oder einen Domain-Fehler, §5.1 kennt „query cancellation“.

Die beiden Modelle [O]

A: Runtime wickelt ab B: Operation meldet
Idee Am Checkpoint startet eine Abwicklung bis zur Task-Grenze, mit der Cleanup-Darstellung von Throw. Jede Warteoperation hat einen Meldeweg (Outcome-Wert oder Domain-Fehler). Aufgeräumt wird auf den normalen Pfaden.
Neue Cleanup-Abdeckung an allen Calls, von denen aus ein Checkpoint erreichbar ist keine; G-7 bleibt wie es ist
Unterbrechungsstellen unsichtbar in der Signatur der Warteoperation sichtbar
Kann Code die Cancellation übergehen? nein ja, aber die Anforderung bleibt stehen
Trusted Core Abdeckungsregel, Trennung von Cleanup- und Handler-Einträgen, eigene Regel für FFI-Grenzen nur ein Fall „durch Cancellation geweckt“ im Warteergebnis der VM

Empfehlung von Claude: B. Unter A wäre der Cancelled-Arm in 03 §3 toter Code.

Eine dritte Möglichkeit, rein mechanisches Aufräumen wie beim Trap, kostet keine Metadaten. Sie widerspricht aber der Absicht „Cleanup normal“ und ist damit verworfen.

Unabhängig vom Modell nötig [V]

  1. Aufschub bei jedem Cleanup. Solange irgendein Frame Cleanup-Code ausführt, wird die Cancellation nicht zugestellt, egal warum der Cleanup läuft. Sonst bleiben Ressourcen halb freigegeben, oder release läuft doppelt.
  2. Eskalation mit Frist auf rein mechanisches Aufräumen (Daten aus F-12), mit einem eigenen Ergebnis. Das darf kein Trap sein (siehe Abschnitt 3).
  3. Ergebnisregel: Wurde die Cancellation angenommen, lautet das Ergebnis Cancelled, auch wenn die Task danach normal zurückkehrt.
  4. Die Nutzlast eines geworfenen Fehlers gehört während der Abwicklung der Task. Tritt mittendrin ein Trap auf, wird sie mit aufgeräumt.

3. Nursery: Wofür ist sie zuständig?

Ergebnis [V]. Für drei Dinge, nicht für ein viertes.

  1. Wer abgebrochen wird. Die Anforderung wandert den Besitzbaum hinunter. Das ist derselbe Baum, über den F-19 Traps nach oben koppelt.
  2. Wann abgebrochen wird. Auslöser sind Scope-Ende, Frist, ein fehlgeschlagenes Geschwister oder ein gewonnenes Rennen. Der Drain eines Contexts ist die Root-Nursery, die die Policy des Contexts ausführt.
  3. Wie das Ergebnis heißt. Wer angefordert hat, ordnet das Ende als Cancelled ein.
  4. Nicht zuständig: wie die Task reagiert. Das Aufräumen läuft in den eigenen Frames der Task.

Folgeregeln [V]:

  • Eine erzwungene Beendigung nach Fristablauf ist kein Trap. Sonst koppelt F-19 den Fault an die Eltern-Nursery, und der Context stirbt an seiner eigenen Drain-Frist.
  • Der Aufschub während des Cleanups betrifft nur die Task selbst, nicht die Weitergabe an ihre Kinder. Sonst wartet eine Eltern-Task am Scope-Ende ewig.
  • Grenzen der Zuständigkeit: Die Nursery besitzt Tasks, keinen Code. Plugin-Code, der auf einer Host-Task läuft, erreicht der Plugin-Drain nicht. Außerdem endet die Zuständigkeit an der Context-Grenze.

Befund [B]. Im Addendum (I.16) heißt die Nursery „nicht-normative Architekturidee“. F-19 [D] und F-21 [A] bauen aber auf dem Besitzbaum auf. Der Baum gehört deshalb in Ring 0; nur die Oberfläche (Spawn, Join) ist Stdlib.


4. Einordnung von Jade (Einschätzung)

  • Architektonisch ist Jade eine statisch typisierte Plattformsprache ohne GC, mit VM, Scheduler, JME und Plugin-Lebenszyklus. Eine Systemsprache im Sinn von Rust oder Zig ist sie nicht. Das Etikett „systems language“ lockt die falsche Zielgruppe an.
  • Verwandte: Koka und Effekt (Rows, Evidence), Erlang/BEAM (Tasks, Hot Reload), Rust (Ownership-Vokabular), Zig (Compile-Zeit), Swift (strukturierte Nebenläufigkeit), Roc (die Umgebung stellt Effekte bereit), Nix (Generationen).
  • Stärke: ein typisiertes Modell von der Funktion bis zur Systemkonfiguration. Das Prinzip „Mechanismen wegnehmen statt anbauen“ trägt.
  • Zielgruppen: Die realistische Einsteigerin ist die YAML-/Docker-Administratorin, nicht die Schülerin.
  • Es gibt drei Oberflächen: Ops (Blueprints, env:), Anwendungsentwicklung und Plattform-/Plugin-Autoren.
  • Es fehlt ein Dokument, das pro Gruppe festlegt, was sie nicht wissen muss.
  • Stolpersteine in der Syntax:
  • Die eckige Klammer hat viele Rollen, vor allem Db[Replica] (Rolle) gegen Db[PostgresHandler] (Handler) im env:-Block.
  • -> dient auch als Cast.
  • : und :> haben mehrere Rollen.
  • Domänen: Die Sprache ist domänenoffen. Die Laufzeit ist auf langlebige, isolierte, nachladbare Dienste zugeschnitten. Kernel, Embedded und harte Echtzeit gehen derzeit nicht.
  • Risiken:
  • Generator und Verifier sind der Vertrauensanker. Der Verifier muss auch die F-12-Tabellen prüfen.
  • Die Specs laufen auseinander, weil ältere Dokumente dem neuen Kern hinterherhinken. Vorschlag: ein Statusfeld pro Dokument.

5. Speichermodell

5.1 Maßgebende Fassung [E]

Fassung 3 ist maßgebend. Sie steht in jdl-storage-memoryregion-empfehlung und in „Wie Jade ausführt“ §10.

  • Orthogonale Achsen: Repräsentation (inline oder Handle), Ownership (Unique, Strong, Weak), Sharing, Storage-Region, Reclamation, Lifetime.
  • Der Context bindet StorageRoles an MemoryRegions. Der Typ wählt keinen Allokator.
  • Implizite Allokation geht in die aktive Default-Region und steht nicht in der Row.

Damit überholt:

  • Fassung 1: Memory-Policy am Typ (03 §1, 06-JME, 11).
  • Fassung 2: Alloc[A] in der Row, M-1, M-2, Arenapolymorphie (07 §5, Sprachreferenz §10, Effect-Rows).

Folgen [V]:

  • Die Aussage „diese Funktion alloziert nicht“ kann bei Bedarf als abgeleitetes Label zurückkommen (wie Pure), etwa für Kernel oder Echtzeit.
  • In P-2′ fällt Alloc[_] aus der für Deterministic erlaubten Menge. Fehlbare Storage-Operationen dürfen nie als neutral zählen.
  • Der Schlüssel memory: in Meta-Records beschreibt nur noch „inline oder Handle“. Das ist ein Kandidat zum Umbenennen.
  • Die offenen Punkte O-1 und O-2 der Sprachreferenz beantworten sich durch Default-Region und Storage-Service.

5.2 Was stimmig ist

  • Die Granularität: Nutzer verwalten Speichergrenzen und Lebensdauern, nicht einzelne Speicherblöcke.
  • Ein Context ist Fehlerdomäne, Speichergrenze und Autoritätsgrenze zugleich. Beim Trap nimmt die JME task-lokale Regionen am Stück zurück (F-12).
  • Handles erlauben Verschieben und Verdichten innerhalb einer Region, außer bei gepinnten Regionen (F-28).
  • Wertsemantik macht Kopien beim Verlassen einer Region unsichtbar.
  • Allokationsfehler: Implizite Erschöpfung ist ein Trap (M-1′). Wer Fehlbarkeit will, ruft eine ausdrücklich fehlbare Storage-Operation, die in der Row steht.
  • Rc-Zyklen lecken bewusst. Budgets begrenzen den Schaden.

5.3 Das Kernproblem: Regionen wirken dynamisch [V]

Die aktive Region gilt auch in aufgerufenem Code, und Bibliothekscode kennt sie nicht (§59). Ein Wert kann auf drei Wegen aus with Storage.region[Scratch] entkommen:

  1. Rückgabe oder Zuweisung an eine äußere Variable. Das ist lexikalisch sichtbar, der Compiler kopiert oder lehnt ab.
  2. Speichern in ein äußeres Objekt in einer aufgerufenen Funktion, etwa über einen änderbaren Parameter. Das ist unsichtbar.
  3. Ein Service mit Zustand (etwa ein Cache-Handler) legt seinen Eintrag in Scratch ab. Das ist ebenfalls unsichtbar.

Ein vergleichbares System ist Javas Echtzeit-Spezifikation mit Zuweisungsprüfungen zur Laufzeit (Suchwort: RTSJ ScopedMemory).

5.4 Vorschläge für den Trusted Core [V]

Eine Prüfung beim Schreiben eines Handles in ein Objekt (Suchwort: write barrier). Regionen haben dafür eine Lebensdauer-Ordnung.

Fall Folge
gleiche Region nichts
Wert aus kürzer lebender Region → Objekt in länger lebender Trap an der Ursache. Werte ohne Identität darf die Laufzeit stattdessen kopieren.
starker Verweis auf länger lebendes Objekt → Objekt in kürzer lebender Region erlaubt. Die Region merkt sich den Verweis (Suchwort: remembered set) und gibt ihn beim Reset frei. Löst §85.
Weak in eine kürzer lebende Region erlaubt; nach dem Reset scheitert upgrade

Weitere Regeln:

  • Drop-Listen: Objekte mit Drop(Custom) tragen sich in einer Bulk-Region in eine Drop-Liste ein. Beim normalen Verlassen laufen die Drops, beim Trap nicht (F-8). Löst §86.
  • Handler laufen in ihrer eigenen Region, nicht in der des Aufrufers. Die Default-Region reist dafür als festes Feld im Evidence-Record mit.
  • Harte Budgets für implizite Allokation gehören nur an Context-Grenzen. Erschöpfung ist ein Trap, und ein Trap beendet den ganzen Context (F-21). Innerhalb eines Contexts taugen Budgets nur als weiche Grenze oder über ausdrücklich fehlbare Operationen.
  • threadLocal in den Guarantees (§31, Addendum I.7/I.14) heißt unter M:N taskLocal.
  • Value-Structs über 64 Bit: keine Zeiger in fremde Frames über einen Safepoint hinweg. 11 §5 anpassen.
  • Slot-Wiederverwendung unter M:N erst, wenn jeder Worker einen sicheren Punkt passiert hat. Möglichst derselbe Mechanismus wie bei der Code-Freigabe.
  • Umzug beim Verlassen einer Region: Äußere Weak müssen den umgezogenen Knoten weiter finden. Entweder ändert sich das Handle beim Umzug nicht (Region steht im Tabelleneintrag), oder äußere Weak in Scratch hinein sind verboten.

Die Schlüsselentscheidung ist das Handle-Layout [O]. Heute sind es 8 Byte, {index, generation}. Daran hängen:

  • die Regions-Generation für einen Reset in O(1);
  • die Domänenprüfung an der Context-Grenze (B-5);
  • die Speicherprüfung beim Schreiben;
  • der Umzug am Regionsende;
  • Zähler, die zwischen Workern sicher sind;
  • die Größe von JadeValue und damit die Calling Convention.

5.5 Aufwand der Implementierung (Einschätzung)

Baustein Schwierigkeit
Handle-Tabelle mit Generation, Refcount und Weak, ein Worker mittel
Regionen aus Bausteinen (Zigs Allocator-Schnittstelle passt dazu) leicht bis mittel
Budget, Accounting leicht
Aufräumen beim Trap (F-12) mittel; der Verifier muss die Tabellen prüfen
Pins und Holds für FFI mittel bis schwer
Handle-Tabelle unter M:N schwer
Escape-Prüfung im Compiler schwer, durch die Speicherprüfung entschärft
Speicherprüfung, gemerkte Verweise, Drop-Listen mittel
Transfer zwischen Contexts mittel

Vor dem ersten JME-Code festlegen:

  1. Handle-Layout
  2. Slot-Wiederverwendung
  3. Lebensdauer-Ordnung und Speicherprüfung
  4. Handler-Region-Regel
  5. welche Typen Identität tragen
  6. Budget-Regel

5.6 Nachzuziehende Dateien [E: erledigt Elias selbst]

Die Zeilenangaben beziehen sich auf die Projektdateien.

Gruppe A: Inhalt muss neu gedacht werden

  • 07-cast-effekte-refinements: §5, §6 (P-2), §11.1
  • jdl-effect-rows-und-throw: §2.5 (M-1′), P-2′
  • jdl-sprachreferenz: §10, §20.3, env:-Beispiele mit Alloc[ReqArena]
  • jade-emergente-eigenschaften: §2.5, Z. 457–460
  • jdl-blueprint-spec: Z. 287
  • wie-jade-uebersetzt: Z. 996 (auch online noch alt)
  • jade-service-typekind-arbeitsnotiz: Z. 115, 172, 421
  • jdl-meta-phase: Z. 311, 376
  • 04a-label-inferenz-regeln: Regeln mit proven(Memory(…))
  • 11-jadevalue-und-register-layout: §2, §6
  • 08-intrinsics-vm-api: Z. 101–104
  • 06-jme-handles-tags: §3–§5, JME-4
  • sketch-escape-analysis-refcount-elision: §4, §7
  • 06-generator-und-code-emission-v2: Z. 296–297
  • 10-source-audit: Z. 135

Gruppe B: Beispiele und Begriffe ersetzen

  • 00-refinements
  • 01-grundlagen (Z. 138)
  • 02-typsystem (Z. 201, 630)
  • 03-runtime (§1)
  • 04-axiomatics-truthprofile
  • 00-ontology-types-tags-phantoms
  • 09-migration-checklist
  • jdl-engine-descriptor-model
  • jade-frames-protos-subsystems
  • jade-commands-with-arena
  • jade-semantics-checklist
  • jade-repl-scriptmode-design-rationale (der Kopf „spec-synchron“ stimmt nicht mehr)
  • jade-typsystem-notizen
  • 05-modulsystem-und-ffi (Z. 237)
  • README (Z. 349)
  • nur online: Notizen/jdl-types-tags-phantoms

Gruppe C: als Archiv markieren

  • bootstrap-seed.jdl, 11a-bootstrap-seed-beispiel, 11-bootstrap-seeding-praegung
  • jdl-typefn-spec
  • 08-grammar-sketch (den Verweis in Sprachreferenz §5.7 nachziehen)
  • jdl-primitives-spec
  • 10-runtime-handles-und-memory-safety
  • jade-design-decisions

Gruppe D: schon auf Fassung 3

  • wie-jade-ausfuehrt, spec-context-model-refactored, spec-resource-model, jade-fehlermodell, Addendum
  • Die Storage-Empfehlung selbst trägt noch „nicht normativ“. Ihr Status sollte angehoben werden.

Reihenfolge: zuerst 07 §5 und Sprachreferenz §10, weil fast alles darauf verweist.


6. Wertsemantik und Identität

Entscheidungen [E]

  • Normale Werte verhalten sich wie Zahlen. var b = a ergibt eine eigene Kopie, und eine Funktion ändert nichts beim Aufrufer. Intern darf die JME Speicher teilen und erst beim Ändern kopieren; das bleibt unsichtbar.
  • Geteilte Identität gibt es nur für Ressourcen und Strong/Weak.
  • Strong/Weak sagen nur etwas über Existenz. Strong[Node] heißt: Den Knoten gibt es, solange ich das hier halte. Weak[Node] heißt: unbekannt, per upgrade nachsehen.
  • Objekte hinter Strong sind nach dem Bau unveränderlich. var an einem Strong heißt nur „auf etwas anderes zeigen“.
  • Weak entsteht nur aus Strong.
  • Geteilte Änderung gibt es nur über Ressourcen per with, etwa beim Mutex.
  • Wer eine Ressource berührt oder einen Weak hochstuft, ist weder Pure noch Deterministic.
  • Worker und Actors halten lokalen, nicht geteilten Zustand. Nachrichten sind Werte.

Warum

  • val als „der Wert ist unveränderlich“ funktioniert nur, wenn var b = a kopiert. Das war Elias' eigener Einwand.
  • P-1 (Pure heißt leere Row und kein RefMut) setzt voraus, dass sich nichts heimlich mitändert.
  • Ressourcen sind laut Resource-Modell keine Row-Elemente. Ohne die Regel oben wäre eine Funktion mit Mutex-Parameter Pure und Deterministic, und der Cache der CompilerDB würde veraltete Werte liefern.
  • Wertsemantik macht automatische Kopien an Regionsgrenzen möglich. Send/Sync spielt für normale Daten keine Rolle mehr.

Folgen [V]

  • Verlassen einer Region: Normale Werte werden kopiert. Identitätswerte, die im Block entstanden sind, ziehen um. Das geht nur am Blockende, und Teilen innerhalb des Ergebnisses bleibt erhalten.
  • Der Compiler braucht eine abgeleitete Eigenschaft „trägt Identität“, die sich über Felder vererbt. Das sieht wie Stdlib aus (Mutex), ist aber Compilerwissen.
  • Unveränderliche Strong-Objekte dürfen gefahrlos von mehreren Tasks gelesen werden. Die Zähler müssen dafür zwischen Workern sicher sein: entweder beim Senden kopieren oder den Zähler beim ersten Senden auf eine atomare Variante umstellen (Suchwort: biased reference counting).
  • Der Preis der Wertsemantik sind versteckte Kopien (die bekannte Swift-Falle). Gegenmittel: Tooling-Hinweise und die Regel, dass die letzte Verwendung eines Werts verschiebt statt teilt.
  • Addendum H.2 („Das Speichermodell“) ist damit weitgehend beantwortet.

7. Übergabearten an Parametern [O]

Idee (Elias). Statt Ref[T]/RefMut[T] stehen die Modifier val/var am Parameter. val bzw. var des Aufrufers bleiben dabei erhalten.

Ausgearbeitet [V]:

  • Die Übergabeart ist keine Eigenschaft des Werts, also gehört sie als Modifier an den Parameter und nicht in den Typ. Ein Modifier kann nicht in einem Feld landen, die Regel „kein Escape“ wird dadurch zur Syntax.
  • Vorbilder: Pascal (var), Swift (inout), Hylo (Suchwort: Hylo parameter conventions).
  • Unter Wertsemantik ist eine lesende Übergabe von einer Kopie nicht zu unterscheiden. Der Compiler darf also selbst wählen.
Schreibweise Bedeutung
ohne Modifier (oder val) nur lesen; Kopie oder Leihe; nicht kopierbare Typen werden geliehen
var ändern erlaubt; das Argument muss selbst var sein
move (Name offen) Besitz übernehmen
Out mit Tupel-Rückgaben kaum noch nötig

Regeln dazu:

  • Exklusivität: Eine Variable darf nicht zweimal als var übergeben werden, und nicht gleichzeitig als var und zum Lesen (Suchwort: Law of Exclusivity in Swift).
  • Kennzeichnung an der Aufrufstelle? Offen.
  • Funktionstypen müssen die Übergabeart ebenfalls ausdrücken können.
  • Closures: var-Parameter dürfen nicht von Closures gefangen werden, die ihren Frame überleben.
  • Trusted Core: Ein var-Verweis ist Frame-relativ, weil der aufgerufene Code suspendieren kann.
  • P-1 wird zu: keine var-Parameter und kein Zugriff auf Zustand mit Identität.

Die Variante Ref[T]() ist nicht empfohlen: Eine Übergabeart ist kein Typ, und () hinter einem Typ liest sich wie ein Konstruktoraufruf.


8. Review des Addendums (Online-Fassung)

Was überzeugt. I.16 (Nursery) deckt die Nebenläufigkeit weitgehend ab:

  • Nursery und Supervisor sind getrennt.
  • Kombinatoren wie race sind JDL-Abstraktionen und bedeuten in jedem Context dasselbe.
  • Ein Actor überlebt die Anfrage, die ihn erzeugt hat.
  • Callbacks sind unterschieden in „derselbe Task kommt zurück“ und „fremder Thread ohne Jade-Task“.
  • Ob ein Abbruch über eine Bridge weitergereicht wird, steht im Vertrag.

Was I.16 fehlt [V]:

  • Eine erzwungene Beendigung ist kein Trap.
  • Die Ergebnisregel für Cancelled.
  • Der Aufschub während des Cleanups gilt nur für die Task selbst.
  • Ein Callback in derselben Task bindet sie an ihren Worker, und eine Cancellation darf nicht über fremde Frames abwickeln (B-1).

Was hinterherhinkt [B]:

Stelle Änderung
I.6, I.17 Region-Struktur nach Fassung 3 (Backing, Allocation, Reclamation, Lifetime, Budget, Accounting, Guarantees). Die Liste mischt heute Lebensdauer-Muster und Allokationsmuster.
I.7, I.14 „thread-lokal“ bzw. „worker-lokal“ → task-lokal für Regionen, die JDL sieht
I.9 harte Budgets nur an Context-Grenzen
H.2 „Das Speichermodell“ weitgehend beantwortet (Abschnitt 6)
H.1 „Relokierbare Referenzen“ das Ergebnis der ExecProto-Runde eintragen (P-30, P-O20)
I.11 arena_reset → Region zurücksetzen
G.2 gegen G.1 Der Wortlaut „Meta-Context … einmal in JDL beschrieben“ widerspricht G.1 und der Entscheidung, Ring-0-Fakten in Zig zu definieren.

Begriffe [B]:

  • „Cancellation“ hat zwei Bedeutungen: das Verwerfen einer CompilerDB-Auswertung (A.1) und der Abbruch einer Task. Die CompilerDB-Seite sollte einen eigenen Namen bekommen.
  • „Ownership“ hat vier Verwendungen (Wert, Task, Ressource, Region). Die Bedeutung ist überall stimmig, aber „MemoryRegion = Ownership + …“ in I.17 liest sich wie ein Widerspruch zur Achsentrennung. Besser: „Region-Besitzer“.
  • „View“ hat drei Bedeutungen: Semantic View, AllocatorView/RegionView und nicht besitzende Daten-Views.

9. Semantic Views und Kernform

9.1 Bewertung der Semantic Views [V]

Das Konzept ist stark. Es löst die Kopplung „jedes Konstrukt kennt jede Analyse“ (Suchwort: expression problem) mit der Query-Mechanik, die schon existiert. Die Grenze für Self-Hosting sitzt richtig, die Regel zur Körnung ist klug, und die Beweislogik ist ehrlich.

Die offene Naht [B]. Der IR-Builder liest den AST direkt (13 §1). Damit hat jedes Konstrukt zwei unabhängige Übersetzungen, eine für die Views und eine für die IR. Die können auseinanderlaufen, und dann gilt ein Beweis nicht zwingend für den Code, der tatsächlich läuft. Das ist kritisch, weil Deterministic das Caching der CompilerDB steuert.

Weitere Punkte [V]:

  • Ein fehlender Beitrag zu einer View darf nie als „leer“ gelesen werden.
  • Datengetriebene Query-Verträge sind Arbeit in Zig: Schema, Identität, kanonische Serialisierung, Hash.

9.2 Lösung: Konstrukte entzuckern sich selbst in eine Kernform [V]

Die Idee kam von Elias: Ein Konstrukt ist für seine eigene Übersetzung verantwortlich. Ausgearbeitet heißt das: Es übersetzt sich in eine kleine, strukturierte Kernform, nicht direkt in Core IR. Views und IR entstehen beide aus dieser Kernform.

Warum nicht direkt in Core IR:

  • Core IR ist flach. Schleifen sind dort nur noch Sprünge, und Totalitätsbeweise brauchen die Struktur.
  • Aufräumen an Ausgängen ist Querschnittsarbeit. Beim direkten Übersetzen müsste jedes Konstrukt alle umschließenden Scopes kennen.
  • Fehlermeldungen lassen sich aus einer Form nahe an der Oberfläche leichter auf den Quelltext zurückführen.
Oberfläche (AST)
  ↓ Entzuckerung (jedes Konstrukt selbst)
Kernform
  ↓ Typprüfung, Musterprüfung, Move-Analyse, Drops einfügen
getypte Kernform ──→ Views → Beweise, Labels
  ↓ IR-Builder (kennt nur die Kernform)
IR → Extended→Core → Verifier → Generator

Folgen:

  • Es braucht keine eigene Autorität für Projektionen, und der View-Dispatch wird einfacher.
  • Ein neues Konstrukt braucht keine eigene Typregel, wenn seine Übersetzung gut typisiert ist.
  • match bleibt ein Knoten bis nach der Typprüfung, weil der Entscheidungsbaum die Varianten kennen muss.
  • Der Verifier bleibt der Vertrauensanker. Fremde Entzuckerungen dürfen falsch sein, aber nie unsicher. Dafür prüft der Verifier auch die F-12-Tabellen und die Unwind-Info.
  • Plugins dürfen neue Oberflächenkonstrukte mitbringen, aber keine neuen Kernknoten.
  • Der Name „Lowering“ ist in 13 schon vergeben. Für den neuen Schritt passt „Entzuckern“.

9.3 Die Kernform (Arbeitstitel) [V]

# Knoten Bedeutung
1 Wert Literal
2 Name Verweis; mit der Variante „im erwarteten Typ suchen“ für .name
3 Bau Struct, Variante, Tupel oder Liste aufbauen; Muster spiegeln diesen Knoten
4 Aufruf auch Operatoren, Service-Operationen und abortive Operationen (Throw)
5 Lambda Funktionswert mit gefangenen Werten
6 Bindung val/var mit einem Muster, das immer passen muss
7 Zuweisung an eine var, auch an einen Feldpfad darin
8 Block Folge von Schritten; öffnet einen Scope
9 Match Subjekt plus Arme; muss vollständig sein
10 Schleife endet nur über Verlassen; trägt ihre Herkunft
11 Verlassen(Ziel, Wert?) der einzige lokale Ausstieg; Typ Never
12 Installieren(Einträge, Handler, Rumpf) Evidence-Einträge für einen Rumpf: Handler, catch, aktive Region
13 Pflicht(Aufräumen, Rumpf) die Aufräumaktion läuft auf jedem Weg genau einmal

Ohne eigenen Knoten:

  • Throw ist ein abortiver Aufruf.
  • Trap ist ein Aufruf vom Typ Never.
  • Drop wird nach der Move-Analyse als implizite Pflicht eingefügt.
  • Suspension ist unsichtbar.
  • Die Nursery ist with über einer Ressource.

Grundsätze:

  • Die Kernform ist klein und geschlossen.
  • Sie ist strukturiert: keine freien Sprünge. Das passt direkt zum WASM-Backend.
  • Jeder Knoten hat einen Typ und eine Row.
  • Herkunft und Quellbezug reisen mit, etwa „for über eine endliche Liste“ für Totalitätsbeweise.

Typ- und Row-Regeln (Skizze):

  • Beim Zusammenführen von Armen fallen Never-Arme weg.
  • Eine Schleife ohne Verlassen hat den Typ Never.
  • Ein Funktionsrumpf, der normal enden kann, verlangt den Rückgabetyp Unit.
  • Installieren entfernt die behandelten Elemente aus der Row (C-2, A-7).
  • Die Aufräumaktion einer Pflicht darf nichts Abortives enthalten (C-7).
  • Verlassen fügt der Row nichts hinzu.

Aufräumen. Für jeden Ausstieg steht statisch fest, welche Scopes er verlässt. Die Unwind-Info aus G-1 lässt sich daraus direkt ableiten.

Regionen. with Storage.region[Scratch] ist Installieren (aktive Region) plus Pflicht (Region zurücksetzen). Ein eigener Region-Knoten, der zwischenzeitlich erwogen wurde, ist damit überflüssig (siehe 10.2).

9.4 Entzuckerung, Auszug [V]

Oberfläche Kernform
if c then a else b, if c { a } Match auf bool
and, or Match auf bool
x?.f, x ?? d Match auf Option
while c { b } Schleife { Match(c) { true → b, false → Verlassen(Schleife) } }
for p in xs { b } Block { Iterator; Schleife { Match(next) { Some(p) → b, None → Verlassen } } }
loop { b } Schleife
ret, break, continue Verlassen
with e as n { b } Bindung (acquire) plus Pflicht (release)
try b catch … Installieren(Throw-Menge, err → Match, b)
Operatoren, \|>, .., f-Strings Aufruf
implizite Konversion, Drop eingefügt von der Typprüfung bzw. der Move-Analyse

Die Protokollnamen (Iterator, next, acquire/release) sind Platzhalter.

9.5 Durchgearbeitete Beispiele (Kurzfassung)

firstAdminName (with, for, match, ret). Durchgespielt in vier Stufen:

  1. Oberfläche;
  2. Kernform;
  3. getypte Kernform mit Ausstiegstabelle;
  4. IR-Skelett mit Unwind-Tabelle.

Dazu ein Aufrufer mit try … catch, dessen Row {Fs, Throw[IoError]} bleibt (Y-1). Achtung: Unter der späteren with-Regel (Abschnitt 10) ist die ursprüngliche Fassung falsch und liefert immer None. Der Wert des with muss gebunden werden.

summarize (Match auf eine Union, continue, var, Lambda, Scratch-Region). Daraus kamen die Befunde zu Mustertests, Regionen, Lambdas und Schleifenwerten. Auch dieses Beispiel ist unter der with-Regel zu korrigieren:

// spekulativ
val summary = with Storage.region[Scratch] { … ret Summary { … } }
ret summary

AllFieldsHaveCodec (Elias, Meta-Funktion):

  • _ als bewusster Auffangarm.
  • .Struct als Muster.
  • ret geht durch match und for hindurch.
  • Nach einem frühen ret kennt die CompilerDB nur die Abfragen, die tatsächlich gestellt wurden.
  • Der Totalitätsbeweis braucht die Herkunft der Schleife.
  • Kleinigkeit: db.typeKind(TypeKind(t)) → db.typeKind(t).

10. Kontrollfluss

10.1 with ist ein Körper [E]

ret innerhalb eines with verlässt nur den with-Block. Wer dessen Wert braucht, bindet ihn; sonst steht dort implizit val _ = with …. Die Funktion verlässt man erst mit einem ret außerhalb.

// spekulativ
val summary = with Storage.region[Scratch] {
    …
    ret Summary { total, skipped, bigOrders: big }
}
ret summary
  • Zeile 1: Der Wert des with-Blocks wird an summary gebunden.
  • Zeile 3: ret beendet den with-Block und liefert dessen Wert.
  • Zeile 5: Erst dieses ret beendet die Funktion.

Warum:

  • Über die Grenze geht genau ein sichtbarer Wert. Bei einer Region ist die Reihenfolge damit eindeutig: Ergebnis feststellen, umziehen, zurücksetzen, draußen binden.
  • Es entstehen weniger Aufräumkanten.
  • Die Frage „Wie kommt ein Wert aus einem Block?“ ist für with beantwortet.

10.2 Fehler über die with-Grenze [E]

Fehler, die den with-Block verlassen, fängt man per catch ab oder reicht sie über die Row weiter.

Mechanik [V]:

  1. Beim Werfen wird das Ziel-catch bestimmt. Es steht in der Evidence (G-4).
  2. Die Nutzlast wird sofort in die Region dieses catch kopiert. Identitätswerte ziehen um, weil alle anderen Verweise mit den abgewickelten Frames sterben.
  3. Danach wird abgewickelt: Drops, release, Region-Resets.
  4. Das catch bekommt den Wert.

Grenzfälle:

  • Gibt es kein catch in der Task, ist die Task-Grenze das Ziel (F-5).
  • Beim Trap wird nichts kopiert.
  • Regel: Fehlertypen dürfen keine scope-gebundenen Ressourcen enthalten.

Die aktive Region im Evidence-Record hat damit zwei Nutzer: die Allokation und das Werfen.

10.3 Ziele und Ausstiege [V]

  • Ein Ausstiegsknoten (Verlassen) für ret, break, continue und den Wert eines Blocks. Vorbild: Zig und WebAssembly.
  • Zwei Sorten von Zielen:
  • Körper (Funktionsrumpf, Lambda, with, vermutlich try) sind das Ziel von ret.
  • Schleifen (for, while, loop) sind das Ziel von break und continue, aber nur innerhalb desselben Körpers.
  • if, match und einfache Blöcke sind für beides durchlässig.
  • Kein Sprung über eine Lambda-Grenze (Regel Z). Die VM braucht keine Sprünge über fremde Frames, und die Row bleibt ehrlich.
  • Folgen:
  • Verlassen überquert nie einen Pflicht- oder Installieren-Knoten.
  • Aufräumaktionen laufen nur am Ende eines Körpers, bei abortiven Ausstiegen und beim Trap.
  • Ein with, dessen Wert nicht Unit ist, darf nicht still verworfen werden. Jeder Weg durch ein with mit Wert endet mit ret oder mit etwas vom Typ Never.
  • break/continue quer durch ein with ist ein Fehler. Umbau: Das with liefert einen Wert, und die Entscheidung fällt draußen. Das hält auch Lock-Bereiche klein.
  • Wird try ein Körper, ist Effect-Rows §1.3 („return in try“) beantwortet.

Status: with als Körper hat Elias entschieden. Die Ausdehnung auf break/continue und try und das Verbot, den Wert still zu verwerfen, sind Vorschläge.

10.4 Weitere Vorschläge [V]

  • Schleifen mit Wert nur aus loop oder aus einem Block mit Ziel. Bei for und while fehlt der Wert, wenn die Schleife von selbst endet.
  • ret ist ein Ausdruck vom Typ Never. Das passt zu if … then ret x else y.
  • Lambdas: Parameter vom Funktionstyp überleben den Aufruf standardmäßig nicht (Suchwort: non-escaping closures in Swift).
  • Nur solche Lambdas dürfen per Referenz fangen, Frame-relativ wie ein var-Parameter.
  • Lambdas, die gespeichert werden dürfen, fangen per Wert.
  • Folge: LoadUp, StoreUp und CloseUp können aus der Core-IR verschwinden.
  • if gibt es in Inline-Form (then/else) und in Blockform.

11. Pattern Matching

11.1 Eine Mustersprache für alles [V]

Muster sind überall gleich: in match, catch, val-Zerlegung, for, Lambda-Parametern und im Meta-match auf Typen. Der einzige Unterschied: An manchen Stellen darf ein Muster scheitern (match, catch), an den übrigen muss es immer passen.

Prinzip: Ein Muster sieht aus wie der Ausdruck, der den Wert baut. Ausnahmen sind Bereich, Typtest und Bedingung. Auf Typebene gilt dieselbe Schreibweise; die Bedeutung ergibt sich aus dem Subjekt.

11.2 Binden oder vergleichen: Variante M [E]

Neue Namen im Muster werden ausdrücklich markiert, etwa Some(val x). Ob gebunden oder verglichen wird, entscheidet also weder die Namensauflösung (Variante N) noch die Groß- und Kleinschreibung (Variante S).

Warum:

  • Ein Tippfehler wird nie zu einer Bindung, die alles fängt.
  • Ein Muster hängt nicht von Namen anderswo im Programm ab.
  • Typvariablen ([val Inner]) brauchen keine Sonderregel.

Regeln [V]:

  • Wo Muster immer passen müssen, markiert die Stelle selbst: val (lat, lon) = pos, for (k, v) in map.
  • Nie gebunden werden: qualifizierte Namen, Konstruktorköpfe und Namen mit führendem Punkt (.Struct).
  • var bindet eine lokale, veränderliche Kopie.
  • as bindet ohne zusätzliches val.
  • Oder-Alternativen binden dieselben Namen mit denselben Typen.
  • Unmarkierte Namen vergleichen nur mit Dingen, die zur Compile-Zeit bekannt sind. [O, empfohlen]
  • Abkürzung val User { name, age } für ein ganzes Teilmuster. [O]

11.3 Benannte Felder nicht neu binden [E]

Der Zugriff über den Namen reicht (user.name). Eine Bindung bleibt nur nötig für:

  • positionale Nutzlast (Some(val u), Tupel);
  • Subjekte ohne Namen;
  • Nutzlast, die es erst nach einer Prüfung gibt.

Verengung im Arm [V, von Elias mit „ok“ quittiert]. Hat ein Arm eine Variante oder einen Typ geprüft, hat der Name des Subjekts im Arm den engeren Typ (Suchwort: flow typing, smart casts).

  • Unter Wertsemantik ist das sicher. Die Verengung endet, wenn das Subjekt im Arm neu zugewiesen wird.
  • as e gibt dem Inhalt einen Namen mit dem engeren Typ, etwa für e.id.
  • Denkbar ist dasselbe für if x is Card { … }.

Trusted Core. Nach der Verengung greift der Code auf die Nutzlast zu, ohne selbst noch einmal zu prüfen. Sicher ist das nur auf einem von zwei Wegen:

  • Der VM-Befehl für den Zugriff nennt die erwartete Variante und trappt bei Abweichung.
  • Der Verifier beweist, dass vorher eine passende Prüfung stattfand. Das ist dieselbe Flussanalyse wie bei „kein Lesen nach Move“.

11.4 Bedingungen am Arm und Wertprüfungen im Muster [O]

Elias ist unsicher, ob | … if … und dueDays: 0..30 überhaupt gewollt sind.

Ohne beides:

  • Muster prüfen die Form, if prüft Werte.
  • Es gibt kein erzwungenes _.
  • Vollständigkeit heißt dann fast nur noch: alle Varianten genannt.
  • C-5 funktioniert ohne Einschränkung.
  • Später hinzufügen geht ohne Bruch, wegnehmen nicht.

Was dabei verloren geht: Arme können nicht mehr zum nächsten durchfallen, und ein Match auf Zahlen oder Texte geht nicht mehr.

Mittelweg [V]: Literale nur ganz oben, also wenn das Subjekt selbst eine Zahl, ein Text oder ein fester Werttyp ist. Keine Literale in verschachtelten Feldern, keine Bereiche.

Falls Wertprüfungen bleiben [V]:

  • Nur mit eingebautem, strukturellem Vergleich. Der Compiler ordnet Tests um, also müssen sie Pure und Total sein.
  • Bedingungen am Arm werden in Quelltext-Reihenfolge ausgewertet und zählen nicht für die Vollständigkeit.

11.5 _ [E]

Ein bewusster Auffangarm ist legitim, etwa „nur .Struct zählt, alles andere ergibt false“. Bei Mengen, die wachsen können (etwa TypeKinds), ist _ sogar Pflicht. Problematisch war nur das _, das Bedingungen am Arm erzwingen.

11.6 Laufzeit [V]

  • Enums und Unions tragen eine lokale Nummer innerhalb ihrer geschlossenen Menge. Globale Typidentität trägt nur DynValue. Addendum A.5 sollte das präziser sagen.
  • Unions sind strukturell. Die Nummerierung braucht deshalb eine kanonische Reihenfolge, die über Module und Generationen hinweg stabil bleibt. Das hängt an Addendum H.1.
  • catch braucht vermutlich keine globale Typidentität. Jedes Throw-Element hat einen eigenen Evidence-Eintrag, der Handler weiß also, welches Mitglied geworfen wurde.
  • Entscheidungsbaum: Jeder Test läuft höchstens einmal. Die Vollständigkeitsprüfung ist Compilerarbeit (Suchwort: pattern usefulness, Maranget).

11.7 Musterformen der Kernform [V]

  • _
  • Bindung (val/var)
  • Literal oder Konstante (Status 11.4)
  • Bereich (Status 11.4)
  • Konstruktor mit ..
  • Typtest, optional mit Bindung
  • Oder
  • as

Bedingungen gehören zum Arm, nicht zum Muster (Status 11.4).


12. provide: Zwischenstand [O]

Elias ist unsicher, ob provide ein Schlüsselwort bleibt. Die Diskussion wurde bewusst abgebrochen („wir schweifen ab“).

Festgehalten:

  • [B] provide hat drei Rollen (Sprachreferenz §11.2): Protokoll implementieren, Methoden direkt am Typ, Service-Handler definieren. provide installiert nichts. Claudes frühere Zeile „provide → Installieren“ in der Kernform war falsch.
  • [B] Installiert wird heute auf drei Wegen:
  • rt.registerHandler, vermutlich ein Überbleibsel der abgeschafften Handler-Map;
  • Runtime.withHandler, das wie Stdlib aussieht, aber Compilerwissen ist (A-6);
  • env:.
  • Elias' Lesart: Provider haben in JDL eine herausgehobene Stellung. Engines können Provider sein, Typen können provided werden, Protokollbindungen liefern Provider.

Daraus folgt [V]:

  • provide heißt: die Antwort auf eine Frage mit festem Schlüssel bereitstellen. Installation heißt: auswählen (env:, with).
  • Kohärenz folgt aus dem Schlüssel: Pro Schlüssel gibt es genau einen Block.
  • Handler in der for-Form (provide Db for PostgresHandler). Dann bedeutet Db[…] nur noch die Rolle.
  • Idee: Installation für einen Block per with Db[Primary] = … { … }. Dann sind alle Formen von with Körper.

Offen:

  • Ist ein provide-Block selbst der Provider, oder liest ihn eine Engine?
  • Wie werden Überschneidungen zwischen generischen und konkreten provide-Blöcken behandelt: verbieten, das Spezifischere gewinnt, oder nur ein Paket darf generisch antworten?

Für den Kontrollfluss spielt das keine Rolle: provide ist kein Block-Konstrukt.


13. Syntax: Punkt-Kurzform und :=

13.1 .name im erwarteten Typ [E: soll Standard werden]

// spekulativ
val balance : RevisionCell[i64] = .init(100)

Regel [V]. Ein führender Punkt sucht im Typ, der an dieser Stelle erwartet wird.

  • Stellen mit erwartetem Typ: Typangabe, Parameter, Rückgabe, Feld beim Bauen, Subjekt eines Musters, Zuweisung.
  • Gefunden werden: Varianten, Konstanten und Funktionen, die genau diesen Typ liefern.
  • Generische Parameter kommen aus der Typangabe.
  • Ohne erwarteten Typ ist .name ein Fehler, und die Meldung nennt die Alternativen.
  • Vorbilder: Zig (Suchwort: decl literals), Swift (Suchwort: implicit member expression).

Festzulegen [V/O]:

  • Nur ein einzelnes Glied, keine Ketten.
  • Vergleiche wie role == .Admin brauchen eine Typprüfung, die Typen auch von außen nach innen weitergibt (Suchwort: bidirectional type checking).
  • Bei Option[User] wird nur in Option gesucht.
  • Die lange Form bleibt gültig; der Formatter bevorzugt die kurze.

Kernform: ein Namens-Knoten mit Markierung, aufgelöst in der Typprüfung. Trusted Core: keine Auswirkung.

13.2 := für abgeleitete Typen [E: gewollt]

// spekulativ
val balance : RevisionCell[i64] = .init(100)    // Typ geschrieben
val balance := RevisionCell[i64].init(100)      // Typ abgeleitet

Lesart [V]. := ist : Typ = mit leerem Typ (Vorbild: Odin, Jai). val und var bleiben, damit die Veränderlichkeit immer sichtbar ist.

Regeln [V]:

  • := gilt bei val/var mit Wert, beim Zerlegen und beim Ergebnis eines with.
  • Muster, for, as und Lambda-Parameter werden immer abgeleitet. Eine Typangabe ist dort erlaubt, := gibt es dort nicht.
  • Funktionssignaturen, Struct-Felder und Deklarationen auf Modulebene schreiben den Typ immer aus, weil er zur Oberfläche bzw. zur ABI gehört.
  • Merksatz: Der Punkt braucht links einen Typ, := hat keinen.
  • Teilweise Ableitung (RevisionCell[_]) kann später kommen.
  • Lexer: := ist ein einzelnes Token. Ob : = dasselbe bedeutet, muss festgelegt werden.

Offen [O]: Wird val x = 5 ohne Typangabe ein Fehler? Empfehlung: ja.


14. Gesammelte offene Entscheidungen

Trusted Core und VM

  1. Handle-Layout: Region, Generation, Domäne; 8 oder 16 Byte.
  2. Cancellation-Mechanik, A oder B; Definition der Checkpoints.
  3. Code-Freigabe von Generationen: Frame-Scan oder Grenzzähler.
  4. Slot-Wiederverwendung unter M:N.
  5. Tail Calls: Garantie ja oder nein.
  6. ABI für Value-Structs über 64 Bit.
  7. Geladene Form der Protos: P-O20 erweitern; TypeId/SymbolId im Const-Pool ersetzen.
  8. Layout in die Kompatibilitätsprüfung aufnehmen (P-22).

Sprache

  1. Übergabearten val/var/move; Kennzeichnung an der Aufrufstelle.
  2. Lambdas überleben den Aufruf standardmäßig nicht.
  3. Wie ein Wert aus mehrzeiligen Armen von match und if kommt.
  4. Körper-Regel für break/continue und try; Verbot, den Wert eines with still zu verwerfen.
  5. Schleifen mit Wert; Sprungmarken.
  6. Bedingungen am Arm; Wertprüfungen im Muster; Literale nur ganz oben.
  7. Unmarkierte Namen vergleichen nur mit Compile-Zeit-Bekanntem; val-Abkürzung für ganze Muster.
  8. Iterationsprotokoll.
  9. provide: Schlüsselwort, Provider-Lesart, Überschneidungen, Form der Installation.
  10. Name für try.
  11. Wird val x = 5 ohne Typangabe ein Fehler?
  12. Wie Plugins neue Oberflächensyntax anmelden.

Begriffe

  1. „Cancellation“ (CompilerDB-Seite), „Ownership“ (Region), „View“ (dreifach), der Schlüssel memory:.

15. Hinweise für Folgegespräche

  • Online-Specs haben Vorrang vor den Projektdateien. Versionen immer prüfen: Die Plugin-Spec war zunächst veraltet abgerufen.
  • Geprüfte Stände: Plugin-System 0.3.7 (16.09.2026), Fehlermodell 0.3.0, Effect-Rows-Notiz 0.1.0, Scheduler 0.2.0.
  • Die Zeilenangaben in 5.6 beziehen sich auf die Projektdateien.