Clarity + Brevity vs. NULL, BLANK!, TRASH, VOID

(2022 note: This question from 2019 and response were lifted out of a thread that was otherwise outdated enough to warrant deleting...trying to update the answer.)

@hostilefork says:

I think any illusions of Ren-C being able to bring evaluator "safety" to a fundamentally unsafe language are being stripped away with time. Instead, what's actually happening is that the features are getting laser-calibrated to where an informed programmer can write short, correct, code easily... the right thing is the short thing, not something riddled with edge-case handling.

Clarity is a catalyst for safety in a language. But beyond that, the bias seems tipping ever toward being clean and powerful enough to "make your own you-made-a-mistake-catching-safety mechanisms"...

Clarity and brevity are two fundamental qualities of good writing. I think that applies to programming as well. Also 4 concepts so close (NULL, BLANK!, NONE! and VOID), make me look for a fundamental problem in the language, or they do not have adequate names.

I do not quite understand the usefulness of these 4 variants but should not there be one, or several but named to make understand to which functionalities of the language they relate (block, value, refinement ...)? Maybe I get it wrong.

NONE! was a Rebol2-ism (along with UNSET!), and does not come up in Ren-C except in "Redbol" emulation. Rebol2's NONE! and Ren-C's BLANK! were supposed to be quite similar, just a different name. But eventually NULL has taken over many of the duties of NONE! as an indicator of "soft failure" from a when a FIND can't find anything, or a conditional statement doesn't run any branches. BLANK! sticks around as the solution to the problem of neutral placeholders in blocks.

So what I describe is how these two ideas (NONE!/UNSET!) have become four:

  • NULL has properties unlike anything in Rebol2. It cannot be put in a BLOCK!. It is a transitional state representing nothing, like how a C null pointer doesn't point to any memory. It is conditionally false if it appears in something like an IF statement, and it is the "soft failure" result indicating something didn't succeed e.g. a FIND that couldn't find what you were looking for, or a loop that BREAKs.

  • BLANK! is a unit type...e.g. a value of type blank holds no further information (than that it is of type blank). Unlike NULL it's something that can appear in a block, and hence can be used as a placeholder--for instance if a block represents a record structure broken into N-element units, it could represent a missing field. (Update: Values of this type were once conditionally false, but it was later deemed better if they were not.)

  • Today's idea of VOID--as promoted by rgchris--is as the absence of a value. If you write append [a b c] void you will get [a b c] back (null would have given an error). It is result of any control construct that does not run a branch.

  • TRASH replaces "unset" as the contents of an unset variable (in Ren-C's parlance, variables are unset, not values.). Like null and void, it also cannot be put in blocks.

I think the names work pretty well. Particularly pleasing is that NULL is exposed in the API as C and JavaScript's NULL. That has turned out to be very important.

Rebol2 always had trouble with #[none]-the-unit-value and none-the-word, frequently rendering it as looking like the word:

rebol2>> find [a b c] 'd
== none

As mentioned, Ren-C does that with NULL now...which is better.

But reserving plain old _ so it's the literal form for a placeholder...and having it be named BLANK!...has felt pretty good. But we're still in that stretch of time for evaluating decisions for their merit...