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:
- Review-Runde zu „ExecProto“ mit Grok und ChatGPT. Ergebnis: keine zweite Funktionsrepräsentation. Der Blick verlagerte sich auf den Trusted Core.
- Cancellation. Die Absicht ist klar, die Mechanik offen.
- Nursery. Wofür sie zuständig ist.
- Einordnung von Jade: Architektur, Zielgruppen, Syntax, Domänen.
- Speichermodell. Fassung 3 ist maßgebend. Dazu Lücken im Trusted Core, Vorschläge und Aufwand.
- Wertsemantik und Identität. Normale Werte verhalten sich wie Zahlen, Identität gibt es nur für Ressourcen und
Strong/Weak. - Übergabearten an Parametern (
val/var). Offen. - Review des Addendums.
- Semantic Views → Kernform.
- Kontrollfluss:
withals Körper, Ausstiege, Fehler über Grenzen. - Pattern Matching: Markierung neuer Namen, Verengung,
_. provide. Zwischenstand.- Syntax: Punkt-Kurzform und
:=. - 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:
- die abgebildeten Referenzen nach dem Laden (Lebensdauer: die Plugin-Generation);
- 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 perSymbolId(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:
JadeValueheiß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).
osCapsist 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]¶
- 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
releaseläuft doppelt. - Eskalation mit Frist auf rein mechanisches Aufräumen (Daten aus F-12), mit einem eigenen Ergebnis. Das darf kein Trap sein (siehe Abschnitt 3).
- Ergebnisregel: Wurde die Cancellation angenommen, lautet das Ergebnis
Cancelled, auch wenn die Task danach normal zurückkehrt. - 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.
- Wer abgebrochen wird. Die Anforderung wandert den Besitzbaum hinunter. Das ist derselbe Baum, über den F-19 Traps nach oben koppelt.
- 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.
- Wie das Ergebnis heißt. Wer angefordert hat, ordnet das Ende als
Cancelledein. - 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) gegenDb[PostgresHandler](Handler) imenv:-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ürDeterministicerlaubten 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:
- Rückgabe oder Zuweisung an eine äußere Variable. Das ist lexikalisch sichtbar, der Compiler kopiert oder lehnt ab.
- Speichern in ein äußeres Objekt in einer aufgerufenen Funktion, etwa über einen änderbaren Parameter. Das ist unsichtbar.
- 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.
threadLocalin den Guarantees (§31, Addendum I.7/I.14) heißt unter M:NtaskLocal.- 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
Weakmüssen den umgezogenen Knoten weiter finden. Entweder ändert sich das Handle beim Umzug nicht (Region steht im Tabelleneintrag), oder äußereWeakin 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
JadeValueund 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:
- Handle-Layout
- Slot-Wiederverwendung
- Lebensdauer-Ordnung und Speicherprüfung
- Handler-Region-Regel
- welche Typen Identität tragen
- 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.1jdl-effect-rows-und-throw: §2.5 (M-1′), P-2′jdl-sprachreferenz: §10, §20.3,env:-Beispiele mitAlloc[ReqArena]jade-emergente-eigenschaften: §2.5, Z. 457–460jdl-blueprint-spec: Z. 287wie-jade-uebersetzt: Z. 996 (auch online noch alt)jade-service-typekind-arbeitsnotiz: Z. 115, 172, 421jdl-meta-phase: Z. 311, 37604a-label-inferenz-regeln: Regeln mitproven(Memory(…))11-jadevalue-und-register-layout: §2, §608-intrinsics-vm-api: Z. 101–10406-jme-handles-tags: §3–§5, JME-4sketch-escape-analysis-refcount-elision: §4, §706-generator-und-code-emission-v2: Z. 296–29710-source-audit: Z. 135
Gruppe B: Beispiele und Begriffe ersetzen
00-refinements01-grundlagen(Z. 138)02-typsystem(Z. 201, 630)03-runtime(§1)04-axiomatics-truthprofile00-ontology-types-tags-phantoms09-migration-checklistjdl-engine-descriptor-modeljade-frames-protos-subsystemsjade-commands-with-arenajade-semantics-checklistjade-repl-scriptmode-design-rationale(der Kopf „spec-synchron“ stimmt nicht mehr)jade-typsystem-notizen05-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-praegungjdl-typefn-spec08-grammar-sketch(den Verweis in Sprachreferenz §5.7 nachziehen)jdl-primitives-spec10-runtime-handles-und-memory-safetyjade-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 = aergibt 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/Weaksagen nur etwas über Existenz.Strong[Node]heißt: Den Knoten gibt es, solange ich das hier halte.Weak[Node]heißt: unbekannt, perupgradenachsehen.- Objekte hinter
Strongsind nach dem Bau unveränderlich.varan einemStrongheißt nur „auf etwas anderes zeigen“. Weakentsteht nur ausStrong.- Geteilte Änderung gibt es nur über Ressourcen per
with, etwa beim Mutex. - Wer eine Ressource berührt oder einen
Weakhochstuft, ist wederPurenochDeterministic. - Worker und Actors halten lokalen, nicht geteilten Zustand. Nachrichten sind Werte.
Warum¶
valals „der Wert ist unveränderlich“ funktioniert nur, wennvar b = akopiert. Das war Elias' eigener Einwand.- P-1 (
Pureheißt leere Row und keinRefMut) 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
PureundDeterministic, und der Cache der CompilerDB würde veraltete Werte liefern. - Wertsemantik macht automatische Kopien an Regionsgrenzen möglich.
Send/Syncspielt 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 alsvarund 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
racesind 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/RegionViewund 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.
matchbleibt 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:
Throwist ein abortiver Aufruf.- Trap ist ein Aufruf vom Typ
Never. Dropwird 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:
- Oberfläche;
- Kernform;
- getypte Kernform mit Ausstiegstabelle;
- 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:
AllFieldsHaveCodec (Elias, Meta-Funktion):
_als bewusster Auffangarm..Structals Muster.retgeht durchmatchundforhindurch.- Nach einem frühen
retkennt 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 ansummarygebunden. - Zeile 3:
retbeendet denwith-Block und liefert dessen Wert. - Zeile 5: Erst dieses
retbeendet 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
withbeantwortet.
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]:
- Beim Werfen wird das Ziel-
catchbestimmt. Es steht in der Evidence (G-4). - Die Nutzlast wird sofort in die Region dieses
catchkopiert. Identitätswerte ziehen um, weil alle anderen Verweise mit den abgewickelten Frames sterben. - Danach wird abgewickelt: Drops,
release, Region-Resets. - Das
catchbekommt den Wert.
Grenzfälle:
- Gibt es kein
catchin 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,continueund den Wert eines Blocks. Vorbild: Zig und WebAssembly. - Zwei Sorten von Zielen:
- Körper (Funktionsrumpf, Lambda,
with, vermutlichtry) sind das Ziel vonret. - Schleifen (
for,while,loop) sind das Ziel vonbreakundcontinue, aber nur innerhalb desselben Körpers. if,matchund 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 nichtUnitist, darf nicht still verworfen werden. Jeder Weg durch einwithmit Wert endet mitretoder mit etwas vom TypNever. break/continuequer durch einwithist ein Fehler. Umbau: Daswithliefert einen Wert, und die Entscheidung fällt draußen. Das hält auch Lock-Bereiche klein.- Wird
tryein Körper, ist Effect-Rows §1.3 („returnintry“) 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
loopoder aus einem Block mit Ziel. Beiforundwhilefehlt der Wert, wenn die Schleife von selbst endet. retist ein Ausdruck vom TypNever. Das passt zuif … 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,StoreUpundCloseUpkönnen aus der Core-IR verschwinden. ifgibt 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). varbindet eine lokale, veränderliche Kopie.asbindet ohne zusätzlichesval.- 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 egibt dem Inhalt einen Namen mit dem engeren Typ, etwa füre.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,
ifprü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
PureundTotalsein. - 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.
catchbraucht vermutlich keine globale Typidentität. JedesThrow-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]
providehat drei Rollen (Sprachreferenz §11.2): Protokoll implementieren, Methoden direkt am Typ, Service-Handler definieren.provideinstalliert 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]:
provideheiß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 bedeutetDb[…]nur noch die Rolle. - Idee: Installation für einen Block per
with Db[Primary] = … { … }. Dann sind alle Formen vonwithKö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]¶
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
.nameein 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 == .Adminbrauchen eine Typprüfung, die Typen auch von außen nach innen weitergibt (Suchwort: bidirectional type checking). - Bei
Option[User]wird nur inOptiongesucht. - 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 beival/varmit Wert, beim Zerlegen und beim Ergebnis eineswith.- Muster,
for,asund 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
- Handle-Layout: Region, Generation, Domäne; 8 oder 16 Byte.
- Cancellation-Mechanik, A oder B; Definition der Checkpoints.
- Code-Freigabe von Generationen: Frame-Scan oder Grenzzähler.
- Slot-Wiederverwendung unter M:N.
- Tail Calls: Garantie ja oder nein.
- ABI für Value-Structs über 64 Bit.
- Geladene Form der Protos: P-O20 erweitern;
TypeId/SymbolIdim Const-Pool ersetzen. - Layout in die Kompatibilitätsprüfung aufnehmen (P-22).
Sprache
- Übergabearten
val/var/move; Kennzeichnung an der Aufrufstelle. - Lambdas überleben den Aufruf standardmäßig nicht.
- Wie ein Wert aus mehrzeiligen Armen von
matchundifkommt. - Körper-Regel für
break/continueundtry; Verbot, den Wert eineswithstill zu verwerfen. - Schleifen mit Wert; Sprungmarken.
- Bedingungen am Arm; Wertprüfungen im Muster; Literale nur ganz oben.
- Unmarkierte Namen vergleichen nur mit Compile-Zeit-Bekanntem;
val-Abkürzung für ganze Muster. - Iterationsprotokoll.
provide: Schlüsselwort, Provider-Lesart, Überschneidungen, Form der Installation.- Name für
try. - Wird
val x = 5ohne Typangabe ein Fehler? - Wie Plugins neue Oberflächensyntax anmelden.
Begriffe
- „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.