We're now in a situation where VOID is rejected by most routines as input. In this world, "VOID-in, NULL out" has been showing off excellently as the replacement for what was long discussed under the title none propagation. I summarize the theory of the benefits here, and why routines taking "NONE!-in, NONE!-out" were a dangerous idea.
But it can't be followed blindly. As an obvious-if-you-think-about-it case, LOGIC!-returning routines have to handle void a different way.
I wanted to write the following;
if exists? maybe some-dir: get-env 'SOME-DIRECTORY [ ... ]
If GET-ENV returns null, the MAYBE turns it to void for EXISTS? to process. But...what if the routine were called DOESN'T-EXIST?, and it followed VOID-in, NULL-out? It would make it look like void inputs did exist, if you were just checking the result for truthiness or falseyness.
This seems like a pretty solid proof that functions returning LOGIC! should not conflate their answers with NULL. (Note: I know
exists? is currently conflated with FILETYPE OF, so it doesn't actually return a LOGIC!, but that's just a bug that hasn't been tended to. The point stands.)
I had a theory that VOIDs and NULLs could act as the quiet NaN and signaling NaN forms of "not-a-number" (NaN). The goal of this is to allow math handling to be more graceful, without needing to set up TRAPs and such--you can be selective about which operations you are willing to have fail, and supply code to fill in such cases.
Wikipedia has a little table about how NaNs work with comparisons:
Comparison between NaN and any floating-point value x (including NaN and ±∞)
- NaN ≥ x => Always False
- NaN ≤ x => Always False
- NaN > x => Always False
- NaN < x => Always False
- NaN = x => Always False
- NaN ≠ x => Always True
Look at that last case. If VOID is the quiet NaN, you can't have that comparison returning NULL, because it would be falsey instead of truthy.
When these routines get VOID they have to decide whether to return true or false depending. It's a close analogy to how "exists?" and "doesn't-exist?" must use their discretion on blank input.
However, the math operations that normally return numbers, and feed into these situations DO follow blank-in-null out. This is the proposed behavior:
>> square-root -1 ; Note: `square-root void` is also null == ~null~ ; isotope >> maybe square-root -1 ; void >> 1 + square-root -1 ** Error: + doesn't accept NULL for its value2 argument >> 1 + (square-root -1 else ) ; selective handling == 11 >> 1 + maybe square-root -1 ; propagation == ~null~ ; isotope >> 10 != (1 + try square-root -1) ** Error: != doesn't accept NULL for its value2 argument >> 10 != (maybe 1 + maybe square-root -1) == ~true~ ; isotope
So that demonstrates a bit of nuance involved in the "VOID-in, null out" rule. LOGIC!-bearing routines should still only return LOGIC!, and if for some reason they can't make a reasonable call one way or another, they need to error vs. ever returning NULL.