Why can't I inject components in a pure context?
← DI Validation · Ref: Q666
Component injection is an impure operation because it involves runtime state management (E08140). The DI container resolves and provides instances at runtime, which conflicts with purity guarantees.
WHY INJECTION IS IMPURE
Pure functions guarantee no side effects and deterministic results. Injection accesses mutable singleton state managed by the DI container, making the result non-deterministic.
CORRECT PATTERN
Inject at the field level, then pass data to pure methods:
defines component MyComponent repository as Repository!
processData() as pure -> items as List of String <- count as Integer: length items
The impure injection happens at field level. Pure methods receive data through parameters.
See Q111 for component basics. See Q560 for purity contracts. See Q667 for abstract injection.
Example
defines module qa.divalidation.purecontext defines component <?- Abstract component defining repository contract. -?> DataStore as abstract fetchAll() as abstract <- rtn as List of String? default operator ? <?- Concrete implementation of the data store. -?> InMemoryStore extends DataStore override fetchAll() <- rtn <- List() of String rtn += "record-alpha" rtn += "record-beta" default operator ? defines function <?- Pure function receives data as parameter, not through injection. This is the correct pattern for processing data without DI coupling. -?> countRecords() as pure -> records as List of String <- total as Integer: length records defines application DataApp register InMemoryStore() as DataStore defines program InjectionPureContextDemo() with application of DataApp stdout <- Stdout() store as DataStore! allRecords <- store.fetchAll() stdout.println(`Records fetched: ${length allRecords}`) recordCount <- countRecords(allRecords) stdout.println(`Count via pure function: ${recordCount}`)
Common mistakes
E08140 — Component injection with '!' is an impure operation (accesses DI container state). A pure method cannot contain injection points. Pass the component as a parameter instead. See ek9 -h E08140 for details.
Incorrect:
override fetchAll() <- rtn <- List() of String rtn += "record-alpha" rtn += "record-beta" pureButBroken() as pure injected as DataStore! require injected?
Correct:
override fetchAll() <- rtn <- List() of String rtn += "record-alpha" rtn += "record-beta"
Other ways to ask this
- What is E08140 injection in pure context?
- Why does injection conflict with purity?
- How do I use components in pure methods?
Coming from another language?
Java: Spring allows injection everywhere (no purity concept). Python: no DI purity enforcement. Go: no DI framework. Rust: no runtime DI. EK9: injection is impure by design, use parameter passing for pure methods.
Keywords: registration, side-effect, text, context, E08140, component, validate, injection, DI, immutable, inject, pure, impure