B4's membership atom chose between predicate and unnest by grain; this finishes the comparison on NULL and cost. NULL axis: predicates treat NULL elements as unknowable-therefore-no-match (three-valued logic inside the array) and a NULL array as an unknown whole; the unnest spelling materializes NULL elements as rows that never join — same net exclusion, different debuggability. Cost axis: predicates evaluate in place, no explosion, no forgotten DISTINCT.
SELECT box FROM boxes WHERE NOT list_contains(ings, 'nut') ORDER BY box;
SELECT box FROM boxes WHERE NOT list_contains(ings, 'nut') AND NOT list_contains(ings, NULL) ORDER BY box;
The nut-safe filter called b2 safe, but its array has a NULL (unlabeled) ingredient that could be a nut — array predicates treat a NULL element as unknowable-therefore-no-match, so a NULL-aware check is needed before declaring a box safe.
Predicates for in-place verdicts, NULL policy stated; unnest when elements (incl. NULLs) must be visible.