This comes up every couple of years or so. Getting rid of variables being able to have an "unset state" doesn't help...it only hurts.
Let's say I define an object with some function as an optional member, and the only options you give me are "it's not there" or "it's there and it's none". I get the choice of having references either error like I never defined anything at all... or if I mention it in the unused state, the none
will pass right by in the evaluator without an error. Not good.
And what of things like this?
for-each [x y] [1 2 3] [
probe :x
probe :y
]
Does it make sense for error messages to not be able to distinguish between the case of not being defined at all, ever... or holding a value that makes this conflate with if the block had been [1 2 3 #[none]]
so you can't tell the difference?
HOWEVER... this highlights that getting rid of "unset!" as a state that can occur in blocks is VERY helpful. That was actually one of the first things Ren-C did (and Red would benefit greatly from taking at least this step).
(Note that I call the unset state "trash!" because I think it makes more sense to say "variables are unset" vs. "values are unset".)
Here's a bit of terminology comparison with Rebol2/Ren-C/Red:
"Unset" vs. "Unresolvable"
For first level picks out of objects, Red doesn't seem to make the unset/unresolved distinction in the same way Rebol2 or R3-Alpha do:
red>> obj: make object! [set/any quote x: #(unset)]
== make object! [
x: unset
]
red>> unset? :obj/x
== true
red>> unset? :obj/asdf
== true ; Rebol2 and R3-Alpha error here
(Note: Red's SELECT is inconsistent here: (select obj 'x) gives an unset, while (select obj 'asdf) gives none)
But if you were to extend the path you'd get an error, which I would say comes from "lack of resolution"... so at least that's still "unresolved".
red>> unset? :obj/asdf/jkl
*** Script Error: asdf is unset in path :obj/asdf/jkl
Clearly, You Can't Get Rid of "Unresolvedness"!!!
Hence when people speak about when wanting to "get rid of unsetness", what they actually want is:
-
turn situations where you would "legitimately want an unset" into unresolvedness
-
declare any other situation "illegitimate/unnecessary" and use a NONE!.
By definition, nothing you can put in a variable can directly denote unresolvedness. All you can do is query about resolvability. If you get a state back and store it in a variable, then what you store is either conflated or a "meta-representation".
To survey the options, imagine:
- UNRESOLVABLE? => gives you a logic
- GET-AS-NONE-IF-UNRESOLVABLE => a conflating operation
- GET-AS-BLOCK-IF-RESOLVABLE => some meta-operation
So:
>> unresolvable? 'obj/asdf/jkl
== true
>> get-as-none-if-unresolvable 'obj/asdf/jkl
== none
>> get-as-block-if-resolvable 'obj/asdf/jkl
== none
>> obj/x: 1020
>> unresolvable? 'obj/x
== false
>> get-as-none-if-unresolvable 'obj/x
== 1020
>> get-as-block-if-resolvable 'obj/x
== [1020]
>> obj/x: [10 20]
>> get-as-none-if-unresolvable 'obj/x
== [10 20]
>> get-as-block-if-resolvable 'obj/x
== [[10 20]]
>> obj/x: none
>> get-as-none-if-unresolvable 'obj/x
== none ; conflated
>> get-as-block-if-resolvable 'obj/x
== [#(none)] ; not conflated
(Ren-C is more elegant w.r.t. meta-operations, but conceptually similar.)
You might feel vindicated looking at that, to think "getting rid of unset" makes sense. Because you're in the same boat whether you have unset or not. Queries and meta-representations are always going to be the tools of last resort.
So why introduce another tier in this hierarchy for "resolvable, but initialized to trash"?
Technically Possible To Drop Unset, but Flying Blind
The people who promote the "no unsets, only unresolveds" would say that if you can enumerate something (such as an object) that everything a key handed back maps to should be friendly... e.g. at least a none. Your binary choice is: it's there and it's initialized to something that you can access without raising an error, or it's not there to find in the first place.
Being able to define an object with slots in it--including slots for functions--where those slots hold "ornery" content on WORD!-fetch is an extremely useful invention. So useful that Ren-C made its TRASH! able to hold a message, so you can be more descriptive about why something is unset.
"Defined but not holding a meaningful value" is useful. You shouldn't throw the baby out with the bathwater... stop the unset state from getting into blocks, but keep it as a useful tool for variables.