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~\ ; antiform
>> opt square-root -1
== \~,~\ ; antiform (ghost!) "void"
>> 1 + square-root -1
** Error: + doesn't accept NULL for its value2 argument
>> 1 + (square-root -1 else [10]) ; selective handling
== 11
>> 1 + opt square-root -1 ; propagation
== \~null~\ ; antiform
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 + square-root -1)
** Error: != doesn't accept NULL for its value2 argument
>> 10 = (opt 1 + opt square-root -1)
== \~null~\ ; antiform
>> 10 != (opt 1 + opt square-root -1)
== \~null~\ ; antiform
(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~\ ; antiform
>> 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~\ ; antiform
>> 10 != (1 + try square-root -1)
== \~okay~\ ; antiform
>> 10 = (1 + try square-root -1)
== \~null~\ ; antiform
>> 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~\ ; antiform
>> block: append [1 2 3] try sqrt -1
** Error: append doesn't accept ~NaN~ antiform as its value argument
>> block: append [1 2 3] lift try sqrt -1
== [1 2 3 ~NaN~]
>> block.4
== ~NaN~
>> 10 + block.4
** Error: + doesn't accept ~NaN~ quasiform as its value2 argument
>> unlift block.4
== \~NaN~\ ; antiform
>> 10 + unlift block.4
== \~NaN~\ ; antiform
And heck, fail ~NaN~ could produce the error antiform, bringing things full circle.