ek9_create_dynamic_class
Inserts `variableName <- (captures) trait of T as class` (or the tuple / generic form) with its members after the line, auto-adding `default operator ?` for captures; answers status, edits[] and summary, with a would-fail carrying the compiler's own diagnostics (unimplemented abstract methods named).
When to use it
'An inline class implementing Describable over the captured prefix' is one call with file, line (the body line it goes after), variableName and traits:[{name}].
When not to use it
- You need a closure/lambda (an abstract-function impl), not a class - use
ek9_create_dynamic_function. - Authoring a NAMED top-level class reusable across the module, or one extending a non-generic concrete class (dynamic classes cannot) - use
ek9_new_constructmode:'class'. - Adding a trait to, or implementing methods on, an EXISTING declared type - use
ek9_adopt_trait/ek9_implement_methods.
Details
Form is set by `form` or auto-detected (traits present -> trait; baseType present -> generic; else tuple); className is a module-level TYPE distinct from the variableName receiver — mandatory for tuple, optional (anonymous) for trait/generic. Scratch-compilation is the sole validator: would-fail carries the compiler's own diagnostics (unresolved trait/base, unimplemented abstract methods, name collision E01040, purity, capture syntax E06230/E06240/E02040), while the fresh binding's expected E08090 (scaffold not yet referenced) is tolerated so a clean insert stays ready. conflicts kinds: target-not-function-body (bad anchor), dynamic-body-not-supported (nesting into a dynamic body, v1 limit).
Arguments
| Argument | Type | Required | What it is |
|---|---|---|---|
baseType | string | Generic-extension form: the parameterised base (e.g. `Iterator of Integer`, `Function of (String, Integer)`). | |
captures | array of object | Capture list (may be empty). Each entry: {fieldName?, sourceExpression}. Captures are by-pointer-value — mutating a captured object inside the class is visible to the caller (copy via :=: before capture for isolation). A non-empty list auto-adds `default operator ?`. | |
className | string | The dynamic class's own name — the grammar identifier, a module-level TYPE, distinct from variableName. REQUIRED for the tuple form; optional (anonymous when omitted) for trait/generic forms. | |
file | string | yes | The .ek9 source file containing the host body (workspace-relative; an absolute path inside the workspace is accepted). |
form | string: trait | tuple | generic | Which grammar alternative to emit. Auto-detected when omitted: traits present → trait; baseType present → generic; otherwise tuple. | |
inheritanceKeyword | string: is | extends | Generic-extension form only: `is` and `extends` are synonyms. Default: `is`. | |
line | integer | yes | 1-based line AFTER which the dynamic class is inserted (read it from ek9_read_file lineNumbers:true). Must be inside a function/program/method body and AFTER the captured variables are declared (a class with captures cannot be hoisted above them). |
members | string | Multi-line aggregateParts body: fields, then methods, then operators, in grammar order (the caller owns ordering). Required for the tuple form UNLESS at least one capture is supplied (captures become the tuple's fields + an auto `default operator ?`). Omit for trait/generic to scaffold header-only (the dryRun would-fail then names the abstract methods to implement). If you author an `operator ?`, the auto `default operator ?` is suppressed. | |
stubAbstractMethods | boolean | When true, scaffold the OUTSTANDING abstract methods the base owes, each as a compiling `override … //AI: complete method` stub (require-guarded params, guard-first return). Generic form: the base type's abstract methods with conceptual type parameters substituted (`Iterator of Integer` -> `next() as Integer`). Trait form: the traits' unimplemented abstract methods. Tuple form has no abstract obligations (no-op). Deduped against methods you already supply in `members` — only the missing ones are added. The result is a SCAFFOLD: bodies are placeholders behind `//AI:` markers, and the fresh variable's E08090 (not-yet-referenced) is tolerated so the dryRun is `ready`. Leave false to instead have the would-fail simply NAME the methods to add. | |
traits | array of object | Trait form: the traits to implement or delegate. Each {name, by?}. Order preserved. | |
variableName | string | yes | Local variable that receives the instance (e.g. wrapper, iterator1). Emitted as `variableName <- …`. |
It also takes the arguments every tool takes.
Recorded calls
Real calls, recorded against small projects and checked on every build - a changed answer fails it. 3 of the 12 recorded are shown.
Conflict not function body
Request
{
"captures" : [ {
"sourceExpression" : "prefix"
} ],
"file" : "Dyn.ek9",
"insertAfterLine" : 2,
"traits" : [ {
"name" : "Describable"
} ],
"variableName" : "w"
}
Response
{
"conflicts" : [ {
"kind" : "target-not-function-body",
"message" : "Line 2 is not inside a function, program or method body — a dynamic class has no host to live in there. Pick a line inside a callable body, after the captured variables are declared."
} ],
"edits" : [ ],
"meta" : {
"serverMs" : "<timing>"
},
"risk" : {
"crossesModuleBoundary" : false,
"estimatedSeverity" : "trivial",
"fileCount" : 0,
"lineCount" : 0,
"touchesPublicApi" : false,
"touchesTests" : false
},
"status" : "conflicts",
"summary" : "Cannot insert dynamic class w — 1 conflict",
"workspaceHash" : "<workspace-hash>",
"wouldBeDiagnostics" : [ ],
"written" : false
}
Trait stub abstract methods
Request
{
"dryRun" : true,
"file" : "Dyn.ek9",
"insertAfterLine" : 12,
"stubAbstractMethods" : true,
"traits" : [ {
"name" : "Describable"
} ],
"variableName" : "d"
}
Response
{
"conflicts" : [ ],
"edits" : [ {
"after" : " d <- () trait of Describable as class\n override describe() as pure\n <- rtn as String: String()\n //AI: complete method",
"endLine" : 13,
"file" : "Dyn.ek9",
"startLine" : 13,
"type" : "insert"
} ],
"meta" : {
"serverMs" : "<timing>"
},
"risk" : {
"crossesModuleBoundary" : false,
"estimatedSeverity" : "trivial",
"fileCount" : 1,
"lineCount" : 1,
"touchesPublicApi" : false,
"touchesTests" : false
},
"status" : "ready",
"summary" : "Would insert dynamic class d (trait of [Describable]) at line 13",
"workspaceHash" : "<workspace-hash>",
"wouldBeDiagnostics" : [ ],
...
Cut short here: 2 more lines. The whole recorded call.
Stub dedups provided members
Request
{
"baseType" : "Iterator of Integer",
"dryRun" : true,
"file" : "Dyn.ek9",
"insertAfterLine" : 12,
"members" : "override hasNext() as pure\n <- rtn as Boolean: true",
"stubAbstractMethods" : true,
"variableName" : "iter"
}
Response
{
"conflicts" : [ ],
"edits" : [ {
"after" : " iter <- () is Iterator of Integer as class\n override hasNext() as pure\n <- rtn as Boolean: true\n\n override next()\n <- rtn as Integer: 0\n //AI: complete method",
"endLine" : 13,
"file" : "Dyn.ek9",
"startLine" : 13,
"type" : "insert"
} ],
"meta" : {
"serverMs" : "<timing>"
},
"risk" : {
"crossesModuleBoundary" : false,
"estimatedSeverity" : "trivial",
"fileCount" : 1,
"lineCount" : 1,
"touchesPublicApi" : false,
"touchesTests" : false
},
"status" : "ready",
"summary" : "Would insert dynamic class iter (is Iterator of Integer) at line 13",
"workspaceHash" : "<workspace-hash>",
"wouldBeDiagnostics" : [ ],
...
Cut short here: 2 more lines. The whole recorded call.
See also
- Show me in the IDE: Create Dynamic Class