Should VOID Assigns Mean "Fully Remove A Key"?

Rebol2 and R3-Alpha distinguish between fields not in an object, and those that are in the object but unset:

rebol2>> obj: make object! [x: "unsetme"]

rebol2>> unset bind 'x obj

rebol2>> >> print mold obj
make object! [
    x: unset
]

rebol2>> unset? obj/x  ; asks if obj/x is an "UNSET! value"
== true

rebol2>> unset? obj/asdf
** Script Error: Invalid path value: asdf

Ren-C has this distinction today, as well.

>> obj: make object! [x: ~]

>> unset? $obj.x  ; asks if obj.x looks up to a TRASH! value
== ~okay~  ; anti

>> unset? $obj.asdf
** Error: Cannot pick asdf in obj

(Red says both obj/x and obj/asdf are "UNSET?", so you have to ask something like (unset? obj/asdf/jkl) before you get an error.)

This Is the "Unresolved" (vs. "Unset"?) Distinction

I think UNRESOLVED? is a pretty good name for the test saying there's no variable location to even find for something to ask about its state.

>> obj: make object! [x: ~]

>> unresolved? $obj.x
== ~null~  ; anti

>> unresolved? $obj.asdf
== ~okay~  ; anti

I don't know if the UNRESOLVED? question should accept multiple steps of not being resolved. Perhaps there's a refinement...

>> unresolved? $asdf.jkl.qwertyiop
** Error: Multi-step unresolved, use UNRESOLVED?:MULTI if intended

>> unresolved?:multi $asdf.jkl.qwertyiop
== ~okay~  ; anti

Beyond that, it's a little tricky to cover all the states related to "unsetness". There's TRIPWIRE! and TRASH! which both generate errors on reads, and currently both count as UNSET?. Then DEFAULT will overwrite null variables as well...so that's DEFAULTABLE? (void variables aren't really understood as to whether default should overwrite them).

I don't know. But I do think that terms like UNSPECIFIED?/UNRESOLVED?/UNDEFINED? all kind of cluster together... and among them UNRESOLVED? stands out to me as the best one to draw from for saying there really is no variable location at all.

Distinction Is Hazy For Things Like MAP!

Originally I had it so you would remove keys from MAP! by setting them to ~null~ antiforms.

Later I decided it would be more consistent if you asked to "remove" them by setting them to trash, so they'd be unset and you'd have to TRY to access them.

But removal from maps removes them from enumeration. This is like being "unresolved", not like being "unset".

A similar issue happened with the ENVIRONMENT! type for modeling environment variables. Removing with trash seems wrong.

Wild Thought: VOID (empty PACK!) To Mean REMOVE ?

Empty PACK! (now called "VOID") is an unstable antiform used to opt out of things like COMPOSE

But what if it was the way to ask for a removal from a MAP?

 >> m: to map! [a 10 b 20]

 >> m.a: void
 == ~[]~  ; anti

 >> m
 == #[map [b 20]]

This would only be usable on datatypes where removals were legal... such as MAP! and ENVIRONMENT!. You can't remove cells from a FRAME!--they're in fixed positions that have been compiled in. Today's OBJECT! doesn't allow removals either (though perhaps it could/should?)

The rule for accepting an unstable assignment of this form would be that this removes the item from enumeration entirely. Whereas if you assigned with a TRASH! the understanding is you're just making something that errors on access, but would still be in an enumeration of available keys.

Strangely Compelling Use Of Unstable Antiforms

This does feel like it pushes to a clearer resolution of my misgivings of the incorrect parallels between map/environment removals and trashing object/frame cells... in terms of the effects on enumeration.

1 Like