In historical Redbol's meaning of the datatype NONE!, it had the bad habit of looking like a WORD!:
rebol2>> 'none
== none
rebol2>> none
== none ; same in R3-Alpha and Red
But it wasn't a word:
rebol2>> type? 'none
== word!
rebol2>> type? none
== none!
It was a distinct type, which also happened to be falsey (while WORD!s are truthy):
rebol2>> if 'none [print "Truthy word!"]
Truthy word!
rebol2>> if none [print "Falsey none!"]
== none
And as we can see, NONE!s served purposes of signaling "soft failures": branches that didn't run, or FINDs that didn't find, or SELECTs that didn't select... etc.
rebol2>> find "abcd" "z"
== none
rebol2>> select [a 10 b 20] 'c
== none
Ren-C Divided NONE!s roles across NULL, VOID, and BLANK!
-
NULL - an "antiform" state of WORD! that couldn't be put in BLOCK!s. Anywhere that NONE! would be used to signal a soft failure operation--like FIND or SELECT--would use ~null~.
>> null == ~null~ ; anti >> find "abcd" "z" == ~null~ ; anti >> select [a 10 b 20] 'c == ~null~ ; anti >> append [a b c] null ** Error: APPEND doesn't allow ~null~ isotope
-
BLANK! was represented by a lone underscore (
_
) and could be put into blocks:>> append [a b c] _ == [a b c _]
At the outset, it retained the choice to be falsey:
>> if _ [print "Won't print because blanks are falsey"]
-
VOID - another "antiform" state of WORD!, that's the result of things that are effectively "no ops". Some contexts choose to make them vanish, and when functions like APPEND get them as an argument they are treated as no-ops:
>> void == ~void~ ; anti >> if null [print "Doesn't print as NULL is falsey"] == ~void~ ; anti >> compose [abc (if false ['def]) ghi] == [abc ghi] >> append [a b c] void == [a b c] >> for-each void [1 2 3] [print "no variable"] no variable no variable no variable
Question One: Should BLANK! Just Be A WORD! ?
Ren-C allows you to use underscores internally to words, so it feels a little bad to take away one word.
Outside of historically being hardcoded as falsey, what makes BLANK! fairly "built in" is that in the path mechanics, it fills in the empty slots:
>> to path! [_ a]
== /a
>> as block! 'a/b/c/
== [a b c _]
There's other places the blank is used, such as to opt-out of multi-returns.
>> [_ value]: transcode/next "abc def"
== " def"
>> value
== abc
Question Two: Does BLANK! Still Need To Be Falsey?
My feeling is that having blank be falsey doesn't have all that much benefit. NULL does a better job of it, and really what it does is mess with its usefulness as a placeholder:
>> append [a b c] maybe all [1 > 2, 3 > 4, _]
== [a b c]
>> append [a b c] maybe all [1 < 2, 3 < 4, _]
== [a b c _] ; this makes sense to me
Thinking of BLANK! as being "null-like" in terms of non-valuedness is generally a hassle. It makes you wonder about whether something like DEFAULT should think of it as being assigned or not:
>> item: _
>> item: default [1 + 2]
== ???
In practice, I prefer only non-array-element things (NULL, NOTHING, etc.) being the only cases that DEFAULT overwrites. This is because NULL is far more useful than BLANK! when it comes to representing something that you think of as "not being assigned"... as you'll get errors when you try to use it places (e.g. in APPEND). Trying to use it to represent nothingness invariably leads to stray appearances in blocks (Shixin wrote a lot of code to try to filter them out in Rebmake, prior to it being switched to NULLs)
This makes more sense, and I think it bolsters the argument that BLANK! is less of a falsey-NULL relative...but more of a placeholder value. I've said "blanks are to blocks what space is to strings". And space is truthy:
>> if second "a b" [print "Space is truthy"]
Space is truthy
>> if second [a _ b] [print "So why shouldn't blank be truthy?"]
???
So Either Way, I Suggest The Removal of BLANK! From Being Falsey. This creates some incompatibility in Redbol (which has been using NONE! as a blank substitute). But it's something that can be worked around.