What Are NULL, VOID, and TRASH?

F.A.Q.

How Did These Types Evolve From Rebol2 UNSET! + NONE! ?

See this thread.

Isn't It Simpler to unify VOID+TRASH as UNSET!, like Rebol2?

One can reasonably ask if when all is considered, having fewer parts is better--even when more parts can be shown to have "a benefit here and there".

So this does hinge on how you feel about things like:

rebol2>> compose [a (print "b") c]
b
== [a c]

Personally I see too much downside to conflating "meaningless" values with "meaningful opting out". While you might be able to make excuses for an oversimplified example that's just PRINT like this one, when you imagine a more complex expression, an error is far preferable.

So in Ren-C you have to erase the trash value, with something like:

>> compose [a (elide print "b") c]
b
== [a c]

Might ~ Antiforms Be The Better Representation for Void?

Given how tilde is just about as light of a token as you can get, one might think the vanishing intent deserves that notation. There could be a switcheroo where ~trash~ was used for the unset variable state, and ~ was used for void.

But in practice, unset variables are far more common... and the super-light notation for creating an unset variable as (var: ~) is just too useful.

Could TRIPWIRE! Replace TRASH?

Ren-C has another type that causes errors on variable access, which is the antiform TAG!, a.k.a. TRIPWIRE!

>> var: ~<some message>~
== ~<some message>~  ; anti

>> var
** Error: var is a tripwire: some message

So it may seem redundant to have antiform blanks doing the same thing. If everywhere that produces TRASH! today made TRIPWIRE!, then antiform blank could more readily be repurposed as VOID.

Mechanically, yes, this can be done (I tried it). But so many of the cheap uses of ~, e.g. unsetting variables... or return: [~] in specs to indicate no result have to be reimagined. There's a rather deep dependency on antiform ~ being an ornery state. Believe me: if I could get rid of one of these forms, I would.