JDL — Namespaces und Module¶
Status: Draft, neu aufgebaut Geltung: Diese Spec regelt, wie ein Symbol adressiert wird und wer es erreichen darf. Nicht hier: Paketmanager, Versionierung, Build-System, Deskriptorformen einzelner Typen, Constitution-Regelsprache (nur Verankerung, §16).
Kennzeichnung
| Marke | Bedeutung |
|---|---|
[N] |
normativ, gilt |
[D] |
Entscheidung dieser Fassung, ersetzt eine frühere Aussage oder schließt eine Lücke |
[O] |
offen |
0. Auf einen Blick¶
0.1 Die vier Wege zu einem Symbol¶
| Weg | Beispiel | Import nötig | Wer kann das |
|---|---|---|---|
| Builtin | i32, str, () |
nein — hat keinen Pfad | jeder |
| Prelude | Result, Option, Ok, None |
nein — implizit in Scope | jeder |
| Import | import users::model : User |
ja | wer den Namespace sehen darf |
| FQN | users::service::findUser(..) |
nein — voller Pfad | wer den Namespace sehen darf |
Die letzten beiden unterliegen derselben Zugriffskette. Ein FQN ist keine Hintertür.
0.2 Die Zugriffskette¶
Anwendercode, Plugins
│ erreichbar: jdl::
│ gesperrt: jade:: und alle Zweige darunter
▼
jdl:: user-facing Stdlib
│ erreichbar: jade:: samt allen Zweigen
▼
jade:: privilegierte Systemmodule
└ jade::intrinsics Zweig darin — Naht zur VM
Die Grenze läuft einmal, zwischen Anwenderraum und jade::. Innerhalb von jade:: gibt es keine.
0.3 Die drei Dateien im Modul-Root¶
| Datei | Rolle | Inhalt | Wirkt |
|---|---|---|---|
mod.jdl |
was für den Ordner gilt | Deklarationen, Doku | auf das Modul |
prelude.jdl |
was innen ohne Import gilt | nur Importe | auf Module darunter |
constitution.jdl |
was freigegeben und erlaubt ist | Regeln | auf alles im Geltungsbereich |
Sie sind nicht austauschbar. Prelude ist Bequemlichkeit, Constitution ist Recht.
Teil I — Grundlagen¶
1. Begriffe¶
- Symbol — Name einer Definition (
User,findUser). - Modul — Quelltext-Einheit, identifiziert über ihren Dateipfad.
- Namespace — Raum, in dem Symbole eindeutig adressierbar sind. In JDL sind Module Namespaces.
- Builtin — Typ, der kein Modulmitglied ist und deshalb keinen Pfad besitzt (§7).
- FQN — vollständiger Pfad ab Root, ohne implizite Präfixe.
- Package — Distributionseinheit. Sie ist die Grenze für
package-Sichtbarkeit (§4) und für die Coherence-Regel (§15).
2. Notation¶
2.1 Trenner [N]¶
::trennt Namespaces. Ausnahmslos..ist Member-Zugriff — Felder (user.email), Methoden (vec.len()) und Enum-Varianten (OwnPolicy.Unique).
Namespaces werden nie mit . getrennt. Varianten nie mit ::.
2.2 Der Pfeil [N]¶
Weil in Signaturen ständig Pfeile vorkommen, hier zur Abgrenzung:
val summary = user -> UserSummary // Cast — linke Seite ist ein Wert
type F = (User) -> UserSummary // Funktionstyp — geklammerte Parameterliste
type G = (User) ->{Db} UserSummary // Funktionstyp mit Effect Row
Ein Funktionstyp hat immer eine geklammerte Parameterliste. Der ungeklammerte Pfeil gehört ausschließlich dem Cast.
3. Modulidentität¶
3.1 Dateipfad ist Modulname [N]¶
users/service.jdl definiert das Modul users::service. Es gibt keine Moduldeklaration in der Datei; der Pfad ist die Quelle der Wahrheit.
3.2 Zirkularität [N]¶
Zirkuläre Modulabhängigkeiten sind ein harter Compilerfehler.
3.3 Der FQN ist die stabile Identität [D]¶
Wo ein Schlüssel gebraucht wird, der einen Compilerlauf überlebt — kanonische Serialisierung, Inhaltshash, persistenter Cache, Seed-Eintrag — ist es der FQN.
TypeIds und andere Arena-Indizes werden in Anlagereihenfolge vergeben und sind lauf-lokal. Sie dürfen in keinem persistierten Schlüssel vorkommen. Für Builtins, die keinen Pfad haben, ist der nackte Name der Schlüssel; das ist die stabilste Form, weil es nichts gibt, das sich verschieben könnte.
Teil II — Freigabe¶
4. Es gibt keine Sichtbarkeits-Keywords [D]¶
JDL kennt kein pub und kein package.
Alles ist standardmäßig unsichtbar. Was ein Modul nach außen gibt, steht in seiner Constitution.
// users/service.jdl — keine Sichtbarkeitsangaben
def validateEmail(email: str) ->{} bool { ... }
def normalizeEmail(email: str) ->{} str
def findUser(email: str) ->{UserDb, Throw[UserError]} User
type User : struct { ... }
type UserError : enum {
NotFound
AlreadyExists
}
Ob findUser das Package verlässt, steht nicht hier, sondern in constitution.jdl.
Warum dort: Vier Mechanismen für eine Frage — pub, package, internal, mod.jdl-Re-Exports — werden einer. Die Oberfläche eines Packages ist an einer Stelle vollständig lesbar, statt über zwanzig Dateien verstreut. Wer wissen will, was ihm zusteht, liest eine Datei, statt einen Baum zu durchsuchen.
Die Monotonie hält. Freigabe ist die einzige Stelle, an der eine Constitution gewährt statt einschränkt. Geöffnet wird aus dem Zustand „nichts", und eine untergeordnete Constitution kann nur öffnen, was die übergeordnete ihr zu öffnen erlaubt. Details in spec-constitution-system §6.2.
Der Preis: Wer eine Datei aufschlägt, sieht nicht, ob eine Definition freigegeben ist. Der Ausgleich liegt beim Compiler: Er meldet, wenn sich der Fingerabdruck der freigegebenen Oberfläche ändert — additiv als Hinweis, strukturell als Warnung. Ein Keyword hätte das nie geleistet; es steht nur da. Siehe spec-constitution-system §6.3.
Die Schreibweise ist wahlweise namentlich oder über ein Muster; gespeichert wird immer die aufgelöste Menge, damit die Oberfläche eine lesbare Liste bleibt und der Fingerabdruck greift.
5. internal — die harte Barriere [N]¶
Ein Modulpfad, der ein Segment internal enthält, ist außerhalb des eigenen Packages nicht erreichbar — weder per Import noch per FQN.
internal bleibt als einziger Sichtbarkeitsmechanismus im Pfad erhalten, obwohl §4 alles andere in die Constitution verlegt. Der Grund ist Redundanz mit Absicht: Die Barriere greift unabhängig davon, was eine Constitution sagt, und trägt auch dann, wenn jemand seine falsch schreibt.
6. mod.jdl — der Deklarationsort des Moduls [D]¶
6.1 Rolle¶
dir/mod.jdl repräsentiert das Modul dir.
Ohne Sichtbarkeitsstufen hat die Datei nichts mehr zu re-exportieren. Was bleibt, ist ihre andere Hälfte: Sie ist die Datei, in der etwas für den ganzen Ordner deklariert wird.
Zulässig ist alles, was eine gewöhnliche JDL-Datei enthalten darf, plus Moduldokumentation und modulweite Erzeuger (§6.2).
Nicht in mod.jdl:
| Was | Wohin | Warum |
|---|---|---|
| Freigaben | constitution.jdl |
Autorität gehört nicht in eine Deklarationsdatei |
| Build-Konfiguration | Manifest | sonst ist Build-Logik über den Baum verstreut |
| Kurznamen ohne Import | prelude.jdl |
anderer Lebenszyklus, keine Validierung |
Jede der vier Fragen hat damit genau einen Ort.
6.2 Modulweite Erzeuger [D]¶
Ein Erzeuger steuert für die Typen unterhalb seines Moduls etwas bei — Methoden, Protokollbindungen, Meta, und unter der Bedingung aus §6.2.6 auch neue Typen. Er wird in mod.jdl deklariert und läuft, wenn das Modul übersetzt wird.
Das ist kein neuer Mechanismus. Ein Erzeuger ist eine gewöhnliche Deklaration am richtigen Ort; seine Materialisierung läuft wie jede andere.
// spekulativ
type ModuleDerive : blueprint {
selector: Selector // dieselbe Form wie in der Constitution
predicate: Predicate // dieselbe Form
apply: MetaFnRef // qualifizierte Funktionsreferenz
}
6.2.1 Die Grenze ist der eigene Baum¶
Ein Erzeuger wirkt auf Typen unterhalb seines Moduls und auf keine anderen.
Damit erledigt sich die Kohärenzfrage, bevor sie entsteht: Der Autor besitzt jeden Typ, den seine Regel trifft, und die Orphan-Regel (§15) ist erfüllt, ohne dass jemand darüber nachdenken muss.
Innerhalb des eigenen Namensraums ja, darüber hinaus nie. Das ist die ganze Regel.
6.2.2 Das Interesse wird deklariert, nicht im Rumpf entschieden¶
selector und predicate sind dieselben Formen wie in spec-constitution-system §3 und §4. Es ist derselbe Satz an beiden Stellen — „welche Subjekte meine ich?" —, einmal um zu urteilen und einmal um beizutragen.
Der praktische Grund ist die Invalidierung (§6.2.7). Nur ein deklariertes Interesse lässt sich als Abhängigkeitskante fassen.
apply ist eine qualifizierte Funktionsreferenz, kein Rumpf. Blueprints enthalten keine gefangenen Closures; Handler-Slots laufen über Referenzen (12 §8.1).
6.2.3 Der Erzeuger arbeitet gegen einen Schnappschuss¶
Die Engine löst Selektor und Prädikat gegen die deklarierten Typen des Moduls auf, bevor irgendein apply läuft. Was Erzeuger selbst produzieren, ist in diesem Schnappschuss nicht enthalten.
Damit ist die Schleife im DepGraph ausgeschlossen, ohne dass es eine Zyklusprüfung braucht. Und zwei Erzeuger im selben Modul sehen beide dasselbe, unabhängig davon, wer zuerst läuft.
6.2.4 Die Reihenfolge ist der FQN¶
apply wird pro Typ aufgerufen, sortiert nach vollqualifiziertem Namen — nicht nach TypeId, die ein Arena-Index ist und die Anlagereihenfolge trägt (§3.3).
Hängt die Ausgabe an der Eingabereihenfolge, ist sie nicht reproduzierbar und der Cache verfehlt bei jedem Umbau.
6.2.5 Der Ausstieg gehört dem Typ¶
Ein Typ, der sich heraushalten will, sagt das an sich selbst — nicht der Erzeuger in seinem Prädikat. Die Derive-Notation kennt die Form bereits: !protocol in der Liste.
Die Engine respektiert das, ohne dass ein Erzeuger danach fragen muss. Sonst müsste jeder daran denken, und einer vergisst es.
6.2.6 Erzeugte Typen brauchen einen ableitbaren Namen¶
apply darf neue benannte Typen erzeugen. Ein Schema in Typen zu übersetzen ist einer der Hauptgründe für den ganzen Mechanismus.
Bedingung. Der Name eines erzeugten Typs muss aus dem Ausgangstyp und dem Erzeuger ableitbar sein.
Nicht „irgendein Typ entsteht", sondern etwa: für User entsteht User.Row. Vorhersagbar, ohne dass man die Erzeugung nachvollzieht.
Ohne diese Bedingung bricht drei Dinge auf einmal: Die Freigabe könnte einen Typ nicht erfassen, der erst nach der Auflösung existiert. Der FQN als stabiler Schlüssel (§3.3) hätte für ihn keinen Wert. Und wer die Datei liest, könnte den Typ nicht benennen.
Ein Modul hat damit nach der Materialisierung mehr Typen, als im Quelltext stehen. Das ist eine bewusste Zusage und keine Nebenwirkung.
6.2.7 Was das bei der Invalidierung kostet¶
Die Abhängigkeitskante lautet nicht „alle Typen in Modul X", sondern „Typen in Modul X, die diesen Selektor erfüllen". Ein neuer Typ, der nicht passt, lässt den Erzeuger kalt.
Das geht nur, weil das Interesse deklariert ist (§6.2.2). Bei einem Erzeuger, der im Rumpf entscheidet, müsste jede Änderung im Modul ihn neu laufen lassen — und bei einem Sprachserver wäre das die teuerste Kante im ganzen Graphen, weil Early Cutoff nicht hilft: Das Ergebnis ändert sich ja tatsächlich.
Teil III — Erreichbarkeit¶
7. Builtins — Typen ohne Namespace [N]¶
bool
i8 i16 i32 i64
u8 u16 u32 u64
isize usize
f32 f64
str
() Einheitstyp, ein Bewohner
Never Bottom-Typ, kein Bewohner
- Ein Builtin wird über seinen nackten Namen angesprochen, überall, ohne Import.
- Es gibt keinen Pfad.
jdl::core::i32existiert nicht und ist ein Fehler. - Builtins sind kein Teil des Preludes. Ein Prelude importiert; ein Builtin braucht keinen Import.
isize und usize sind eigenständige nominale Typen und keine Aliase von i64/u64, auch nicht auf Zielen gleicher Breite [N].
Die Deskriptoren der Builtins entstehen im Bootstrap Seed. Was in ihnen steht, regelt die Primitives-Spec, nicht diese.
8. Imports¶
8.1 Formen [N]¶
import users::service // Modul als Name im Scope
import users::service as user_service // mit Alias
import users::service : User, findUser // gezielte Symbole
import users::service : User as AppUser
import users::service::UserService // Sugar für : UserService
Es gibt keinen Wildcard-Import. Explizit ist Pflicht.
8.2 Relative Pfade [N]¶
parent::— der Parent-Namespace des aktuellen Moduls. In einem Top-Level-Modul ein Fehler.package::— das Root-Modul des aktuellen Packages.
// users/http/router.jdl
import parent::middleware : Middleware
import package::model : User, UserId
Weitere relative Segmente gibt es nicht [D]. Zwei reichen für die Fälle, die auftreten, und jedes weitere macht die Auflösung schwerer nachvollziehbar.
8.3 Lokale Imports [N]¶
Imports dürfen in Funktionsrümpfen stehen.
def parseConfig(path: str) ->{Fs, Throw[ParseError]} Config {
import jdl::formats::toml : parse
parse(path)
}
Der Sinn ist Sichtbarkeit: Die Abhängigkeit steht genau dort, wo sie besteht. Deshalb ist der lokale Import auch der vorgesehene Weg für den CompilerDB-Zugriff in Meta-Funktionen.
8.4 Services werden nicht importiert [D]¶
Ein Service ist eine Bedingung an den Aufrufkontext, kein Symbol, das man in den Rumpf holt. Er steht in der Effect Row zwischen Parameterliste und Rückgabetyp.
def findUser(email: str) ->{UserDb, Throw[UserError]} User {
import myapp::services : UserRepo // COMPILERFEHLER
...
}
Damit gilt:
- Es gibt keinen Effect-Typkonstruktor.
Effect[R, E, D]existiert nicht. - Es gibt keinen zweiten Stil, der Abhängigkeiten als Parameter durchreicht.
- Eine fehlende Row bedeutet die leere Menge.
def f(a: i32) -> i32ist exaktdef f(a: i32) ->{} i32.
Der Grund für das Verbot ist derselbe wie der Grund für §8.3, nur mit umgekehrtem Vorzeichen: Ein lokaler Import zeigt eine Abhängigkeit an der Verwendungsstelle. Bei einem Service ist das die falsche Stelle, weil der Aufrufer sie erfüllen muss und nicht der Rumpf.
9. Fully Qualified Names [N]¶
Ein Symbol darf ohne Import über seinen vollen Pfad verwendet werden:
- Der Compiler erfasst FQN-Referenzen als Abhängigkeiten im Modulgraphen, wie Importe.
- Die Zugriffskette (§12) gilt unverändert. Ein FQN öffnet keinen Namespace, den ein Import nicht öffnen dürfte.
- Builtins haben keinen FQN und können so nicht referenziert werden.
10. Prelude¶
10.1 Der implizite Prelude [D]¶
Der Compiler bringt eine kleine, feste Menge in Scope:
OptionundResultsind kanonisch injade::coredefiniert.- Sie stammen aus dem Bootstrap Seed, nicht aus dem Stdlib-Plugin.
- Der Prelude bringt die Typen samt ihren Varianten unqualifiziert in Scope.
Warum aus dem Seed: Der Plugin Loader hat selbst fehlbare Operationen und braucht Result, um sie auszudrücken. Käme Result mit dem Stdlib-Plugin, bräuchte der Loader das Plugin, um das Plugin zu laden.
Warum die Varianten mit: Anwendercode darf jade:: nicht erreichen (§12). Ohne die Varianten im Prelude wäre Ok(x) nicht schreibbar und es gäbe keinen Ausweg über einen Import. Der Prelude ist hier nicht Bequemlichkeit, sondern der einzige Zugriffsweg.
Kein Opt-out [D]. Ein projektweiter Verzicht auf den Prelude würde Result und Option unerreichbar machen, nicht bloß unbequem.
Zur Rolle von Result: Es ist kein Rückgabetyp-Mechanismus. Der Fehlerkanal einer Funktion steht als Throw[E] in der Row; Result[T, E] ist das, was ein catch daraus materialisiert. Der Typ wird an der Aufrufstelle gebraucht, nicht in der Signatur.
10.2 Prelude-Dateien [D]¶
Ein Modul-Root darf eine prelude.jdl führen. Sie gilt für alle Module darunter.
- Ihr Inhalt sind ausschließlich Importe. Sie deklariert nichts.
- Sie wirkt nach innen und verändert die öffentliche Oberfläche nicht.
Die Berechtigungsgrenze: Ein Prelude darf Namen verkürzen, aber keine Berechtigung transportieren. Was Zugriff auf eine tiefere Ringschicht eröffnet — insbesondere jade::intrinsics — wird pro Datei importiert.
Sonst steht die einzige Stelle, an der sichtbar wird, dass eine Datei die Maschine anfasst, in einer anderen Datei.
Die Regel gilt in allen Namespaces gleich, auch in jade::.
Ein Prelude ist nicht der Ort für Regeln. Weder für Freigaben noch für Verbote. Constitutions gehören in constitution.jdl (§16).
Teil IV — Auflösung¶
11. Konflikte und Vorrang¶
11.1 Kollision zwischen Importen [N]¶
Zwei Importe, die dasselbe Symbol unqualifiziert in Scope bringen, sind ein Compilerfehler.
import jdl::collections : Map
import mylib::types : Map // FEHLER
import mylib::types : Map as MyMap // ok
11.2 Vorrang gegenüber dem Prelude [D]¶
| Fall | Verhalten |
|---|---|
| Lokale Definition gegen Prelude-Symbol | Definition gewinnt, kein Fehler |
| Expliziter Import gegen Prelude-Symbol | Import gewinnt, kein Fehler |
Impliziter Prelude gegen prelude.jdl |
Fehler |
Die Asymmetrie ist gewollt. Ein Autor, der ausdrücklich ein eigenes Result will, soll es benutzen können. Zwei Preludes, die sich still überschreiben, wären dagegen genau die Sorte unsichtbarer Vorrang, die man beim Lesen einer Datei nicht rekonstruieren kann.
11.3 Kollision zwischen Seed und Plugin [D]¶
jade::core ist beim Laden des Stdlib-Plugins nicht leer — der Seed hat ihn bereits befüllt.
Das Stdlib-Plugin darf jade::core nicht befüllen. Der Namespace ist für Seed-Inhalt reserviert.
Die Alternative — Befüllen erlaubt, Kollision ist Ladefehler — wurde verworfen, weil sie eine Ladereihenfolge zur Semantik macht.
Teil V — Ringe¶
12. Reservierte Namespaces und Zugriffskette¶
12.1 Die Namespaces [N]¶
| Namespace | Rolle |
|---|---|
jdl:: |
user-facing Root der Stdlib |
jade:: |
reservierte privilegierte Systemmodule |
jade::core |
Seed-Inhalt, siehe §11.3 |
jade::intrinsics |
direkte Naht zur VM |
jade:: ist keine dünne Naht. Die Erwartung ist eine wachsende Schicht.
std:: existiert nicht [D]. Ältere Beispiele mit std::crypto, std::collections oder std::typefn sind auf jdl:: zu normalisieren; std::typefn entfällt ersatzlos, weil typefn als Konstrukt abgelöst ist.
12.2 Die Kette [N]¶
- Anwendercode darf
jade::weder importieren noch per FQN referenzieren. Das gilt für alle Zweige darunter,jade::intrinsicseingeschlossen. jdl::darfjade::samt allen Zweigen erreichen.- Innerhalb von
jade::gibt es keine weitere Grenze[D].jade::intrinsicsist ein Zweig und kein eigener Namensraum; die Constitution überjade::vererbt sich dorthin, und ein Namensraum braucht keine Erlaubnis, in sich selbst zu greifen. - Die einzige Ausnahme für Anwendercode ist der implizite Prelude (§10.1).
Wer eine Fähigkeit aus jade:: im Anwenderraum braucht, bekommt eine Fassade in jdl:: — keine Ausnahme in der Kette.
Eine Fassade ist ein Alias plus eine Freigabe [D]. Der Alias ist durchsichtig, der Typ bleibt derselbe und ist überall austauschbar; die Freigabe in jdl::s Constitution macht den Aliasnamen erreichbar. Der kanonische Name bleibt dabei der aus jade:: und scheint in Diagnosen durch — das ist der ehrliche Preis dafür, dass es derselbe Typ ist. Wer die Herkunft verbergen will, nimmt einen Newtype und übernimmt die Umwandlung an der Grenze. Siehe spec-constitution-system §6.4.
13. @compiler entfällt [D]¶
Es gibt keinen Namensraum @compiler. Das @-Zeichen markiert ausschließlich Annotationen — @cfg, @assert, @intrinsic, @deprecated.
Die frühere Fassung sah @compiler als compiler-provided Namensraum vor, mit Build- und Zielfakten, Layout-Queries und Low-Level-Operationen. Alle drei haben ein anderes Zuhause, und der Namensraum hat damit keinen Nutzer mehr.
13.1 Warum er keinen haben kann¶
@cfg wird nach dem Parsen und vor der Namensauflösung ausgewertet. Ein Namensraum setzt Namensauflösung voraus. @cfg kann @compiler::target::os also nie benutzen — deshalb hat die Predicate-DSL ihre eigene Schreibweise.
Wer die Fakten danach braucht, ist in der Meta-Phase und hat die db-Fassade. Zwischen beiden liegt niemand.
13.2 Wo die drei Aufgaben liegen¶
| Aufgabe | Zugang | Phase |
|---|---|---|
| Build- und Zielfakten für Pruning | Predicate-DSL: target.os, build.mode, feature("x") |
vor der Namensauflösung |
| Build- und Zielfakten in Meta-Code | db.target() |
Meta-Phase |
| Layout und Typfakten | Meta-Schicht über die CompilerDB | Meta-Phase |
Low-Level-Operationen (trap, Atomics, Bitcast) |
jade::intrinsics |
Laufzeit |
Layout-Queries kommen ausdrücklich nicht aus einer Compiler-Fassade. Sie sind Aussagen über Typen, und Typwahrheit kommt aus der CompilerDB — ein zweiter Weg hätte keinen Proof Trace.
Die Trennlinie bleibt dieselbe wie zuvor, nur ohne eigenen Namensraum: Die Predicate-DSL und db.target() wissen, wofür gebaut wird. Die Meta-Schicht weiß, was gebaut wird.
13.3 @cfg verändert die Oberfläche [D]¶
@cfg schneidet AST-Zweige weg, bevor irgendein Descriptor entsteht. Weggeschnittener Code existiert für Auflösung, Typprüfung und IR-Bau nicht.
Daraus folgt für den Fingerabdruck der freigegebenen Oberfläche (§4): Er gilt pro Build-Konfiguration. Zwei Builds mit verschiedenen Features haben verschiedene Oberflächen, und ein Vergleich über Konfigurationsgrenzen hinweg sagt nichts aus.
Ein Symbol hinter @cfg(feature("x")) ist in einem Build freigegeben und im anderen nicht vorhanden. Das ist kein Widerspruch, sondern zwei verschiedene Artefakte.
14. Entstehungsreihenfolge [D]¶
1 Builtins kein Namespace aus dem Seed
2 jade::core Option, Result, u.a. aus dem Seed
3 jade::* privilegierter Teil Stdlib-Plugin
4 jdl::* user-facing Teil Stdlib-Plugin
5 Anwendercode
- Ein Symbol darf nur auf Symbole einer Stufe ≤ seiner eigenen verweisen.
Stdlib.loadvor abgeschlossenem Seeding ist ein harter Fehler.
Teil VI — Anschlussstellen¶
15. Coherence und die Orphan-Regel¶
15.1 Die Regel [N]¶
Ein provide P for T ist nur zulässig, wenn P oder T im selben Package oder Modul definiert ist. Für fremde Typen nimmt man Newtypes.
15.2 Die Core-Distribution ist ein Package [D]¶
Seed, jade:: und jdl:: bilden für die Coherence-Regel ein Package.
Ohne diese Festlegung wäre provide Result[T, E] { def orThrow(...) } unzulässig: Result liegt in jade::core und stammt aus dem Seed, der Provide läge in jdl::. Dasselbe träfe jedes Equatable, jedes Hashable, jedes Protokoll der Stdlib für einen Seed-Typ.
Die Festlegung ist keine Ausnahme, sondern die zutreffende Beschreibung: Die drei werden zusammen gebaut, zusammen versioniert und zusammen ausgeliefert. Der Zweck der Orphan-Regel — zu verhindern, dass zwei unabhängige Lieferanten widersprüchliche Implementierungen für denselben Typ mitbringen — ist hier nicht berührt, weil es nur einen Lieferanten gibt.
Für Anwendercode ändert sich nichts. Ein Anwenderpackage ist ein anderes Package, und die Regel greift wie zuvor.
16. Constitution — Verankerung¶
Die Regelsprache gehört nicht in diese Spec. Ihre Verankerung im Modulsystem schon:
- Deklariert in
constitution.jdlim Modul-Root oder übermod.jdlbenannt. Nicht inprelude.jdl. - Geltungsbereich ist ein Namespace samt allem darunter.
- Sie ist ein
blueprintund wird von einer Engine materialisiert. Ein TypeKind: constitutionexistiert nicht. - Constitutions sind monoton: Jede Ebene darf verschärfen, keine darf lockern.
Sie ist außerdem der Ort, an dem Freigaben stehen (§4). Das ist ihre einzige gewährende Rolle; alle übrigen Regeln schränken ein.
Import- und Exportkanten kann sie regeln, ohne dass die CompilerDB dafür erweitert werden muss: Der Trigger bringt die Kante als Subjekt mit, samt beiden Enden und den importierten Symbolen. Siehe spec-constitution-system §2.3.
Die Abgrenzung: Sichtbarkeit sagt, wer etwas sieht. Die Constitution sagt, was in einem Bereich gilt, ohne dass jeder Autor darin es hinschreiben muss. Sobald eine Regel nur einen einzelnen Typ meint, gehört sie an den Typ — nicht in die Constitution.
Anhang A — Entscheidungsregister¶
| Nr. | Entscheidung | Ersetzt |
|---|---|---|
| D-1 | Option und Result liegen in jade::core und stammen aus dem Seed |
„kanonisch in jdl::core" |
| D-2 | Der Prelude bringt Some, None, Ok, Err unqualifiziert mit |
war offen |
| D-3 | Kein Opt-out vom Prelude | war offen |
| D-4 | Das Stdlib-Plugin darf jade::core nicht befüllen |
war ungeregelt |
| D-5 | Lokale Definition und expliziter Import verdecken Prelude-Symbole; zwei Preludes kollidieren | war ungeregelt |
| D-6 | Seed, jade:: und jdl:: sind für Coherence ein Package |
war ungeregelt |
| D-7 | Layout-Queries kommen aus der Meta-Schicht | „Layout/Type Queries (später)" |
| D-8 | Die Zugriffskette gilt für FQN wie für Import | war nur für Import formuliert |
| D-9 | Services stehen in der Effect Row; Effect[R,E,D] und der Parameterstil existieren nicht |
„explizite Parameter oder Effect[R,E,D]" |
| D-10 | Der FQN ist die stabile Identität für persistierte Schlüssel | war ungeregelt |
| D-11 | Keine weiteren relativen Segmente | war offen |
| D-12 | std:: existiert nicht |
Altbestand in Beispielen |
| D-13 | Keine Sichtbarkeits-Keywords; alles unsichtbar, Freigabe über die Constitution | pub, package |
| D-14 | mod.jdl ist Deklarationsort, nicht API-Shaping |
Re-Export-Semantik |
| D-15 | internal bleibt als harte Barriere im Pfad |
war neben drei anderen Mechanismen |
| D-16 | Eine Fassade ist ein Alias plus eine Freigabe | war offen (B-3) |
| D-17 | Der Compiler meldet Änderungen am Fingerabdruck der Oberfläche | war offen (B-4) |
| D-18 | @compiler entfällt als Namensraum; @ markiert nur Annotationen |
@compiler::* |
| D-19 | Der Fingerabdruck gilt pro Build-Konfiguration | war ungeregelt |
| D-20 | Modulweite Erzeuger in mod.jdl, begrenzt auf den eigenen Baum |
war ungeregelt |
| D-21 | Erzeuger deklarieren ihr Interesse über Selektor und Prädikat der Constitution | war ungeregelt |
| D-22 | Erzeugte Typen brauchen einen ableitbaren Namen | war ungeregelt |
| D-20 | Modulweite Erzeuger in mod.jdl, Reichweite auf den eigenen Baum begrenzt |
war ungeregelt |
| D-21 | Interesse wird über Selektor und Prädikat deklariert, Schnappschuss, FQN-Reihenfolge, Ausstieg am Typ | war ungeregelt |
| D-22 | Erzeugte Typen tragen ableitbare Namen | war ungeregelt |
Anhang B — Was offen bleibt¶
B-2 [O] Warnung bei systematischem Verdecken von Prelude-Varianten. None und Ok sind kurze, allgemeine Namen. D-5 regelt den Vorrang, aber nicht, ob eine Diagnose angebracht ist, wenn ein Projekt sie durchgehend überschreibt.