The one point here though, is that switches tend to expand. Like new modes get added, to where you might expect there to be not just ON and OFF but some distinctions like HIBERNATING or whatever.
And when the instructions expand, the new instructions have to be quoted as words, for recycle 'infrequent
. Then you have the issue of just ON and OFF standing out as being able to be not quoted, and it's inconsistent.
This is something I wrote about a long time ago (2017):
So my fear is that once you go into something semantic like ON and OFF, that jump into the semantic realm may be just the beginning. And letting you not quote the words may in the long run lead to having an inconsistent interface when your semantics expand.
Back To Where This Line Of Thinking Has Always Led...
...ever since 2017 (and before), which is that there is no LOGIC! type, and you instead say:
some-flag: 'true
if true? some-flag [...]
And then what gets returned by TRUE? is either NULL or some canon "you should take the branch" value (I'll avoid calling that "truthy"). It could be rigged so that TRUE? and FALSE? accept only TRUE and FALSE words.
>> true? 'true
== # ; or whatever...as long as it's not ~null~ antiform (or ~NaN~ antiform...)
>> true? 'false
== ~null~ ; anti
>> true? 10
** Error: TRUE? and FALSE? tests only work on TRUE and FALSE words
>> logic? 'false
== #
>> logic? 10
== ~null~ ; anti
If trash were truthy, I can see advantages to making it the canon "run the branch" value. This means it's good enough for comparisons that aren't being stored in a value:
>> 10 < 20
>> if 10 < 20 [print "Good enough for this."]
Good enough for this.
But if you store it in a variable, it unsets it. That's a good way of saying you didn't triage it into some sort of reified state, which would be friendly to rendering and putting in blocks etc.
>> flag: 10 < 20
>> if flag [print "You triaged it into a meaningful reified state"]
** Error: Flag is ~ antiform (unset variable)
So instead you'd have to write:
>> flag: either 10 < 20 ['true] ['false]
== true
>> flag: to-logic 10 < 20
== true
>> flag: boolean 10 < 20 ; fewer characters, no hyphen
== true
>> flag: bool 10 < 20 ; abbreviation for those who want it
== true
Bummer here is still that if you leave off the TRUE? test in if true? then your FALSE state will run the branch, because all reified values are non-null. But with the interplay of LOGIC!s sometimes being words (or sometimes being quasiforms) you were always at risk of that. This just bites the bullet and tells you to learn the new and consistent way.
If you're starting from scratch learning the language you can be taught that IF just tests for non-nullness. That's all it does. If you know that's all it does--and it's ingrained to you from the beginning--then you will know that you have to say if blank? or if trash? or if true? or if false? etc. etc. You won't expect it to test flags.
Furthermore: if flag
now has a distinct meaning. It means "if the flag has been assigned a value--including either true or false". That is useful in its own right if one has learned it this way, and if you read it simply as "if flag is not null" it is actually quite obvious. When you're trained to read it that way, that's what you'll see it as.
A New Idea. But a Learnable One.
It appears to be the better long-tail path for a Rebol in which we've seen the tension between words and non-reifyable states (or undesirably ugly reifyable states) causing catastrophic consequences in the code.
-
It applies absolutely everywhere else (including BLANK? now, as it too causes branches to run).
-
There'd never be a confusion between the "fetched state" of a LOGIC! and the WORD! of the LOGIC because the WORD! is the logic.
-
The new "run the branch" signaling of TRASH turns the arbitrary choice of what logic expressions return for true into something non-arbitrary. It serves a purpose in calling attention to non-triaged results being used as variables.
-
if flag
becomes a new and interesting test, to help discern an unassigned flag (one that is null) from one that has been assigned a value (either TRUE or FALSE, or whatever else).- Someone learning Ren-C from scratch would learn that meaning and read it for that meaning, not expecting it to test for true or false.
-
Rendering the TRUE and FALSE "enum states" becomes as natural as any other word, and the states can expand without requiring a difference in handling.
-
You can put the currency we use for representing logic into a BLOCK!
-
With TRUE and FALSE being themselves "take the branch" signals, they can both drop out of things like ANY or ALL verbatim.
flag: update all [ threshold > maximum even? number-of-variants 'false ] ; result could never be false before, always null
-
We can dismiss with this question of ON OFF YES NO antiforms. All of them (as well as TRUE and FALSE) would be undefined.
And if people get exposed to things like either 10 < 20 ['true] ['false]
early on, it might train their brain to start thinking about all the possibilities Rebol has to offer them for expression. It's a pattern with a smooth slope toward getting more out of the representation and out of the language.
My 2017-Self May Have Known More Than My 2022-Self
The lingering feeling now seems spot on.
I think this is the answer.
I'll point out that we now type check quoted literal things.
machine-control: func [mode ['on 'off]] [
switch mode [
'on [print "Turning machine on"]
'off [print "Turning machine off"]
]
]
>> machine-control 'explode
** Error: machine control requires mode as ['on 'off]
We still might want a TOGGLE? type constraint, and functions ON? and OFF? which check to make sure you're only testing something that is the WORD! of ON or OFF... same for YES? and NO?