E11023: manual delegation detected - use 'by' keyword
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
- Your class implements a trait WITHOUT using 'by'
- You have a field that could implement that trait (same type or subtype)
- 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
- USE 'by' FOR FULL DELEGATION
- Add 'by fieldName' to your trait declaration
- Remove manually-overridden forwarding methods
- EK9 generates the delegation automatically
- 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: