Do traits, records, and components also check for circular hierarchies?
← Type Hierarchy Constraints · Ref: Q604
E05020 applies to ALL EK9 construct types that support inheritance or implementation relationships, not just classes.
TRAITS
Traits can extend other traits, forming hierarchies. Circular trait chains (TraitA extends TraitB extends TraitA) are detected and rejected with E05020.
RECORDS
Records that extend other records are subject to the same cycle detection. A record extending another record that extends the first is a compile error.
COMPONENTS
Components participate in hierarchies through trait implementation. Circular dependencies between components are detected.
CLASSES WITH TRAITS
Even when cycles involve a mix of classes and traits (a class implementing a trait that requires extending a class that circularly depends on the original), the compiler detects the cycle.
UNIFORM ENFORCEMENT
The type hierarchy checker runs the same cycle detection algorithm across all construct types. This means you get the same error (E05020) regardless of whether the cycle involves classes, traits, records, or components.
See Q602 for circular hierarchy basics. See Q106 for trait hierarchies. See Q97 for records vs classes.
Example
defines module qa.typehierarchy.allconstructs defines trait //CORRECT: Linear trait hierarchy Describable describe() as abstract <- rtn as String? Displayable with trait of Describable display() as abstract <- rtn as String? defines record //CORRECT: Simple record (records are closed by default) Point xPos as Float: 0.0 yPos as Float: 0.0 Point() -> xPos as Float yPos as Float this.xPos: xPos this.yPos: yPos operator $ as pure <- rtn as String: `(${xPos}, ${yPos})` default operator ? defines class //Class implementing trait hierarchy correctly Widget with trait of Displayable label <- "widget" Widget() -> label as String this.label: label override describe() <- rtn as String: label override display() <- rtn as String: `[${label}]` default operator ? defines function testCorrectHierarchies() point <- Point(3.0, 4.0) require point? require $point == "(3.0, 4.0)" widget <- Widget("button") require widget? require widget.describe() == "button" require widget.display() == "[button]"
Common mistakes
E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.
Incorrect:
require widget.describe().toUpperCase() == "button"
Correct:
require widget.describe() == "button"
E05120 — When implementing abstract trait methods like describe from Describable, the override keyword is required to show explicit intent. See ek9 -h E05120 for details.
Incorrect:
describe()
Correct:
override describe()
Other ways to ask this
- Does EK9 check circular inheritance on traits?
- Can records have circular hierarchies in EK9?
- Is E05020 only for classes or all construct types?
Coming from another language?
Java: circular check on classes and interfaces. C#: circular check on classes, interfaces, and structs. Kotlin: circular check on all types including sealed. Rust: no class inheritance; trait coherence rules prevent cycles. Go: interfaces are structural, no circular hierarchy concept. EK9: uniform E05020 check across classes, traits, records, and components.
Keywords: inherit, hierarchy, uniform, record, trait, component, circular, construct, type, E05020, cycle