How does EK9 resolve the diamond problem with trait method conflicts?
← Type Hierarchy Constraints · Ref: Q680
When a class implements two traits that declare the same method name, EK9 detects the conflict and requires the class to provide an explicit override to resolve it.
DIAMOND DETECTION (E06150)
If TraitA declares process() and TraitB also declares process(), a class implementing both has a conflict. The compiler reports E06150 (conflicting methods to be resolved) on the class unless the class provides an override.
AMBIGUOUS CALLS (E06140)
While the conflict is unresolved, every call to that method on the class is also reported, with E06140 (ambiguous match), naming both trait versions.
A SHARED TRAIT IS NOT A CONFLICT
If Walker and Swimmer both have trait of Named, a class with trait of Walker, Swimmer reaches Named's methods along two routes - but each is ONE method with one body, so there is nothing to resolve and no override is needed. It is a conflict only when the routes bring DIFFERENT bodies: Walker overrides Named's describe() and Swimmer does not, or the super class overrides it and a listed trait brings Named's.
ONE IMPLEMENTATION PLUS ABSTRACT DECLARATIONS IS NOT A CONFLICT
If Named's describe() is abstract, Walker implements it and Swimmer leaves it abstract, a class with trait of Walker, Swimmer has Walker's describe(): the implementation IS the method, it meets the abstract requirement, and no override is needed. The same holds when the super class implements it (Base with trait of Named) and a listed trait leaves it abstract (Sub extends Base with trait of Swimmer), or the other way round. It is still E06150 when two DIFFERENT implementations reach the class, when a 'by' delegate's route meets another route with a body (a delegate alone does implement an abstract method), or when the abstract method belongs to an unrelated trait that merely has the same signature; and a concrete class whose abstract method nothing implements is E07130.
RESOLUTION VIA OVERRIDE
The class must provide its own 'override' of the conflicting method. This removes ambiguity by giving the class full control over the method body, and both errors go away.
UNREACHABLE DISPATCH (E05210)
In dispatchers, a handler for a type that is not reachable from the base dispatch type is flagged with E05210. This ensures all dispatcher handlers can actually be invoked.
CORRECT PATTERN
TraitA
process() ...
TraitB
process() ...
MyClass with trait of TraitA, TraitB
override process() ... // Resolves the conflict
See Q107 for multiple trait implementation. See Q579 for trait override chains. See Q611 for hierarchy validation summary.
Example
#!ek9 defines module qa.typehierarchy.diamondtrait defines trait <?- First trait: declares formatRecord() with a default body. -?> Auditable auditEntry() as pure <- rtn as String: "audited" formatRecord() as pure <- rtn as String: `[AUDIT] ${auditEntry()}` <?- Second trait: declares a formatRecord() method too. This creates a conflict for classes implementing both traits. -?> Printable printLabel() as pure <- rtn as String: "printable" formatRecord() as pure <- rtn as String: `[PRINT] ${printLabel()}` defines class <?- Class implementing both traits. Must override formatRecord() to resolve the diamond conflict. -?> DocumentRecord with trait of Auditable, Printable title <- String() DocumentRecord() -> title as String this.title: title override auditEntry() as pure <- rtn as String: title override printLabel() as pure <- rtn as String: title //Resolves the diamond: both Auditable and Printable have formatRecord() override formatRecord() as pure <- rtn as String: `[DOC] ${title}` default operator ? <?- Another class implementing only one trait: no conflict. -?> SimpleAudit with trait of Auditable action <- String() SimpleAudit() -> action as String this.action: action override auditEntry() as pure <- rtn as String: action default operator ? defines program DiamondTraitDemo() stdout <- Stdout() doc <- DocumentRecord("Invoice #42") stdout.println(doc.formatRecord()) stdout.println(doc.auditEntry()) stdout.println(doc.printLabel()) simple <- SimpleAudit("login") stdout.println(simple.formatRecord())
Common mistakes
E50010 — UnrelatedTrait is not defined in this module. Adding an unresolved type to the trait list triggers E50010. See ek9 -h E50010 for details.
Incorrect:
DocumentRecord with trait of Auditable, Printable, UnrelatedTrait
Correct:
DocumentRecord with trait of Auditable, Printable
E06150 — When two traits provide conflicting method implementations and the implementing class does not override the method to resolve the ambiguity, E06150 is reported. The class must provide its own override. See ek9 -h E06150 for details.
Incorrect:
//formatRecord() not overridden, leaving the diamond conflict unresolved
Correct:
override formatRecord() as pure <- rtn as String: `[DOC] ${title}`
Other ways to ask this
- What is E05210 dispatcher unreachable handler?
- What is E06150 conflicting methods from multiple traits?
- Why do I get E06140 ambiguous match when calling a method two traits both define?
- How do I resolve conflicting methods from two traits?
Coming from another language?
Java: interface default method conflict requires class override. Kotlin: must override and can use super<TraitName>.method(). Python: MRO determines which implementation wins. C++: virtual inheritance for diamond. Rust: explicit disambiguation with <Type as Trait>::method(). EK9: class must provide override for conflicting trait methods.
Keywords: E05210, E06140, E06150, abstract, circular, conflict, diamond, function, hierarchy, inherit, multiple, open, override, resolution, trait, type, virtual