Nil is a pessimist
Guifa was unhappy with $xml.elements
returning a list with one undefined element if there are no child nodes. That led me to the conclusion that Nil
is only halve Empty
.
Let’s consider this piece of code.
sub nilish() { Nil };
for nilish() { say 'oi‽' }
my $nil := nilish();
for $nil { say 'still oi‽' }
sub no-return() { }
for no-return() { say 'even more oi‽' }
sub return-a-list( --> List:D ) { Nil }
for return-a-list() { say 'Should this happen?' }
# OUTPUT:
# oi‽
# still oi‽
# even more oi‽
# Should this happen?
We are iterating over the special container-reset-value called Nil
because there is no container to reset. Since for
binds to its arguments it binds to Nil
. A type object, even a very special one as Nil, is a single item which is treated as a list with one element by for
.
We can solve this problem by a multi sub that turn unreasonable values into the empty list.
multi sub undefined-to-empty(Nil) { Empty }
multi sub undefined-to-empty(@a where all @a.map(!*.defined)) { Empty }
multi sub undefined-to-empty(\item) { item }
for undefined-to-empty(Nil) { say 'nothing happens' }
for undefined-to-empty((Any,)) { say 'nothing happens' }
By adding a candidate that checks if there are only undefined values in a list we can also solve guifa`s problem.
This is of cause just a shim. The real solution is to stop turning null
into Nil
in native bindings. If you write a sub that has to return a list but can’t either fail
or return Empty
if there is nothing to return. To help avoid that mistake in the future I filed #2721.
If it looks empty or sounds emtpy or tastes emtpy make it Empty
!