E11023: manual delegation detected - use 'by' keyword

Category: E11xxx Code quality and IR analysis · Reported in phase: PRE_IR_CHECKS · Classification: MISSING_BY_DELEGATION · All error codes

What went wrong

Your class implements a trait without 'by', has a field compatible with that trait, and manually overrides most/all trait methods. This is boilerplate that EK9's 'by' delegation eliminates automatically.

When this error triggers

  1. Your class implements a trait WITHOUT using 'by'
  2. You have a field that could implement that trait (same type or subtype)
  3. You've overridden most of the trait's methods (>= 70%, at least 5)

This suggests the overrides are forwarding to the field - use 'by' instead.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 859 (When should I use 'by' delegation instead of manual metho...)

How to fix it

  1. USE 'by' FOR FULL DELEGATION
  • Add 'by fieldName' to your trait declaration
  • Remove manually-overridden forwarding methods
  • EK9 generates the delegation automatically
  1. USE 'by' WITH SELECTIVE OVERRIDES (if some methods need custom logic)
  • Add 'by fieldName' for automatic delegation of most methods
  • Override ONLY the methods that need custom logic
  • The non-overridden methods still delegate automatically

Transform

The [EXAMPLE] section below shows a class forwarding all seven methods of a trait to a field by hand, then the class declared 'with trait of Processor by delegate' with no forwarding methods at all (good.ek9), and the same delegation overriding only the one method that needs custom logic (good_selective.ek9).

Multiple traits

You can delegate different traits to different fields: 'with trait of Reader by reader, Writer by writer' sends Reader's methods to the 'reader' field and Writer's to 'writer'.

Subtype delegation

The delegate field can be a subtype of the trait: 'with trait of Processor by impl' works when 'impl' is an AdvancedProcessor that IS-A Processor.

See: https://ek9.io/traits.html "Delegation is now as easy as inheritance!" The 'by' syntax means you don't have to write 20 methods that just call delegate.methodName() - EK9 generates all the forwarding automatically. Override only where you need custom behavior.

Why this matters

  • Eliminates repetitive boilerplate code
  • Reduces maintenance burden when trait evolves
  • Makes delegation intent explicit and clear
  • Follows Kotlin-style zero-boilerplate delegation

THRESHOLD: >= 70% trait methods overridden AND >= 5 manual overrides

EXCEPTION: Manual override when traits have conflicting method signatures is REQUIRED - EK9 excludes this case from the error.

Research basis

  • Gamma et al.: 'Design Patterns' (1994) - Delegation pattern
  • Kotlin Language: Class delegation with 'by' - zero-boilerplate inheritance
  • Fowler, M.: 'Refactoring' (1999) - Code smells, unnecessary duplication

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E11023: manual delegation detected - use 'by' keyword

#!ek9
defines module error.e11023.bad

  defines trait
    Processor
      cleanup() as abstract

      execute() as abstract

      finalize() as abstract

      prepare() as abstract

      process() as abstract

      transform() as abstract

      validate() as abstract

  defines class
    ProcessorImpl with trait of Processor
      override cleanup()
        require true

      override execute()
        require true

      override finalize()
        require true

      override prepare()
        require true

      override process()
        require true

      override transform()
        require true

      override validate()
        require true

  defines class

    ManualDelegator with trait of Processor
      //Has a field compatible with the trait
      delegate as Processor: ProcessorImpl()

      //Manually overrides all 7 methods (100% > 70% threshold)
      //This is boilerplate - should use 'by delegate'
      override cleanup()
        delegate.cleanup()

      override execute()
        delegate.execute()

      override finalize()
        delegate.finalize()

      override prepare()
        delegate.prepare()

      override process()
        delegate.process()

      override transform()
        delegate.transform()

      override validate()
        delegate.validate()

      default operator ?

Fixed: Delegate the trait with by

#!ek9
defines module error.e11023.good

  defines trait

    Processor
      process() as abstract
      validate() as abstract

  defines class

    ProcessorImpl with trait of Processor
      override process()
        stdout <- Stdout()
        stdout.println("process")

      override validate()
        stdout <- Stdout()
        stdout.println("validate")

    Delegator with trait of Processor by delegate
      delegate as Processor: ProcessorImpl()

      default operator ?

Fixed: Delegate with 'by' and override only the methods that need custom logic

#!ek9
defines module error.e11023.good.selective

  defines trait
    Processor
      process() as abstract
      validate() as abstract
        -> request as String
        <- rtn as Boolean?

  defines class
    ProcessorImpl with trait of Processor
      override process()
        stdout <- Stdout()
        stdout.println("process")
      override validate()
        -> request as String
        <- rtn as Boolean: not request.empty()

    //'process()' still delegates; only 'validate()' adds logging
    Delegator with trait of Processor by delegate
      delegate as Processor: ProcessorImpl()

      override validate()
        -> request as String
        <- rtn as Boolean: delegate.validate(request)
        stdout <- Stdout()
        stdout.println(`Validating: ${request}`)

      default operator ?

Questions and answers

These Q&A entries show E11023 and its fix: