Prior to the existence of isotopes--when only VOID and NULL existed as outliers--I had an idea 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.
Original Idea: Math Ops Follow VOID-IN-NULL-OUT
This was the proposed behavior for NULL as signaling NaN and VOID as quiet NaN:
>> square-root -1
== ~null~ ; anti
>> maybe square-root -1
== ~void~ ; anti
>> 1 + square-root -1
** Error: + doesn't accept NULL for its value2 argument
>> 1 + (square-root -1 else [10]) ; selective handling
== 11
>> 1 + maybe square-root -1 ; propagation
== ~null~ ; anti
But I Don't Like VOID as Quiet NaN (and It Breaks Compares)
I prefer that when void arguments are received by math functions, they return the other operand (with the exception of void divisors)...not a noisy NaN.
>> 10 * if 1 > 100 [20]
== 10
Also, 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...so VOID-in-NULL-out breaks down here:
>> 10 != (1 + try square-root -1)
** Error: != doesn't accept NULL for its value2 argument
>> 10 = (maybe 1 + maybe square-root -1)
== ~null~ ; anti ...not a LOGIC!, but it is falsey, so uh...perhaps? :-/
>> 10 != (maybe 1 + maybe square-root -1)
== ~null~ ; anti ...okay, now that's just wrong.
(I've made similar observations about VOID-in-NULL-out with LOGIC! returning functions before.)
Beyond not liking VOID for quiet NaN, I'm not hot on NULL being the noisy NaN either.
New Idea: Signaling is Error Antiform, Quiet is ~NaN~ Antiform
A lot of design space opened up with the introduction of generalized isotopes.
So I think signaling NaN should be an error antiform. And quiet NaN should probably just be the ~NaN~ WORD! antiform.
TRY typically converts antiform errors to NULL, so they don't promote to abrupt failures. But if TRY converted the specific NaN error antiform to the ~NaN~ word antiform, we could avoid having to come up with a special word for "MATH-TRY".
(This makes me think that quiet ~NaN~ word antiform should be falsey, so the usage pattern can line up with other null-bearing TRY instances...and I imagine MAYBE should also turn quiet ~NaN~ to VOID, for similar reasons.)
Anyway, here's how this would play out:
>> square-root -1 ; unhandled raised error return promotes to abrupt failure
** Error: Not a Number
>> try square-root -1 ; TRY intercepts raised error before it abruptly fails
== ~NaN~ ; anti
>> 1 + square-root -1
** Error: Not a Number ; was promoted to abrupt failure before + could see it
>> 1 + try square-root -1 ; TRY makes quiet ~NaN~, then + propagates
== ~NaN~ ; anti
>> 10 != (1 + try square-root -1)
== ~true~ ; anti
>> 10 = (1 + try square-root -1)
== ~false~ ; anti
>> 1 + ((try square-root -1) else [10])
== 11
>> 1 + any [square-root -1, 10]
** Error: Not a Number
>> 1 + any [try square-root -1, 10]
== 11
I Think That Looks Solid
Another day, another success story for isotopes. Definitionally raised error antiforms provide the mechanics to make an interceptible error by contract as a return value that isn't itself an abrupt failure, but promotes to abrupt failure when not anticipated.
And it's cool that we have another WORD! antiform state that has the properties we want, while not being able to be put in blocks... but which has a quasi state that can, if you need to pipe things around:
>> try sqrt -1
== ~NaN~ ; anti
>> block: append [1 2 3] try sqrt -1
** Error: append doesn't accept ~NaN~ antiform as its value argument
>> block: append [1 2 3] meta try sqrt -1
== [1 2 3 ~NaN~]
>> block.4
== ~NaN~
>> 10 + block.4
** Error: + doesn't accept ~NaN~ quasiform as its value2 argument
>> unmeta block.4
== ~NaN~ ; anti
>> 10 + unmeta block.4
== ~NaN~ ; anti
And heck, raise ~NaN~
could produce the error antiform, bringing things full circle.