NonEmptyList · Nel

Una lista con la garantía estática de contener al menos un elemento — el portador de errores de la API de acumulación. Coste cero: un extension type, borrado en tiempo de ejecución.

extension type NonEmptyList<T> implements Iterable<T> — typedef Nel<T> = NonEmptyList<T> factory NonEmptyList.of(T head, [Iterable<T> tail = const []]) static NonEmptyList<T>? orNull<T>(List<T> list) on Iterable<T>: NonEmptyList<T>? toNelOrNull()

Lección

«Una lista de errores de validación» tiene un caso límite incómodo: ¿qué significa una lista de errores vacía? NonEmptyList (alias Nel) elimina la pregunta en el sistema de tipos: si tienes uno en la mano, hay al menos un elemento, así que head es total y no puede lanzar, a diferencia de List.first. Eso es justo lo que necesita la acumulación: EitherNel<E, A> = Either<Nel<E>, A>, donde un Left siempre lleva al menos un error.

Es el análogo en Dart del value class NonEmptyList de Arrow: un extension type sobre List — cero asignaciones de memoria, borrado en tiempo de ejecución y, como implements Iterable, cualquier pipeline de fxdart y cualquier bucle for lo aceptan directamente. La invariante es disciplina en tiempo de compilación: construye uno únicamente mediante NonEmptyList.of(head, [tail]) o NonEmptyList.orNull(list) (que devuelve null para una lista vacía — la comprobación de vacuidad ocurre exactamente una vez, en la frontera). Un cast como list as Nel<int> saltaría esa comprobación por tu cuenta y riesgo.

Demo 1 · of, orNull, head & tail

Demo 2 · map, +, y pipelines

Demo 3 · toNelOrNull — cualquier Iterable

Nel.orNull recibe una List, así que cada pipeline acumulador terminaba en un baile de .toList() antes de que sus errores pudieran volverse un panel. La extensión toNelOrNull() (el toNonEmptyListOrNull de Arrow) acepta cualquier Iterable — incluida una cadena fx perezosa —, la copia y te da el Nel? directamente: null para "sin errores", una lista garantizada no vacía en caso contrario.

Pruébalo tú

Ejercicio: completa summarize — con el caso null ya tratado, nel.length y nel.head no pueden fallar.

Relacionado: acumulación — donde Nel transporta todos los fallos · EithertoEitherNel() eleva un fallo a un Nel de un solo elemento · firstOrNull — el acceso nullable-first que vuelve total · errores tipados — guía completa