Why does AI try to extend List, Dict, or closed types in EK9?
← What AI Gets Wrong About EK9 · Ref: Q278
AI models trained on Java generate 'class MyList extends ArrayList' or 'class TypedMap extends HashMap' patterns. In EK9, types are CLOSED by default. Built-in collection types (List, Dict, Optional, Result, PriorityQueue) cannot be extended. Attempting to extend them produces error E05030.
THE AI MISTAKE
AI generates 'MyList extends List of String' or 'SafeDict extends Dict of (String, Integer)'. This fails because List and Dict are closed types. The AI is importing the Java pattern where ArrayList and HashMap are open.
THE EK9 WAY: COMPOSITION
Wrap the collection as a private field and expose only the operations you need:
TaskQueue
items as List of String
addTask()
-> taskName as String
items += taskName
pending() as pure
<- count as Integer: length items
The List is hidden. Clients interact with TaskQueue, not List.
WHY CLOSED BY DEFAULT
Closed types prevent mixing collection behavior with application logic. If you extend List, callers get both your custom API and every List method. This creates confusing, hard-to-maintain interfaces. Composition gives you complete control over the public API.
ADDING CUSTOM VALIDATION
Composition lets you add business rules that inheritance cannot:
ValidatedList
items as List of String
add()
-> entry as String
if entry?
items += entry
Only set (valid) strings are added. With inheritance, callers could bypass your add method and use the parent List methods directly.
EXPOSING ITERATION
Delegate to the wrapped collection when needed:
allTasks() as pure <- tasks as List of String: List(items)
Return a copy to prevent external modification of internal state.
MODERN PRECEDENT
Swift structs are closed. Rust has no inheritance. Kotlin classes are final by default. EK9 follows this modern trend. Types require explicit 'as open' to allow extension.
See Q96 for class definitions. See Q212 for composition over inheritance. See Q210 for trait delegation. See Q281 for verifying AI code.
Example
defines module qa.ai.mistakes.closedtypes defines class // === COMPOSITION WRAPPING A LIST === TaskQueue items as List of String: List() of String default TaskQueue() addTask() -> taskName as String if taskName? items += taskName pending() as pure <- count as Integer: length items allTasks() as pure <- tasks as List of String: items + List() of String override operator ? as pure <- isSet as Boolean: items? operator $ as pure <- asString as String: `TaskQueue(${pending()} tasks)` // === COMPOSITION WRAPPING A DICT === Settings entries as Dict of (String, String): Dict() of (String, String) default Settings() put() -> key as String setting as String if key? and setting? entries += DictEntry(key, setting) get() as pure -> key as String <- setting as String: entries.getOrDefault(key, "") has() as pure -> key as String <- exists as Boolean: entries.contains(key) override operator ? as pure <- isSet as Boolean: entries? defines program ClosedTypeDemo() stdout <- Stdout() // === USE TASKQUEUE (COMPOSITION OVER INHERITANCE) === queue <- TaskQueue() queue.addTask("Deploy") queue.addTask("Test") queue.addTask("Review") stdout.println($queue) stdout.println(`Pending: ${queue.pending()}`) tasks <- queue.allTasks() for taskItem in tasks stdout.println(` Task: ${taskItem}`) // === USE SETTINGS (COMPOSITION OVER INHERITANCE) === prefs <- Settings() prefs.put("theme", "dark") prefs.put("lang", "en") stdout.println(`Theme: ${prefs.get("theme")}`) stdout.println(`Has lang: ${prefs.has("lang")}`) stdout.println(`Missing: ${prefs.get("missing")}`)
Common mistakes
E05030 — EK9 types are closed by default. List, Dict, Optional, Result, and PriorityQueue cannot be extended. Use composition instead: wrap the collection as a private field. See ek9 -h E05030 for details.
Incorrect:
TaskQueue extends List of String items as List of String
Correct:
TaskQueue
items as List of String
E05030 — Dict is a closed type and cannot be subclassed. Wrap it as a field and expose domain-specific methods instead. See ek9 -h E05030 for details.
Incorrect:
Settings extends Dict of (String, String) entries as Dict of (String, String)
Correct:
Settings
entries as Dict of (String, String)
Other ways to ask this
- Why does AI generate 'MyList extends List of String' in EK9?
- How do I fix AI-generated class extension of closed types?
- Why are EK9 types closed by default?
Coming from another language?
Java: ArrayList and HashMap are open, commonly extended (considered bad practice). Python: list is open, commonly subclassed. C++: virtual inheritance, open by default. Rust: no inheritance, composition is the pattern. Go: embedding for delegation, no inheritance. Kotlin: classes final by default like EK9. Swift: structs are closed. EK9: ALL types closed by default, 'as open' required for extension, built-in collections cannot be extended, composition is the natural pattern.
Keywords: hallucination, inherit, extend, list, delegate, dict, pitfall, open, common-error, composition, sealed, migrate, wrong, exhaustive, closed, ai, mistake