Rebol made an unusual choice in deciding that all things that "look like" words would be words, and reassignable. So true and false were kept following the general rules of words. Including that all words are truthy.
>> code: [flag: false]
>> second code
== false
>> if second code [print "the word false is truthy"]
the word false is truthy
There was a LOGIC! type, and it could be made via the #[true]
and #[false]
notation:
rebol2>> code: [flag: #[false]]
rebol2>> if not second code [print "the literal #[false] is falsey"]
the literal #[false] is falsey
So the default definitions are true: #[true] and false: #[false]. But the rendering would conflate with the word, despite not being a word:
rebol2>> code: [flag: #[false]]
rebol2>> code
== [flag: false]
rebol2>> type? second code
== logic!
rebol2>> true
== true
rebol2>> word? true
== false
There was puzzling about wondering what a better notation for LOGIC! literals might be. Considerations included things like $true and $false, among others.
I wanted to see there be $word $(gr o up) $[bl o ck] $tu.p.le $pa/th
as additional pieces in the box of parts...so sacrificing $ for this wasn't appealing to me.
Hence for a long time, Ren-C just rendered them as #[true] and #[false].
Rebol/Red's Bad Rendering Reveals a Subliminal Truth
As it turns out, a lot of places where you're building up structures you don't want an ugly literal (however it looks). For a lot of scenarios you want to reconstitute the word.
When isotopes came on the scene it afforded the interesting choice to say that the logic-reactive ~true~ and ~false~ antiforms couldn't be put into blocks... and would have to be triaged.
>> false
== ~false~ ; anti
>> append [flag:] false
** Error
>> append [flag:] meta false
== [flag: ~false~] ; evaluates to the right thing under DO
>> append [flag:] logic-to-word false
== [flag: false]
All BLOCK! Items Truthy, Out-of-Bounds NULL
This gave another benefit, which is that the null returned from out-of-bounds access of arrays gives the unique falsey result for various enumerations. For example:
>> block: [a b ~false~ c]
>> while [value: try take block] [print mold value]
a
b
~false~
c
Or:
>> block: [a b ~false~ c]
>> third block
== ~false~
>> if third block [print "There's a third element in block"]
There's a third element in block
>> fifth block
== ~null~ ; isotope
>> if not fifth block [print "No fifth element in block"]
No fifth element in block
These kinds of scenarios present classic problems in Rebol and Red, because people will write code assuming that they can use conditional logic to decide if a value is there... but then one day they hit a LOGIC! or a NONE! literal and it breaks. Having nothing that's actually in a block be falsey is a good thing.
Eventually, ~false~
=> ~null~
and ~true~
=> ~okay~
While antiform LOGIC showed benefits, it didn't show benefit to having a separate ~false~
type from ~null~
. The real value of TRUE and FALSE came from being the recognizable words themselves. Which weren't necessarily better than other choices (like ON and OFF, or YES and NO).
So the system migrated to something called "Flexible Logic". This introduced a new antiform complement to NULL called OKAY, and focused on the conversion between these forms and words when block representations were necessary.
No Answer is Perfect, But This Has Solid Benefits
The need to store things in blocks that are themselves directly testable as falsey isn't all that valuable in practice. And it frequently led to broken code when people were assuming a conditional test could be used to know whether an element was in a block or not.
Encouraging discipline in triage with whether you want a word or a meta-representation of a logic (which evaluates to something that has the "branch triggering" or "branch inhibiting" property) has--in my opinion--turned out to be a net benefit.