orj

NonEmptyList: emptiness, unrepresentable

Half the defensive code in a codebase guards against collections that "shouldn't" be empty. NonEmptyList<T> deletes that code by making emptiness impossible to construct: it's a head plus a tail, so head() always succeeds and size() is always at least one. This is parse, don't validate applied to collections. It is also the foundation Validated stands on, which is why an Invalid can never carry zero errors.

The parser is the boundary

fromList is the only door in from a possibly-empty world, and it returns Optional: absence, not failure, because only the caller knows what an empty input means in their domain. Past that door, the guarantee holds everywhere the value flows.

Optional<NonEmptyList<String>> owners = NonEmptyList.fromList(List.of("ada", "grace"));
Optional<NonEmptyList<String>> nobody = NonEmptyList.fromList(List.of());

assertTrue(owners.isPresent());
assertEquals("ada", owners.get().head());   // total: no isEmpty() check, ever
assertTrue(nobody.isEmpty());

Operations preserve the guarantee

map, append, and concat all return NonEmptyList, so non-emptiness survives by construction and downstream code keeps the proof without re-checking anything.

NonEmptyList<Integer> counts = NonEmptyList.of(1, 2, 3).map(n -> n * 10);
assertEquals(List.of(10, 20, 30), counts.toList());

NonEmptyList<Integer> more = counts.append(40).concat(NonEmptyList.of(50));
assertEquals(5, more.size());
assertEquals(10, more.head());

It iterates, and it can't be mutated

NonEmptyList is Iterable (head first), and it defends its invariant: the list you build it from is defensively copied, so no one holding the original reference can empty it out from under you.

var source = new ArrayList<>(List.of("keep", "these"));
var nel = NonEmptyList.fromList(source).orElseThrow();

source.clear();                          // mutate the original all you like

var seen = new ArrayList<String>();
for (String s : nel) {
    seen.add(s);
}
assertEquals(List.of("keep", "these"), seen);

This page is generated from a test file. Read or improve it on GitHub.