The concept of having isotopic ~true~ and ~false~ as WORD! antiforms instead of having a distinct LOGIC! type is admittedly somewhat weird. It meant they couldn't be put in blocks and need some sort of transformation if they're going to be put into blocks.
-
The generic and reversible way to make them real it is to META them and get
~true~
and~false~
quasiforms, which you can UNMETA back to the antiforms -
The readable way to make them real is LOGIC-TO-WORD them and get
true
andfalse
as plain WORD!, which only gets turned back into the antiforms if they are bound to variables that hold the antiforms, and you evaluate them. -
Your circumstance may mean that neither of those choices are what you want when in a situation where the antiforms can't work...maybe it means you have to rethink what you're doing, or maybe you want some other transformation. It's good to be alerted to the fact that it's dodgy to put what you think of as a "logic" in a reified context.
One should note that Lisp has no false at all, only NIL for false (since everything else is truthy, you could use anything else for true, but they have T predefined). But their NIL isn't an antiform--so it can appear in lists literally.
And as I've repeatedly pointed out, Redbol's historical choice to render LOGIC! conflated with the words true
and false
(instead of #[true]
and #[false]
) shows a desire to avoid the logic literals "escaping" into the reified consciousness of the user. Making the logic forms actually impossible to put into blocks without a conscious transformation feels like it's good.
I know it's a strange choice, but it's seeming like it fits with the territory. It's a sort of tradeoff you need when you choose to be firm that TRUE and FALSE are redefinable words and not lexical forms of logic constants.
NULL Was Once ~_~ Antiform, But Now ~null~
antiform
Once upon a time I called antiform BLANK! null. It was not pretty looking:
>> null
== ~_~ ; anti
>> _
== ~_~ ; anti
It was because I made BLANK! evaluate to its antiform. This meant if you assign a bunch of variables to null, it gave a pretty visibility to the actual values to pay attention to:
obj: make object! [
alpha: _
beta: _
delta: true
epsilon: ~
gamma: "nutty"
rho: _
omega: 'now
]
I was reticent about using the word antiform ~null~, because at source level we're writing:
obj: make object! [
alpha: null
beta: null
delta: true
epsilon: ~
gamma: "nutty"
rho: null
omega: 'now
]
And if we view it after evaluation that fetches (null
=> ~null~ ; anti
), it seemed uglier:
make object! [
alpha: ~null~
beta: ~null~
delta: ~true~
epsilon: ~
gamma: "nutty"
rho: ~null~
omega: 'now
]
We can't show it as the WORD! null because there's no guarantee that word always will evaluate back to the null isotope. But the concept was that since BLANK! can't be redefined, we can put it in an evaluative context as a substitute for ~_~
if we want.
If we were "more honest" and just META'd the antiform blank, we'd get renders like:
make object! [
alpha: ~_~
beta: ~_~
delta: ~true~
epsilon: ~
gamma: "nutty"
rho: ~_~
omega: 'now
]
Which I actually liked that less than seeing ~null~
there.
I was torn, but Chose The WORD! Antiform ~null~
Certainly having people be able to see ~null~ written out as a word corresponds to what we want to refer to the state as. We aren't going to replace people's vernacular to say "antiform blank" instead of null. And trying to teach people "antiform blank is null" is an uphill battle.
Let's look back at that seemingly beautiful situation with all the blanks-to-nulls at source level:
obj: make object! [
alpha: _
beta: _
delta: true
epsilon: ~
gamma: "nutty"
rho: _
omega: 'now
]
The ~
is now an assignable state meaning variable is not set. How often will you want to set something to NULL...which won't generate an error on access like being unset would, but is falsey and can't be passed to many routines without a MAYBE?
Statistically, we might often have a situation where NULL is the minority initialization, more like this:
obj: make object! [
alpha: ~
beta: ~
delta: true
epsilon: null
gamma: "nutty"
rho: ~
omega: 'now
]
My point is that intentionally initializing things to null may not be as common as setting to an error-provoking unset state. And when null happens it may be just as noteworthy to call out as setting something to true or false.
My general intuition was that from a user's standpoint, it's an uphill battle to teach them antiform blank is something called "null"... and that the majority of technical problems that are involved in making NULL be a word isotope are problems that have to be tackled anyway with true and false as word isotopes.
Additionally, I've written about my desire for BLANK!s to serve in dialects as spaces. That gets very screwed by the (_
=> ~_~
; anti) evaluation. Recovering blank for space intent and having them be unevaluated was good
Some Technical Difficulties of ~null~
It had some of the same problems as having to deal with the type checking of LOGIC! actually being an antiform subclass of two words. It means "NULL!" is an isotopic subclass of one word, so there's no specific null datatype.
Previously NULL cells held nothing, so I'd put a payload of the file and line that were in effect in the evaluator. This was supposed to give better errors about where a null originated from. This could still be done if NULL was a special case of WORD! antiform that said it was null via a flag vs. storing the symbol, but could be complicated. None of the better error mechanics were done yet.