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.

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

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: type, circular, resolution, hierarchy, E06150, E06140, conflict, abstract, inherit, virtual, function, E05210, trait, override, diamond, multiple, open