NonEmptyList · Nel
A list statically guaranteed to hold at least one element — the error carrier of the accumulation API. Zero-cost: an extension type, erased at runtime.
Lecture
"A list of validation errors" has an awkward edge case: what does an
empty error list mean? NonEmptyList (alias
Nel) removes the question in the type system — if you hold
one, there is at least one element, so head is total and
cannot throw, unlike List.first. That's exactly what
accumulation needs:
EitherNel<E, A> = Either<Nel<E>, A>,
where a Left always carries at least one error.
It is Dart's analogue of Arrow's value class NonEmptyList:
an extension type over List — zero allocation,
erased at runtime, and it implements Iterable, so every
fxdart pipeline and for loop takes it directly. The
invariant is compile-time discipline: build one only through
NonEmptyList.of(head, [tail]) or
NonEmptyList.orNull(list) (which returns null
for an empty list — the emptiness check happens exactly once, at the
boundary). A cast like list as Nel<int> would bypass
the check at your own risk.
Demo 1 · of, orNull, head & tail
Demo 2 · map, +, and pipelines
Demo 3 · toNelOrNull — any Iterable in
Nel.orNull takes a List, so every
accumulating pipeline used to end in a .toList() shuffle
before its errors could become a panel. The
toNelOrNull() extension (Arrow's
toNonEmptyListOrNull) accepts any Iterable —
including a lazy fx chain — copies it, and gives you the
Nel? directly: null for "no errors", a
guaranteed-non-empty list otherwise.
Try it yourself
Exercise: finish summarize — with the null case
already handled, nel.length and nel.head can't
fail.
Nel carries every failure ·
Either — toEitherNel() lifts a failure into a singleton Nel ·
firstOrNull — the nullable-first access it makes total ·
typed errors — full guide