JDL/Jade — Runtime-TypeDescriptor, reified, dyn und Function Unions¶
Status: Konsolidierungsnotiz / Architekturentwurf
Zweck: Sammlung und Präzisierung der Erkenntnisse aus der Diskussion zu reifizierten Typinformationen, dyn-Werten, Function Unions, TypeFns, Blueprints, Contexts, Engines, ContextBridge, Syntax-Erweiterungen und runtime-konsumierbaren Descriptoren.
Normativität: Nicht normativ. Diese Notiz ist Vorarbeit für spätere Anpassungen an TypeFn-, Dispatch-, CompilerDB-, Blueprint-, Runtime-, API-Contract- und Web-/Wire-Specs.
Leitsatz: Runtime-Dynamik in Jade entsteht nicht durch eine laufende TypeEngine, sondern durch zur Compile-Time geprüfte und in Artefakte exportierte Descriptor-Projektionen. Contexts begrenzen Ausführung; Engines betreiben dynamische Abläufe innerhalb dieser Grenzen.
1. Kernthese¶
JDL unterscheidet drei Ebenen:
Compile-Time-Typwissen
vollständig, beweisend, erklärend
TypeDescriptor, MetaRecord, TruthProfile, ProofTrace, Source-Spans, Constitution-Kontext
Runtime-Descriptor-Projektion
kompakt, freigegeben, ausführungsnah
RuntimeTypeDescriptor, RuntimeTypeInfo, CodecDescriptor, DispatchDescriptor, RouteDescriptor
Runtime-Wert
VM-Wert, Handle, DynValue, ActorMessage, Request, Response
Ein Typ wird nicht dadurch dynamisch, dass er reified ist. Ein reified Typ ist weiterhin statisch bekannt. Der Unterschied ist: Ein Teil seines zur Compile-Time materialisierten TypeDescriptors wird als runtime-lesbare Projektion ins Artefakt übernommen.
Ein dyn P-Wert ist dagegen ein existentieller Wert: Der konkrete Typ ist an der statischen Aufrufstelle verborgen, aber die Grenze ist durch ein Protocol P beschränkt. dyn P ist keine allgemeine Any-Form.
Function Unions bilden eine dritte Kategorie: eine geschlossene Menge typisierter Operationen, deren konkrete Variante zur Runtime gewählt wird, deren Gesamtform aber zur Compile-Time bekannt und prüfbar ist.
2. Begriffe¶
2.1 TypeDescriptor¶
Ein TypeDescriptor ist das vollständige, zur Compile-Time materialisierte Typartefakt. Er enthält oder referenziert unter anderem:
- Typidentität,
- TypeKind,
- generische Bindungen,
- MetaRecord,
- Ring-0-Facts,
- TruthProfile / LabelProfile,
- LayoutDescriptor,
- ProvideDescriptoren,
- DispatchDescriptoren,
- ProofTrace,
- Diagnostics- und Source-Kontext.
Der vollständige TypeDescriptor gehört primär der Compile-Time-Welt. Er ist Material für TypeEngine, Materializer, IRBuilder, Verifier, Generator und Tooling.
2.2 RuntimeTypeDescriptor / RuntimeTypeInfo¶
Ein RuntimeTypeDescriptor ist eine kontrollierte Projektion eines TypeDescriptors für die Runtime.
Er enthält nur, was zur Runtime benötigt und freigegeben wurde, zum Beispiel:
RuntimeTypeDescriptor {
typeId
qualifiedName? // optional, stripbar
kind // Struct | Enum | Union | Primitive | Function | Dyn | Blueprint
fieldTable? // nur bei Schema/Reflection/Serde-Bedarf
variantTable? // nur bei Enum/Union-Serde/Inspection
layoutRef? // LayoutDescriptor-Referenz, wenn nötig
dropRef? // DropDescriptor-Referenz, wenn nötig
dispatchRefs? // für dyn/Protocol-Dispatch
codecRefs? // für Wire/Serde
flags // kompakte Runtime-Policies
}
Er enthält normalerweise nicht:
- TypeFn-Bodies,
- vollständige MetaRecords,
- ProofTraces,
- Constitution-Regeln,
- AST,
- Source-Spans,
- CompilerDB-Queries,
- Provider-Interna.
Ausnahme: Debug-/REPL-/Tooling-Bundles können zusätzliche Informationen laden. Das ist kein Normalfall der Production-Runtime.
2.3 Reified Type¶
Ein reified Typ ist ein statisch bekannter Typ, dessen runtime-relevante TypeDescriptor-Projektion absichtlich ins Artefakt exportiert wird.
reified ist ein Bindungsmodus, kein normales Refinement. Eine TypeFn darf den Reification-Modus lesen und voraussetzen, aber nicht nachträglich an bestehenden Bindungen verändern.
2.4 Dyn Protocol Value¶
Ein dyn P-Wert ist ein existentialer Wert mit expliziter Protocol-Grenze.
Er bedeutet:
Der konkrete Typ ist an dieser Stelle statisch verborgen.
Bekannt ist nur: Der Wert erfüllt Protocol P.
Die Runtime-Repräsentation braucht daher eine Typidentität und Dispatch-Information.
Konzeptionell:
DynValue[P] {
payload: JadeValue | Handle
typeId: RuntimeTypeId
dispatchRef: DispatchDescriptorRef[P]
}
dyn P ist kein Any. Es trägt eine Grenze, keine Kapitulation.
2.5 Function Union¶
Eine Function Union ist eine geschlossene Union typisierter Operationen.
Korrekte Syntax:
type UserCommand : union =
| getUser(id: UserId) -> Result[User, ApiError]
| createUser(req: CreateUserRequest) -> Result[User, ApiError]
| deleteUser(id: UserId) -> Result[(), ApiError]
Sie ist „statisch-dynamisch“:
- statisch, weil die Menge der Varianten und ihre Signaturen bekannt sind,
- dynamisch, weil zur Runtime eine konkrete Variante ausgewählt und verarbeitet wird.
Function Unions eignen sich als Operations-Interface für Actors, RPC, Routing, Message-Busse und transport-agnostische API-Contracts.
3. Architekturfluss¶
Der Grundfluss lautet:
Blueprint / TypeSubject / Function Union / Contract
↓
TypeEngine / TypeFnEvaluator / Provider
↓
MaterializationPatch
↓
CompilerDB Materializer
↓
Descriptoren
↓
Generator / ArtifactBuilder
↓
Runtime-Descriptor-Projektionen im Artefakt
↓
RuntimeBus / JME / RoutingEngine / DispatchEngine / CodecEngine konsumieren Tabellen
Die Runtime bekommt keine TypeEngine. Sie bekommt vorbereitete Verträge.
Das ist der zentrale Sicherheits- und Ressourcenpunkt: Der teure und beweisende Teil geschieht zur Compile-Time. Die Runtime liest kompakte Tabellen und führt definierte Commands aus.
4. reified T¶
4.1 Bedeutung¶
reified T bedeutet:
Der konkrete Typ T ist statisch bekannt.
Ein runtime-lesbarer Descriptor-Anteil von T muss im Artefakt verfügbar sein.
Nicht gemeint ist:
Der Typ T wird zur Runtime neu erzeugt.
T wird dynamisch.
Die Runtime bekommt den vollständigen Compile-Time-TypeDescriptor.
4.2 TypeFn-Verhalten¶
Eine TypeFn darf:
- prüfen, ob ein Typparameter
reifiedgebunden ist, - daraus Descriptor-Patches erzeugen,
- RuntimeTypeInfo-Anforderungen anmelden,
- Diagnostics erzeugen, wenn Reification fehlt.
Eine TypeFn darf nicht:
- einen bestehenden Typparameter nachträglich
reifiedmachen, - die Calling Convention rückwirkend ändern,
- Runtime-TypeInfo ohne explizite Reification erzwingen.
Zulässig:
Nicht zulässig:
4.3 Runtime-Kosten¶
reified ist nicht automatisch teuer. Der Preis ist abhängig von der exportierten Projektion.
Stufenmodell:
Minimal:
typeId, kind, optional name
Schema:
fieldTable, variantTable, field types
Layout:
size, align, offsets, layoutRef
Dispatch:
protocol bindings, dispatchRefs
Debug:
lesbare Namen, Source-Spans, pretty inspection
Proof:
normalerweise nicht Runtime
Production-Builds sollten Projektionen strippen können. Debug- und REPL-Builds können mehr tragen.
5. dyn P¶
5.1 Bedeutung¶
dyn P ist ein dynamischer Protocol-Wert. Der konkrete Typ ist verborgen, aber die operationale Grenze ist explizit.
Das ist keine offene Universalform. Es gibt kein Any.
5.2 Abgrenzung zu reified¶
| Mechanismus | Bedeutung |
|---|---|
reified T |
T ist bekannt; Typinfo von T soll runtime-sichtbar sein |
dyn P |
konkreter Typ ist verborgen; Protocol-Grenze P ist bekannt |
dyn P -> T |
Runtime-Pipeline materialisiert einen konkreten Zieltyp |
| Function Union | geschlossene Menge typisierter Operationen |
5.3 Dyn als Boundary-Form¶
dyn ist besonders sinnvoll an Grenzen:
- Plugin-Grenzen,
- REPL/Inspection,
- Routing,
- heterogene Collections,
- Message-Bus,
- Service-Registries,
- WebSocket/RPC,
- Hostobjekte.
Nicht sinnvoll ist dyn als Ersatz für Generics oder normale Protocol-Constraints.
5.4 Dyn-Pipeline in konkreten Typ¶
Der bevorzugte Umgang mit dyn ist:
Beispielhaft:
dyn Message
→ typeId/schema route
→ decoder/validator
→ UserCreated | PaymentFailed | HttpRequest[CreateUser]
→ konkreter Handler
Die Bindings können statisch vorbereitet werden:
DynDispatchDescriptor
TypeId → ConcreteBinding
ProtocolId + TypeId → Handler
SchemaId → Decoder
RouteId → HandlerProto
Damit bleibt dyn kontrollierte Dynamik, kein architektonischer Nebel.
6. Function Unions als statisch-dynamische Interfaces¶
Function Unions sind ein tragendes Konzept, weil sie Operationen als Typen ausdrücken.
Sie sind kein OO-Interface und kein dyn-Wert. Sie sind eine geschlossene Operationssumme.
6.1 Beispiel¶
type UserCommand : union =
| getUser(id: UserId) -> Result[User, ApiError]
| createUser(req: CreateUserRequest) -> Result[User, ApiError]
| listUsers(filter: UserFilter) -> Result[[User], ApiError]
| deleteUser(id: UserId) -> Result[(), ApiError]
6.2 Actor-Verwendung¶
Die Actor-Engine kann daraus einen ActorMessageDescriptor ableiten.
Ein match über UserCommand ist exhaustiv prüfbar. Jeder Arm hat seinen eigenen Rückgabetyp. Damit wird dynamische Operationauswahl möglich, ohne Any oder offene Reflection zu verwenden.
6.3 Generische Function Unions¶
Function Unions können generisch sein:
type RepositoryCommand[T, E] : union =
| get(id: Id) -> Result[T, E]
| list() -> Result[[T], E]
| delete(id: Id) -> Result[(), E]
Das erlaubt generische API-, Repository-, Actor- und RPC-Modelle, ohne die Operationsmenge zu öffnen.
6.4 Beziehung zu Contracts¶
Ein API-Contract-Blueprint kann eine Function Union ableiten oder enthalten.
Oder struktureller:
HandlerMap[C] ist konzeptionell eine TypeFn/Type-Materialisierung, die aus Contract-Operationen passende Handler-Signaturen ableitet.
Offen bleibt, ob Function Unions manuell geschrieben, aus Contract-Blueprints abgeleitet oder beides unterstützt werden.
7. Blueprints als ideale Steuerform¶
Blueprints sind die natürliche Oberfläche für diese Mechanik.
Beispiele:
RouteBlueprint
ServiceBlueprint
SchemaBlueprint
CodecBlueprint
DynProtocolBlueprint
FfiBindingBlueprint
ActorBlueprint
ContractBlueprint
Blueprints beschreiben Absicht. Engines werten diese Beschreibungen aus. Descriptoren sind das materialisierte Ergebnis.
Für Web/Routing bedeutet das:
RouteBlueprint
+ Handler-Signatur
+ Body[T]
+ Returns[U]
+ Effect-/Service-Information
+ reified TypeDescriptor-Projektionen
↓
RouteDescriptor
CodecDescriptor
SchemaDescriptor
DispatchDescriptor
Runtime-Routing-Tabelle
Zur Runtime liest die RoutingEngine nur:
Sie liest keine CompilerDB und führt keine TypeFns aus.
8. Web-Backend-Anwendung¶
Ein Web-Backend profitiert stark von reified, dyn und Function Unions, aber nur an Grenzen.
8.1 Reified im Web¶
Sinnvoll für:
- Request Body Parsing,
- Response Serialization,
- Schema/OpenAPI-Generierung,
- Config- und Form-Validation,
- Debug/Inspection,
- Plugin-Routes.
Beispiel:
route POST "/users"
:> Body[CreateUserRequest]
:> Returns[UserResponse]
:> Uses[UserDb]
{
createUser
}
Compile-Time:
Body[CreateUserRequest]
Returns[UserResponse]
→ verlangt CodecDescriptor / SchemaDescriptor
→ nutzt RuntimeTypeDescriptor-Projektion der beteiligten Typen
Runtime:
HTTP request
→ RouteDescriptor match
→ bodyCodec.decode(bytes)
→ CreateUserRequest
→ HandlerProto
→ responseCodec.encode(UserResponse)
8.2 Dyn im Web¶
Sinnvoll für:
- WebSocket-Nachrichten,
- polymorphe Event Streams,
- Plugin-Requests,
- Middleware-Objekte mit bewusst verborgener Implementierung,
- Routing über Message-Typen.
Empfohlenes Muster:
Nicht empfohlen:
Das wäre ein Any-Ersatz mit besserer Frisur.
8.3 Function Unions im Web¶
Function Unions eignen sich als transport-agnostische Operationsebene:
type UserApi : union =
| getUser(id: UserId) -> Result[UserResponse, ApiError]
| createUser(req: CreateUserRequest) -> Result[UserResponse, ApiError]
HTTP, gRPC, WebSocket oder IPC können dieselbe Operationsmenge unterschiedlich abbilden.
UserApi
→ HttpRouteDescriptoren
→ GrpcMethodDescriptoren
→ WsMessageDescriptoren
→ ActorCommandDescriptoren
Die Operationen bleiben gleich. Nur die TransportEngine ändert sich.
9. TypeFns, Generics und TypeEngine-Schichtung¶
9.1 Generics vor TypeFn-Evaluation¶
Generische Bindungen müssen vor der TypeFn-Auswertung zumindest gebunden und als TypeSubject sichtbar sein.
Ablauf:
Parse
→ Name Resolution
→ Generic Binding
→ Constraint Environment
→ TypeSubject
→ TypeFn Evaluation
→ PatchSet
→ Materializer
Eine TypeFn soll keine rohe Generic-Syntax auflösen. Sie arbeitet auf bereits gebundenen TypeSubjects.
9.2 TypeFn-Evaluation¶
TypeFns laufen während der Meta-/Type-Resolution, nachdem TypeSubjects und Namen ausreichend aufgelöst sind, aber bevor Layout, IRBuilder und Generator harte Annahmen treffen.
TypeFns erzeugen keine direkten CompilerDB-Mutationen, sondern Patches.
9.3 Tags statt Types für Phantom-State¶
Compile-Time-Zustände in Phantom-Parametern sind Tags, keine Types.
tag SyntaxKnown
tag NamesBound
tag GenericsBound
tag MetaApplied
tag TruthClosed
tag LayoutKnown
tag RuntimeDescribed
Ein State-Blueprint könnte so aussehen:
type TypeMaterialization[phantom S] : blueprint {
// beschreibt Zustand und erlaubte Übergänge der Typmaterialisierung
}
Übergänge:
def bindNames(t: TypeMaterialization[SyntaxKnown])
-> TypeMaterialization[NamesBound]
def bindGenerics(t: TypeMaterialization[NamesBound])
-> TypeMaterialization[GenericsBound]
def applyRefinements(t: TypeMaterialization[GenericsBound])
-> TypeMaterialization[MetaApplied]
Das ist eine typisierte State-Machine der Materialisierung. Die ausführende Engine ist davon zu trennen.
10. Kein Any¶
JDL sollte keinen universellen Any-Typ besitzen.
Any ist ein Escape Hatch, der Architekturfehler verschleiert. Wer Any will, meint meistens eines der folgenden Konzepte:
| Gemeinter Zweck | JDL-Mechanismus |
|---|---|
| mehrere bekannte Varianten | union / enum |
| irgendein Wert mit Verhalten | dyn P |
| bekannter Typ mit Runtime-TypeInfo | reified T |
| transportierte Operation | Function Union |
| fremde Ressource | opaque Handle |
| deklarative Beschreibung | Blueprint |
| runtime-lesbares Schema | RuntimeTypeDescriptor / SchemaDescriptor |
Regel:
Dynamik muss in JDL immer über eine explizite Grenze formuliert werden.
11. Ressourcenprofil¶
reified und dyn sind nicht automatisch teuer.
Kosten entstehen durch:
- exportierte Descriptor-Daten,
- dynamischen Dispatch,
- Runtime-Introspektion,
- Codec-/Schema-Tabellen,
- Debug-/REPL-Daten.
Der Hot Path kann statisch bleiben, wenn dynamische Werte früh in konkrete Typen materialisiert werden.
Empfohlene Strategie:
Boundary:
dyn / reified / descriptors erlaubt
Core Logic:
konkrete Typen, Generics, statische Provides
Debug/REPL:
optionale zusätzliche Descriptor-Projektionen
Production:
stripbare RuntimeTypeInfo-Stufen
12. Gegenargumente und Grenzen¶
12.1 Runtime-Fehler statt Compile-Time-Fehler¶
dyn verschiebt bestimmte Fehler an die Runtime. Das ist akzeptabel an Grenzen, aber schlecht als inneres Programmiermodell.
Lösung: dyn möglichst früh materialisieren.
12.2 Descriptor-Disziplin wird kritisch¶
Wenn RouteDescriptor, CodecDescriptor oder DispatchDescriptor falsch materialisiert sind, wird die Runtime zur Lotterie.
Lösung: Materializer, Verifier und Artifact-Validator müssen Descriptor-Kohärenz hart prüfen.
12.3 Hot-Path-Verführung¶
dyn ist bequem und kann missbraucht werden.
Lösung: Lints, Performance-Warnings und Idiomatik: dyn nur an Boundaries.
12.4 Security-Grenzen¶
dyn über Plugin-, Web- oder Message-Grenzen darf nicht unkontrolliert akzeptiert werden.
Lösung: Herkunft, Schema-Version, Plugin-Trust, allowed Protocols und Capability-Kontext prüfen.
12.5 Reification-Bloat¶
Zu breite RuntimeTypeInfo-Projektionen vergrößern Artefakte.
Lösung: Reification-Profile und stripbare Descriptor-Stufen.
13. Offene Entscheidungen¶
- Syntax für
dyn:dyn P,Dyn[P]oder andere finale Form. - Reification-Profile: Minimal, Schema, Layout, Debug, Full? Welche Namen?
- Function-Union-Variantensyntax: finale Signaturform ohne
defbestätigen. - Ableitung aus Contract-Blueprints: automatisch, per TypeFn, per explizitem
:> DeriveCommands()? - RuntimeTypeDescriptor-Format: welche Felder sind stabil, welche optional?
- Debug-Bundles: getrenntes Artefakt oder Build-Profil im VmModuleArtifact?
- Web-RouteDescriptor: in
jdl::httpoder allgemeiner injdl::api? - Dyn-Pipeline-API: Standard-Protocol für
tryMaterialize[T]? - No-Any-Regel: wo normativ verankern?
- TypeMaterialization-State-Machine: eigene Spec oder TypeFn-/CompilerDB-Erweiterung?
14. Empfohlene spätere Spec-Patches¶
14.1 dispatch-modes.md¶
Ergänzen:
dyn Pist Boundary-Mechanismus, kein Generics-Ersatz.dyn Pist keinAny.dyn → concretePipeline als empfohlenes Muster.- DynValue referenziert RuntimeTypeId und DispatchDescriptor.
14.2 jdl-typefn-spec.md¶
Ergänzen:
- TypeFns dürfen Reification-Modus lesen, nicht mutieren.
- TypeFns können RuntimeDescriptor-Anforderungen als Patches anmelden.
- Generics müssen vor TypeFn-Evaluation gebunden sein.
- Structural TypeFns für HandlerMap / TypedClient / Function Union Derivation als Ausbaustufe.
14.3 jade-compiler-db-spec.md¶
Ergänzen:
- RuntimeDescriptorProjection als Artifact/Descriptor-Klasse.
- Materializer-Regeln für reified/dyn-Projektionen.
- Descriptor-Stripping/Profile im ArtifactDomain.
14.4 12-design-constitution.md¶
Ergänzen:
- Keine Runtime-TypeEngine.
- Runtime konsumiert Descriptor-Projektionen.
Anyals Nicht-Ziel.- Reification/Dyn als kontrollierte Descriptor-Dynamik.
14.5 jdl-api-contract-design-note.md¶
Ergänzen:
- Function Unions als statisch-dynamisches Interface-Modell.
- korrekte Syntax
type X : union = .... - generische Function Unions.
- Ableitung aus Contract-Blueprints.
14.6 Web-/HTTP-Spec¶
Neu oder später:
- RouteBlueprint,
- RouteDescriptor,
- CodecDescriptor,
- SchemaDescriptor,
- reified Body/Return-Typen,
- dyn Message Pipelines,
- transport-agnostische Function Unions.
15. Kurzfassung¶
reified T
= bekannter Typ T, dessen TypeDescriptor-Projektion zur Runtime verfügbar ist
dyn P
= unbekannter konkreter Typ mit bekannter Protocol-Grenze P
Function Union
= geschlossene Menge typisierter Operationen; statisch bekannt, runtime auswählbar
Blueprint
= deklarierte Absicht
Descriptor
= geprüfter, materialisierter Vertrag
Runtime
= konsumiert Tabellen, führt keine TypeEngine aus
Der entscheidende Satz:
Jade erlaubt kontrollierte Dynamik, indem dynamische Runtime-Grenzen zur Compile-Time descriptor-basiert vorbereitet werden.
reified,dynund Function Unions sind keine Escape Hatches, sondern drei unterschiedliche Arten, Typwissen sicher und begrenzt über Phasengrenzen zu tragen.
Teil II - Context, Engines, Bridges und Surface-Syntax¶
Status dieses Teils: Ergaenzung aus der Folgediskussion. Nicht normativ.
Zweck: Die bisherigen Descriptor-/Dyn-/Reification-Erkenntnisse werden um Contexts, Engines, ContextBridge, Syntax-Erweiterungen, Parser-Kombinatoren, Property Testing und Artefakt-Store-Gedanken ergaenzt.
Der zentrale Zusatz lautet:
Context = static env einer Ausfuehrungsdomaene
Engine = dynamic env / Mechanik innerhalb eines Contexts
Bridge = expliziter Gatekeeper zwischen Contexts
Descriptor = gepruefter Vertrag zwischen Semantik und Maschine
Damit wird Runtime-Dynamik nicht global, sondern lokal und gegrenzt. Engines duerfen dynamisch sein. Aber sie sind dynamisch innerhalb eines statisch materialisierten Contexts.
16. Context als static env¶
Ein Context ist keine neue Runtime und kein globaler Service-Locator. Ein Context ist eine descriptor-materialisierte Environment-Grenze.
Er beschreibt, was innerhalb einer Ausfuehrungsdomaene erlaubt, verfuegbar und gebunden ist.
ContextDescriptor
profile
services
capabilities
engines
storage
scheduler
messaging
bridges
reification
limits
parentExpose
Ein Context beantwortet statisch:
Welche Services sind sichtbar?
Welche Capabilities sind erlaubt?
Welche Engines duerfen laufen?
Welche Scheduler-Strategie gilt?
Welche Storage-Policy gilt?
Welche Runtime-TypeDescriptor-Projektionen werden exportiert?
Welche Kommunikationswege sind erlaubt?
Welche Parent-Services werden exposed?
Ein Context fuehrt diese Dinge nicht selbst aus. Die eigentliche Maschinenarbeit bleibt bei Ring 0 / VM / JME / RuntimeBus / Scheduler-Kern. Der Context liefert die gepruefte Verdrahtung.
Der praezise Satz:
Ein Context ist die statisch materialisierte Environment-Grenze einer Ausfuehrungsdomaene. Er verdrahtet seine Inhabitants kontrolliert mit erlaubten Maschinen- und Service-Faehigkeiten, waehrend die tatsaechliche Ausfuehrung von Engines und Ring-0-Domaenen getragen wird.
16.1 Context und Runtime trennen¶
Jade Runtime
VM, JME, RuntimeBus, Scheduler-Kern, Intrinsics, FFI-Bridge
JDL Context
Services, Engines, Policies, Capabilities, Storage-/Scheduler-Konfiguration,
Middleware, Messaging, Reification-Profile, Parent-Exposes
Runtime ist die operative Maschine. Context ist die gepruefte Umgebung, in der Anwendungsmechanik laufen darf.
16.2 Context-Profile¶
Context-Profile sind keine eigenen Sprachprimitive. Sie sind vordefinierte Profile desselben Context-Schemas.
ApplicationContext allgemeiner App-Rahmen
WebContext HTTP, Routing, Middleware, Request-Scopes, Serde
CliContext Args, StdIO, ExitCode, Config, Logging
ReplContext live CompilerDB, Debug, Inspection, Hot-Load
PluginContext isolierte Plugin-Ausfuehrung
FfiContext ABI, Marshal-Descriptoren, Host-Grenze
TestContext deterministischer Scheduler, Property Testing, Seeds
ActorContext Mailbox, Scheduler, Isolation, Message Passing
LibraryContext keine Host-Caps, exportierbare APIs, kein Runtime-Start
EffectContext Service-Bindings, Retry/Timeout/Transaction-Policies
Mehr Context-Profile sollten nur entstehen, wenn eine echte Grenze vorliegt:
- eigene Capability-Grenze
- eigene Scheduler-Strategie
- eigene Storage-/Resource-Policy
- eigene Trust-/Plugin-Grenze
- eigene Runtime-Kommunikationsform
- eigener Debug-/CompilerDB-Zugriff
Kein neuer Context fuer Logging, JSON, Auth, Metrics oder einzelne Middleware. Das sind Services, Engines, Refinements oder Policies. Sonst entsteht Context-Flut. Und Context-Flut ist nur globale Unordnung mit besseren Substantiven.
17. Engines als dynamic env¶
Eine Engine ist Mechanik. Sie validiert, materialisiert, dispatcht, scheduled, serialisiert, transportiert oder interpretiert Descriptoren. Eine Engine besitzt dynamischen Zustand nur in ihrer laufenden Instanz, nicht in ihrer deklarativen Definition.
Der bessere Schnitt:
Blueprint / Refinements / Provides
|
v
Descriptor
|
v
ContextDescriptor
|
v
EngineInstance
|
v
dynamic env: Queues, Tasks, Mailboxes, Requests, Effects, Handles, Scopes
Der Context sagt:
Die Engine sagt:
17.1 Fundamentale Engines¶
Fundamentale Engines sind identitaetsstiftende Ausfuehrungsdimensionen von Jade. Sie sind nicht dumm. Sie definieren mechanische Invarianten, auf denen spezialisierte Engines aufbauen.
SchedulerEngine Tasks, Yield, Join, Cancellation, Fairness, Backpressure
ConcurrencyEngine Task-Grenzen, Share/Send/Sync, Channels, Mailboxes
ResourceEngine with, Cleanup, Drop-Garantien, Scope-Ende
StorageEngine Arenas, Pools, Rc, Allocation Policies
DispatchEngine static/generic/dyn Dispatch, Function Unions, Protocol Dispatch
CodecEngine Wire/Serde, Schema, Reification Profiles
EffectEngine Effect-Werte, Service-Bindings, Handler-Kontext
CapabilityEngine Services, Hostrechte, Runtime-Caps, Plugin-Grenzen
TransportEngine Nachrichtenbewegung zwischen Contexts/Nodes
Spezialisierte Engines komponieren fundamentale Engines:
HttpEngine =
RouteBlueprint
+ SchedulerEngine
+ CodecEngine
+ ResourceEngine
+ CapabilityEngine
+ MiddlewareBlueprints
ActorEngine =
FunctionUnion
+ SchedulerEngine
+ ConcurrencyEngine
+ DispatchEngine
+ MailboxPolicy
RpcEngine =
FunctionUnion
+ CodecEngine
+ TransportEngine
+ DispatchEngine
+ CapabilityEngine
Der wichtige Satz:
Spezialisierte Engines besitzen nicht die ganze Welt selbst. Sie orchestrieren fundamentale Engines innerhalb eines Contexts.
17.2 Engine-Spezialisierung ueber Protocols und Provides¶
Eine Engine wird nicht durch Vererbung erweitert, sondern ueber Protocol-Faehigkeiten und provide-Implementierungen spezialisiert.
Beispielhaft:
protocol RoutableEngine[E] {
def describeRoutes(self: E) -> RouteTableDescriptor
}
protocol MiddlewareEngine[E, M] {
def applyMiddleware(self: E, middleware: M) -> MiddlewareDescriptor
}
provide RoutableEngine[HttpEngine] for HttpEngine {
def describeRoutes(self) -> RouteTableDescriptor = ...
}
provide MiddlewareEngine[HttpEngine, AuthMiddleware] for HttpEngine {
def applyMiddleware(self, middleware: AuthMiddleware) -> MiddlewareDescriptor = ...
}
Provides duerfen eine Engine nur innerhalb erlaubter Protocol-Grenzen erweitern. Sie duerfen keine fremden Descriptoren direkt schreiben, keine Ring-0-Facts aendern und keine Runtime-Capabilities heimlich einschleusen.
18. ContextApi und ContextBridge¶
18.1 ContextApi: Gatekeeper innerhalb eines Contexts¶
Die ContextApi ist die lokale API innerhalb eines Contexts. Engines verwenden sie, um Services, Storage, Scheduler, Messaging oder Ressourcen anzufordern.
Engine -> ContextApi.require(Service)
Engine -> ContextApi.schedule(task)
Engine -> ContextApi.openArena(RequestArena)
Engine -> ContextApi.send(message)
Engine -> ContextApi.resolveCodec(typeId)
Die ContextApi prueft gegen den ContextDescriptor:
Wenn statisch bekannt: Compile-Time-/Load-Time-Fehler. Wenn erst zur Runtime sichtbar: Trap am Context-Gate.
18.2 ContextBridge: Gatekeeper zwischen Contexts¶
Eine ContextBridge ist eine explizit deklarierte, typisierte und policy-gepruefte Kommunikationsoberflaeche zwischen zwei ansonsten isolierten Contexts.
Wichtig:
Ohne Bridge gibt es keinen Kommunikationspfad. Nicht "verboten, aber technisch moeglich", sondern schlicht: nicht adressierbar.
+-------------------+ +-------------------+
| PluginContext | | ApplicationContext |
| | | |
| Engine A | | Engine B |
| | | | ^ |
| v | | | |
| ContextBridge ----+----------+---+ |
+-------------------+ +-------------------+
Eine Bridge exponiert nur die deklarierte Oberflaeche:
allowed messages
allowed queries
allowed commands
codec / wire format
capability requirements
direction
lifetime
backpressure policy
error model
trust policy
Beispielhaft ueber eine Function Union:
type UserPluginBridge : union =
| getUser(id: UserId) -> Result[UserView, UserError]
| emitAudit(event: AuditEvent) -> Result[(), AuditError]
Daraus kann entstehen:
ContextBridgeDescriptor {
from: PluginContext
to: ApplicationContext
interface: UserPluginBridge
allowedQueries: [getUser]
allowedCommands: [emitAudit]
codec: JadeWire
capabilities: [ReadUser, EmitAudit]
policy: LocalOnly
}
Damit bekommt der PluginContext nicht direkt UserDb, nicht den WebContext und nicht den Scheduler des Parent. Er bekommt nur die im BridgeDescriptor erlaubten Operationen.
18.3 Bridge-Formen¶
Die Bridge-Semantik bleibt gleich, auch wenn die Mechanik variiert:
local bridge gleiche VM, billige Message/Call-Grenze
thread bridge Scheduler/Mailbox-Grenze
process bridge Transport/Codec-Grenze
node bridge Jade Node / Netzwerk
mock bridge TestContext / deterministic replay
Die Form ist Mechanik. Die erlaubte Oberflaeche ist Descriptor-Semantik.
19. Context-Hierarchie und Parent-Expose¶
Contexts koennen hierarchisch angelegt werden, aber Kommunikation bleibt explizit.
ApplicationContext
|
+-- WebContext
| |
| +-- RequestContext
|
+-- ActorContext[WorkerPool]
|
+-- PluginContext[ImageCodec]
|
+-- ReplContext
Ein ChildContext sieht nicht automatisch den Parent. Der Parent muss gezielt Services oder Bridges exposen.
ParentContext
exposes: [Logger, Config]
PluginContext
imports: [Logger]
cannot see: Db, Network, SchedulerInternals
Der Context ist damit eine kontrollierte Env. Er gruppiert sinnvoll. Er enthaelt alles, was seine Aufgabe gesichert abarbeiten muss, aber nicht mehr.
20. Storage im Context¶
Storage kann ueber normale Blueprints im Context verdrahtet werden. Es gibt aber kein eigenes Keyword service blueprint oder context blueprint. Es gibt nur blueprint; Rollen ergeben sich aus Struktur, Refinements und Provides.
Wichtig:
Nicht:
Context bekommt die JME.
Sondern:
Context bekommt Storage-Policies, Arena-/Pool-Specs und Limits.
Die JME bleibt Ring-0-Mechanik.
Beispielhaft:
type RequestStorage : blueprint {
requestArena: ArenaSpec
bodyPool: PoolSpec
limits: StorageLimits
}
type WebContext : blueprint {
storage: RequestStorage
scheduler: SchedulerConfig
services: ServiceSet
engines: EngineSet
}
Compile-Time / Load-Time:
RequestStorage
-> StorageDescriptor
-> ArenaDescriptor
-> PoolDescriptor
-> StorageLimitDescriptor
-> ContextDescriptor
Runtime:
Der Satz:
Storage ist im Context als deklarierte Policy verdrahtet; operative Speicherhoheit bleibt bei der JME.
21. Context-Flut vermeiden¶
Context ist stark, solange er selten und begruendet bleibt. Ohne Regeln wird Context zur neuen Gottklasse.
21.1 Ein Schema, viele Profile¶
Es gibt ein gemeinsames ContextDescriptor-Schema. Profile sind vordefinierte Belegungen dieses Schemas.
Nicht:
als inkompatible Welten.
Sondern:
ContextDescriptor(profile = Web)
ContextDescriptor(profile = Actor)
ContextDescriptor(profile = Ffi)
21.2 Keine Feature-Contexts¶
Kein eigener Context fuer:
Das sind Services, Middleware, Engines, Refinements oder Policies.
21.3 Lint-Regeln¶
Spaeter sollte Tooling Context-Missbrauch melden:
CONTEXT-001: Context unterscheidet sich nur durch Service-Binding.
CONTEXT-002: Context hat keine eigene Capability-Grenze.
CONTEXT-003: Context dupliziert Scheduler/Storage-Profil.
CONTEXT-004: Engine-spezifische Policy wurde als Context modelliert.
22. Syntax-Sugar und User-Erweiterungen¶
JDL-Syntax sollte nicht magisch sein. Eingebauter Sugar und User-Sugar sollten dieselbe Mechanik verwenden.
Grundfluss:
Surface Syntax
-> SyntaxDescriptor
-> ParserCombinator
-> SyntaxNode / SugarNode
-> LoweringProvider
-> normales AST / Descriptor / Extended IR
-> Core IR
Eingebauter Sugar:
:= -> val-Binding mit Type Inference
|> -> verschachtelte Calls / Pipeline-IR
=? -> Result-Propagation
with -> Scope-/Resource-Desugaring
User-Sugar:
Der Unterschied ist Autoritaet, nicht Mechanik.
Built-in JDL Sugar
privilegierter, versionierter Standard-Provider
User Syntax Extension
scoped, context-/package-/namespace-gebunden,
Constitution-/Materializer-geprueft
22.1 SyntaxExtensionDescriptor¶
Eine Syntax-Erweiterung sollte materialisiert werden als:
SyntaxExtensionDescriptor {
scope
entryPoint
precedence
associativity
parserCombinator
outputNodeKind
loweringProvider
conflicts
requiredCapabilities
}
Die ParserEngine kann dadurch pruefen:
Ist diese Syntax in diesem Context aktiv?
Kollidiert sie mit bestehender Syntax?
Welche Prioritaet hat sie?
Welche Fehlermeldungen liefert sie?
Welcher Lowerer ist zustaendig?
22.2 Harte Regeln fuer Syntax-Erweiterungen¶
1. Core-Syntax bleibt klein und stabil.
2. Sugar lowered immer zu normaler Semantik.
3. User-Sugar ist scoped, nie global.
4. Bestehende Syntax darf nicht ueberschrieben werden.
5. Lowering darf keine Ring-0-Facts aendern.
6. Parser erzeugt Syntax, aber committed keine Descriptoren.
7. Der Verifier sieht nur gelowerten IR/Core.
Der Satz:
JDL-Syntax ist privilegierter, standardisierter Sugar ueber dieselben Syntax-/Lowering-Schnittstellen, die auch kontrollierte User-Erweiterungen verwenden duerfen.
Das erlaubt lokale DSLs, ohne die VM-Semantik zu veraendern.
23. Constitution-Enforcement als Blueprint-System¶
Eine Constitution ist kein Text, der streng schaut. Sie wird selbst materialisiert.
ConstitutionSpec
-> ConstitutionEngine
-> ConstitutionDescriptor
-> RuleIndex / EnforcementHooks
-> Compilerphasen / Runtime-Gates
Regeln laufen nicht frei als beliebiges Programm, sondern werden in eine endliche Regel-Algebra normalisiert.
Minimalstruktur:
type ConstitutionRule : struct {
trigger: RuleTrigger
selector: RuleSelector
predicate: RulePredicate
action: RuleAction
}
Typische Trigger:
OnNamespaceLoad
OnImport
OnTypeSubject
OnApplyRefinement
OnProvide
OnEffectDescriptor
OnDescriptorCommit
OnIrVerify
OnArtifactEmit
Wichtigster Gate: OnDescriptorCommit. Dort wird verhindert, dass illegale Semantik zur CompilerDB-Wahrheit wird.
Der Satz:
Eine Blueprint-Constitution wird enforced, indem sie in einen ConstitutionDescriptor mit endlicher Regel-Algebra materialisiert wird. Enforcement passiert an festen Gates der Compiler- und Runtime-Pipeline, nicht durch freie Programmausfuehrung.
24. Compile-Time Inlining und Lazy¶
24.1 Comptime Inlining¶
Comptime Inlining ist kein VM-Feature. Es ist ein IR-/Pass-Thema.
Gute Kandidaten:
kleine pure Funktionen
Operator-Wrapper
CastTo lossless
kleine Provide-Methoden
Getter/Setter
kleine Generator-Combinators
kleine Function-Union-Dispatcher
Schlechte Kandidaten:
grosse Funktionen
rekursive Funktionen
FFI-Grenzen
RuntimeBus-Commands
Scheduler-/JME-Operationen
Debug-Hooks
Inline sollte ein Hint oder Refinement sein, kein semantischer Zwang.
24.2 Lazy¶
Jade hat bereits zwei saubere Lazy-Formen:
Generator[T]
lazy Sequenz, next() liefert Option[T]
Effect[R, E, D]
Beschreibung einer Berechnung, Ausfuehrung erst durch Dependency-Kontext
VM-Lazy sollte nicht allgemeiner Auswertungsmodus sein. Falls spaeter noetig, dann als expliziter Typ:
Empfehlung:
Pipeline-Fusion ist wichtiger als VM-Lazy:
users |> filter(active) |> map(email) |> collect
naiv:
Generator wrapper + closures + next calls
optimiert:
eine Schleife mit if und push
25. Property Testing und Arbitrary¶
Property Testing passt sehr gut zu Jade, weil Jade Descriptoren besitzt.
Normales Muster:
Arbitrary sollte nicht nur Zufallswerte erzeugen, sondern auch shrinken.
type GenSeed : struct {
value: u64
}
type GenContext : struct {
seed: GenSeed
size: u32
depth: u32
}
type GenResult[T] : struct {
value: T
seed: GenSeed
}
protocol Arbitrary[T] {
def arbitrary(ctx: GenContext) -> GenResult[T]
def shrink(value: T) -> Generator[T]
}
Rng oder besser GenRng ist ein deterministischer Seed-Transformer:
Eigenschaft:
Damit sind Testfehler reproduzierbar.
Beispiel:
Ein TestContext kann dafuer bereitstellen:
PropertyEngine
GeneratorEngine
ShrinkerEngine
SeedControl
FailureDatabase
DeterministicScheduler
MockBridge
Property Testing prueft nicht nur Usercode, sondern auch Jade selbst:
parse(prettyPrint(ast)) ~= ast
lower(extendedIR) enthaelt keine ExtendedOps
serialize(deserialize(x)) == x
copy(x) erhaelt Struktur und Sharing-Regeln
dyn -> concrete Pipeline akzeptiert nur erlaubte Typen
ContextDescriptor erlaubt keine nicht deklarierten Capabilities
26. Content-addressable Jade Store¶
Nix adressiert immutable Store-Objekte und ihre Closure. Jade kann ein aehnliches Prinzip fuer Jade-native Artefakte sauberer anwenden, weil Jade-Artefakte selbst descriptor-basiert sind und nicht aus fremden Buildsystemen domestiziert werden muessen.
Jade sollte keinen Git-Tree nachbauen. Git adressiert Source-Historie. Jade sollte gepruefte Artefakte und Descriptor-Closures adressieren.
Git:
Was ist der Dateibaum?
Nix:
Was ist das gebaute Store-Objekt und seine Closure?
Jade:
Was ist das gepruefte Artefakt, seine Descriptoren,
sein Context und seine Closure?
Moegliche ArtifactId:
ArtifactId = hash(
normalized JBC,
descriptorSet,
contextDescriptor,
runtimeTypeProjections,
dependencyArtifactIds,
targetDescriptor,
abiProfile,
capabilityDescriptor
)
Unterscheidung:
Pure Jade Artifact
JBC, Descriptoren, Context-Anforderungen
stark reproduzierbar
gut content-addressable
Host-bound Jade Artifact
FFI, ABI, OS-Caps, native Bindings
braucht TargetDescriptor
reproduzierbar relativ zu Host-/ABI-Profil
External Artifact
fremde Software, C-Lib, Systempaket
braucht eigene Integritaets- und Provenance-Regeln
Der Satz:
Jade muss nicht jedes fremde Buildsystem nachtraeglich disziplinieren. Jade kann Artefaktsemantik selbst definieren. Fremdheit kommt nur an den Raendern zurueck und muss dort explizit descriptorisiert werden.
27. := als lokale immutable Type-Inference-Bindung¶
Aus der Odin-Diskussion wurde ein moegliches Sugar-Modell fuer JDL abgeleitet.
Sollte bedeuten:
Aequivalent zu:
Nicht erlaubt:
x := 456 // wenn x bereits existiert
var x := 123 // Unsinn
pub x := 123 // API sollte expliziter sein
Harte Regel:
Damit bleibt die Grundwahrheit sichtbar:
28. Architektur-Story von Jade¶
Die bisherige Diskussion laesst sich in einen zentralen Satz verdichten:
Jade trennt deklarierte Absicht, gepruefte Bedeutung und laufende Ausfuehrung.
Oder technischer:
Blueprints beschreiben Absicht.
Refinements praezisieren Absicht.
Provides binden Faehigkeiten.
Descriptoren materialisieren Bedeutung.
Contexts begrenzen Ausfuehrung.
Bridges kontrollieren Context-Grenzen.
Engines betreiben dynamische Ablaeufe.
VM/JME/RuntimeBus fuehren kontrolliert aus.
Jade loest damit reale Probleme:
- globale Runtime-Umgebungen werden explizit und gegrenzt
- Dynamik wird descriptor-basiert statt Any-basiert
- Compile-Time-Wissen verdampft nicht, sondern wird selektiv exportiert
- Plugins werden in eigene Contexts isoliert
- Web/RPC/Actor/REPL koennen auf denselben Descriptor-Grundlagen bauen
- Artefakte koennen in Jade Node / JadeOS reproduzierbar aktiviert werden
Der Pitch ohne Vergleichsfolklore:
Jade macht die Ausfuehrungsumgebung eines Programms zum Teil der Sprache.
Oder:
Jade ist eine VM-first Sprache fuer descriptor-getriebene Anwendungen. Sie verbindet statische Typsemantik mit kontrollierter Runtime-Dynamik: Blueprints beschreiben Absicht, Descriptoren materialisieren Bedeutung, Contexts begrenzen Ausfuehrung, Engines betreiben die Anwendung.
Wichtige Warnung:
Nutzer sollten zuerst einfache Formen sehen:
Nicht zuerst:
Die innere Jade-Regel lautet:
29. Erweiterte offene Entscheidungen¶
Zu den bisherigen offenen Entscheidungen kommen hinzu:
- ContextDescriptor-Schema: Welche Felder sind Kernschema, welche Profile?
- ContextApi: minimale API fuer
require,schedule,openResource,send,resolve. - ContextBridgeDescriptor: genaue Form fuer Queries, Commands, Messages, Policies.
- Parent-Expose-Modell: wie Services/Capabilities vom Parent an Child-Contexts gegeben werden.
- Fundamentale Engines: finale Liste und Grenze zwischen Ring 1/Ring 2.
- Storage im Context: Syntax und Descriptoren fuer Arena-/Pool-/Rc-Policies.
- SyntaxExtensionDescriptor: ParserCombinator-, Precedence-, Scope- und Lowering-Modell.
- JDL-eigener Sugar: welche Sprachformen werden selbst ueber diese Pipeline definiert?
- Arbitrary/Property Testing: Stdlib-Protocol, derive-Mechanik, TestContext.
- Artifact Store: Content-addressed Store fuer Jade-native Artefakte.
- Context-Flut-Lints: wann ist ein Context-Profil gerechtfertigt?
- Inline/Fusion/Lazy: welche Optimierungen sind IR-Pass, welche Stdlib-Typen, welche VM-Mechanik?
30. Ergaenzte Spec-Patch-Empfehlungen¶
30.1 Neue Context-Spec¶
Anlegen einer eigenen Spec, z.B.:
Inhalte:
Context als static env
ContextDescriptor-Schema
ContextApi
Context-Profile
Parent-Expose
ContextBridge
Context-Flut-Regeln
PluginContext-Isolation
30.2 Engine-/Descriptor-Spec aktualisieren¶
Ergaenzen:
fundamentale Engines
spezialisierte Engines als Komposition
Engine dynamic env vs Context static env
Engine-Spezialisierung ueber Protocols/Provides
Scheduler/Storage nicht als Engine-Eigentum, sondern Context-Policy + Ring-0-Mechanik
30.3 Parser-/Syntax-Spec ergaenzen¶
Ergaenzen:
SyntaxExtensionDescriptor
ParserCombinator-Registry
scoped Syntax Activation
LoweringProvider
JDL-Sugar nutzt dieselbe Pipeline wie User-Sugar
Konflikt-/Precedence-Regeln
30.4 Test-/Property-Spec anlegen¶
Ergaenzen:
Arbitrary[T]
GenSeed / GenContext / GenResult
Shrink-Protokoll
TestContext
FailureDatabase
deterministische Reproduktion
Descriptor-basiertes derive Arbitrary
30.5 Artifact-/Store-Spec spaeter anlegen¶
Ergaenzen: