Zum Inhalt

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.

users::internal::validator     innerhalb des Packages erreichbar
                               außerhalb: Compilerfehler

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::i32 existiert 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) -> i32 ist exakt def 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:

val u = users::service::findUser("a@b.c")
  • 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:

Option    Some    None
Result    Ok      Err
  • Option und Result sind kanonisch in jade::core definiert.
  • 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::intrinsics eingeschlossen.
  • jdl:: darf jade:: samt allen Zweigen erreichen.
  • Innerhalb von jade:: gibt es keine weitere Grenze [D]. jade::intrinsics ist ein Zweig und kein eigener Namensraum; die Constitution über jade:: 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.load vor 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.jdl im Modul-Root oder über mod.jdl benannt. Nicht in prelude.jdl.
  • Geltungsbereich ist ein Namespace samt allem darunter.
  • Sie ist ein blueprint und wird von einer Engine materialisiert. Ein TypeKind : constitution existiert 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.