ADR-007: Validation Error Accumulation via Applicative Functor
Context
The compiler runs multiple validation checks — exhaustiveness, evolve totality, guard consistency. These checks can either fail-fast (stop at first error) or accumulate all errors and report them together.
The question is how to compose independent checks and which checks can run in parallel.
| Strategy | Errors reported | Composition |
|---|---|---|
| Monad (Either) | First error only | Sequential (>>=) |
| Applicative (Validation) | All errors | Parallel (<*>) |
Source: specs/theory.allium — “Applicative Functor (validation composition)” section
(Th-3).
Decision
Use Validation<List<Diagnostic>, A> — the applicative functor, not a monad.
Independent checks (exhaustiveness, totality, guard consistency) compose via applicative
<*> without sequencing — they can execute in any order or in parallel. Dependent checks
(postconditions depend on evolve totality) require monadic bind and run only after their
prerequisites succeed.
The first iteration ships with stop_on_first_error: true (configured in core.allium).
Full applicative accumulation is the target steady state and is enabled progressively as
the validation pipeline stabilises.
Consequences
Positive
- Domain modelers see all errors at once rather than fixing one at a time.
- The applicative vs. monadic distinction explicitly documents which checks depend on which, making the dependency structure visible in code.
- Independent checks can execute concurrently without coordination overhead.
Negative
stop_on_first_error: truein the first iteration means accumulation is not yet active; the architecture is in place but the user-facing benefit is deferred.- The applicative/monadic distinction adds conceptual complexity for contributors unfamiliar with functional programming abstractions.