NULL Isotope Naming Issues

I'm moving ahead, quickly, with NULL isotopes.

Firstly I'll say that the analogy to atomic isotopes is an extremely apt one. So this isn't likely to just be a codename--it would be user-facing terminology: "isotope", "decay", "heavy null", "light null".

For background reading on precedent for names and notation, check out the Wikpiedia Article on Tritium. (I'm linking Tritium instead of Deuterium because it is radioactive, while Deuterium is stable.)

No matter what notation is chosen, I want the console to show which kind of NULL you have. If we want to help reinforce the "counting the number of nullons", maybe it's done with a number:

>> if false [<whatever>]
; null

>> if true [null]
; null-2

More fanciful representations are possible:

>> if false [<whatever>]
; ¹null

>> if true [null]
; ²null

But if there's going to be a function that generates these isotopes out of thin air, it should probably be easy to type:

>> null then [print "This won't print."]
; null

>> null-2 then [print "This prints."]
This prints.
== ~void~

The canon property of the isotope is reactivity to THEN and ELSE, which suggests that rather than convoluted names for the tests (like null?/light) we can do better with that:

>> else? null
== #[true]

>> else? null-2
== #[false]

Grokking The "Gotcha" In The Mechanic

I want to point out what we're trading off here. We're making what seems to be 99% of cases easier to handle, while creating a small glitch in substitutability:

>> (x: if true [null]) else [print "As likely desired, this won't run."]
; null-2

>> x else [print "But going through variable substitution WILL run."]
But going through variable substitution WILL run.
== ~void~

>> x
; null   <- here is why: WORD!-fetch decayed it, so no longer null-2

A little strange, but explainable. And when you compare to the headaches that voidification brought, this is a much more minor amount of pain to bear.

Notice that even in the first case, passing through assignment to a SET-WORD! didn't disrupt the value on the evaluator stack.

So... With That Laid Down... Naming?

I actually first wrote NULL+ ... but quickly moved away from that, because that suggests charge, and we're deliberately talking about isotopes and not ions. The distinction is important, because the "superficial reactivity" is unchanged:

>> null? if true [null]
== #[true]

>> null? if false [<whatever>]
== #[true]

So I thought maybe NULL* to indicate some kind of...uncharged augmentation. @BlackATTR thought that looked kind of wildcard-y.

NULL-T or T-NULL could imply it is "THEN-reactive"

>> null-t then [print "causes then to run..."]
causes then to run...
== ~void~

>> t-null then [print "causes then to run..."]
causes then to run...
== ~void~

I actually am kind of drawn to the small leading decoration for the heavy null, with nothing on the "light" null:

>> if false [<whatever>]
; null

>> if true [null]
; ²null

What I like there is the concept that it kind of fades out unless you need to pay attention to it.

I'm probably just going to go with NULL-2 for the moment, with the aforementioned ELSE? test to check if a value is a NULL-2.


"The essence of architecture is the suppression of information not necessary to the task at hand, and so it is somehow fitting that the very nature of architecture is such that it never presents its whole self to us but only a facet or two at a time."

2 Likes

So the update now is that there is no separate datatype for "null isotopes". Rather, the only things that have isotopes are BAD-WORD!s.

Like other BAD-WORD!s, evaluating ~null~ makes it into an isotope.

>> ~null~
== ~null~  ; isotope

This isotope is also the product when branch is taken and evaluates to NULL:

>> if true [null]
== ~null~  ; isotope

Most BAD-WORD! isotopes can't be passed to the condition of IF at all, because it's not a ^META parameter. So the question of whether they are "truthy" or "falsey" does not apply, since all the tests for truth don't allow isotopes to be passed.

But the ~null~ isotope is special, because it converts itself into a plain NULL when passed to a non-^META parameter:

>> normal: func [x] [probe x, return none]

>> normal 1020
== 1020

>> normal null
; null

>> normal ~null~
; null

A ^META parameter can tell the difference; removing the isotope state (and leaving the null as-is):

>> detector: func [^x] [probe x, return none]

>> detector 1020
== '1020

>> detector null
; null

>> detector ~null~
~null~

So the reason why the ~null~ isotope appears to be falsey is because of this translation! It's not actually falsey at all, but all the logic detecting functions take normal parameters so they see it as a plain null...

>> did ~null~
== #[false]

>> did if true [null]
== #[false]

The same reason that NULL? can't tell the difference...it doesn't take a meta parameter, so the ~null~ isotope looks like NULL to it:

>> null? ~null~
== #[true]

>> null? if true [null]
== #[true]

The non-isotope form of ~null~ is neither true nor false, like other BAD-WORD!s:

>> second [a ~null~ b]
== ~null~

>> did second [a ~null~ b]
** Script Error: BAD-WORD! values aren't conditionally true or false

ELSE, THEN, ALSO, etc. Detect via ^META Parameters

We saw above that if you take a ^param instead of a normal param, you have the information to distinguish. That's exactly what ELSE does with its left hand side.

>> null else [print "This runs!"]
This runs!

>> ~null~ else [print "This doesn't!"]

>> ~null~ then [print "But this does!"]
But this does!

>> if true [null] else [print "This won't run!"]
== ~null~  ; isotope

Hopefully if you're following enough of this it's starting to make sense. :slight_smile:

So Just Call It "The Null Isotope" or "~null~ isotope"

There might be some creative writing somewhere that refers to it as being the "heavy" form, but that's not really necessary.

Terms like NULL-2 are not needed.

1 Like

This concession arose from a desire to try to appease people like @rgchris who believe they do not need ELSE or THEN, to sweep the isotope under the rug.

Looking at the ripples of inconsistency this creates, I think the learnable/better solution is just to give you a tool to turn isotopes that are representative of values back to the value they are an isotope of.

>> match blank! _
== ~blank~  ; isotope

>> decay match blank! _
== _

>> if true [null]
== ~null~  ; isotope

>> decay if true [null]
; null

You still get the decay on variable assignment:

>> x: if true [null]
== ~null~  ; isotope

>> x
; null

And also on RETURN by default (the RETURN/ISOTOPE will suppress the decay).

>> foo: func [] [return if true [null]]
>> foo
; null

>> bar: func [] [return/isotope if true [null]]
>> bar
== ~null~  ; isotope

Having to use DECAY to pass ~null~ isotopes to normal parameters is a bit of a thorn. But the trouble is that if we subvert it, we lose the benefits isotopes are there to provide.

Consider this example of something that would happen in less contrived circumstances:

 data: ["a" b <c>]

 until [
     item: take data
     if word? item [
         item: null
     ]
     match [<opt> integer!] item
 ]

It looks like we're trying to end the loop once the item is either null or an integer. But NULL is falsey, so MATCH needs to give back a ~null~ isotope.

Some contexts can treat a null isotope like a NULL on a case-by-case basis. This shows one that can't: UNTIL actually cares about the intent of truth or falsehood. The existence of a null isotope is to keep the "null intent" while disabling its participation in things that might react to it as "falsey" or "branch not taken"

For any given function parameter, you can't tell whether it matters automatically. The condition in an IF needs to react differently to a ~blank~ from a BLANK!, but a REDUCE doesn't have to.

>> reduce [match [integer! blank!] _]
== [_]  ; reduce can safely convert ~blank~ isotopes to _

The REDUCE knows it's not looking at the truth or falsehood, so it can restore the blank. But an IF has to error:

>> if match [integer! blank!] _ [print "needs to error!"]
** Error: IF doesn't allow ~blank~ isotope as its condition parameter

The Grand Theory Of Isotopes is Coming Together

Isotopes represent special cases of communicating intent, while saying that the value is "in limbo". The default reaction to things in limbo is to error on them. But if you know how to handle them, you can.

I'll write it up in more detail with some of the truly interesting examples of problems they solve. But the upshot is, ~null~ is like all the other bad-word isotopes, and neither true nor false!

2 Likes