How does EK9 limit inheritance depth and what happens when exceeded?
← Type Hierarchy Constraints · Ref: Q697
EK9 limits the depth of class inheritance hierarchies. Excessively deep hierarchies are a code smell that indicates design problems.
DEPTH LIMIT (E11013)
The compiler tracks the number of levels in an inheritance chain. When the chain exceeds the threshold, E11013 is raised. This forces redesign toward flatter hierarchies.
WHY LIMIT DEPTH
Deep hierarchies create problems:
1. Fragile base class: changes at the top cascade unpredictably
2. Understanding difficulty: must read 6+ classes to understand behavior
3. Constructor complexity: super() chains become error-prone
4. Testing complexity: each level adds testing permutations
CORRECT APPROACH
Prefer composition and trait-based design over deep inheritance:
// Instead of: A -> B -> C -> D -> E -> F // Use: F with trait of Auditable, Printable, Validatable
ACCEPTABLE DEPTH
A 3-5 level hierarchy is typically acceptable:
Base -> Intermediate -> Concrete Shape -> Polygon -> Triangle
See Q611 for hierarchy validation summary. See Q677 for abstract implementation chain. See Q602 for circular hierarchy detection.
See Q729 for inheritance depth boundary example.
Example
defines module qa.typehierarchy.depthlimit defines trait //Traits provide behavior without deep hierarchies Auditable auditLog() as pure <- rtn as String: "audited" Printable printLabel() as pure <- rtn as String: "printable" defines class //Level 1: abstract base Entity as abstract entityType() as pure abstract <- rtn as String? default operator ? //Level 2: intermediate with common behavior NamedEntity extends Entity as open entityName <- String() NamedEntity() -> entityName as String this.entityName: entityName getName() as pure <- rtn as String: entityName override entityType() as pure <- rtn as String: "named" default operator ? //Level 3: concrete with traits for additional behavior Customer extends NamedEntity with trait of Auditable, Printable customerId <- String() Customer() -> customerName as String customerId as String super(customerName) this.customerId: customerId getCustomerId() as pure <- rtn as String: customerId override entityType() as pure <- rtn as String: "customer" override auditLog() as pure <- rtn as String: `customer:${customerId}` override printLabel() as pure <- rtn as String: `${getName()} (${customerId})` default operator ? defines program InheritanceDepthLimitDemo() stdout <- Stdout() customer <- Customer(customerName: "Alice", customerId: "C001") stdout.println(customer.printLabel()) stdout.println(customer.auditLog()) stdout.println(customer.entityType())
Common mistakes
E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.
Incorrect:
stdout.println(customer.printLabel().toUpperCase())
Correct:
stdout.println(customer.printLabel())
Other ways to ask this
- What is E11013 inheritance depth exceeded?
- How deep can class hierarchies be in EK9?
- Is there a maximum inheritance depth?
- Why does EK9 limit inheritance chain length?
Coming from another language?
Java: no depth limit (static analysis tools may warn). Python: MRO handles deep hierarchies but they are a smell. C++: no depth limit. Kotlin: no depth limit. Rust: no inheritance, uses trait composition. Go: no inheritance, uses embedding. EK9: compile-time depth limit to enforce good design.
Keywords: E11013, chain, limit, depth, inheritance, design, composition, hierarchy, circular, type