Guard<T> accumulates errors as a fixed NonEmptyList<String>
Context
Guard<T> needs an error type to represent validation failures. The natural alternative would be to make it generic (Guard<T, E>), but that drastically complicates composition.
Decision
The error type of Guard<T> is always NonEmptyList<String> — human-readable error messages as strings.
Consequences
Positive:
- Composition (
and,or,negate) does not require an externalMonoid<E>— list concatenation is the only merge operator needed. - Validation lambdas are simple:
Guard.of(predicate, "error message"). - Direct interop with
Validated<NonEmptyList<String>, T>.
Negative / tradeoffs:
- Not suitable when the error must be a typed domain object (e.g., a
ValidationErrorenum). - In that case the user must work directly with
Validated<E, A>.
Alternatives considered
- Generic
Guard<T, E>: requires the user to provide aBinaryOperator<E>for merging inand/or— more complex API. Guard<T>withList<String>: allows an empty list, which is semantically invalid for an error.