Should ANY-VALUE! Include Stable Antiforms?

If you don't put any type restrictions on a parameter, it will allow antiforms:

 >> foo: func [x] [probe x]

>> foo null
~null~  ; anti

And right now, that is a full synonym for ANY-VALUE!.

 >> foo: func [x [any-value!]] [probe x]

>> foo null
~null~  ; isotope

But what if you want to exclude antiforms, and only accept things that can legally be put in blocks?

We could have a name for the typeset excluding antiforms. ANY-ARRAY-ITEM! would be pedantically explicit, but seems accurate enough.

Or we could say that ANY-VALUE! doesn't include antiforms by default, and so you have to write [any-value! antiform!] if you want them. Or just include the specific ones you intend (like [null! any-value!]).

However, the code has a meaning for Value*, and it really is pretty much literally "the kind of state that can be held by a variable". So I think we need another name.

1 Like

In the mechanics of the implementation, I use the name "Cell" as the base class of "Value".

This was originally a distinction that was made for binding. A "Value" was something that combined a raw array cell with the information needed to look things up in it. (e.g. a Cell + a FRAME!). If you had just a WORD! Cell* in your hand, you could not ask to look up its variable... you had to combine it with a "Specifier" to get a Value* first.

Cells are the base class of lots of things. When you have an antiform ERROR!, that's in something that derives from Cell as well. Hence I don't think ANY-CELL! is a good name for ANY-ARRAY-ITEM! The implementation shouldn't be using words incongruently from usermode.

Perhaps Element is a good name for things you can put in array slots?

I'm scratching my head over the usefulness of an Element subclass in the implementation itself (beyond the exposed typeset). One of the problems is that Rebol guts are based on using common array routines for objects and function frames...and so even common-sense-sounding ideas like "you can't just increment a pointer to a variable to get to another variable" are subverted all over the place.

I'll have to keep thinking on it, but at least there's a bit of progress with the "element" term. So append would accept [void! any-element! splice!]... with void resulting in a no-op, element adding a single item, and splice adding an array itemwise.

1 Like

Ultimately I deemed it critical to invent mechanics to stop writes of antiforms into array cells (or of unstable antiforms into variables).

So I broke down the implementation into Cell, Atom, Value, and Element. Cell is an internal concept that doesn't correspond to any direct terms in userspace. But ANY-ATOM?, ANY-VALUE?, and ANY-ELEMENT? are exposed and correspond to the implementation subclasses.

The C++ build does helpful type-checking to try and make sure everything lines up. But you can compile the sources as plain C also, which simply defines Atom/Value/Element as synonyms for Cell.

I showed the code to ChatGPT and explained what the motivations were. It made a summary good enough to put as a comment in the source (edited a bit):

//=//// CELL SUBCLASSES FOR QUARANTINING STABLE AND UNSTABLE ANTIFORMS /////=//
//
// Systemically, we want to stop antiforms from being put into the array
// elements of blocks, groups, paths, and tuples.  We also want to prevent
// unstable antiforms from being the values of variables.  To make it easier
// to do this, the C++ build offers the ability to make `Element` that
// can't hold any antiforms, `Value` that can hold stable antiforms, and
// `Atom` that can hold anything--including unstable isotopes.
//
// * Class Hierarchy: Atom as base, Value derived, Element derived
//   (upside-down for compile-time error preferences--we want passing an
//   Atom to a routine that expects only Element to fail)
//
// * Primary Goal: Prevent passing Atoms/Values to Element-only routines,
//   or Atoms to Value-only routines.
//
// * Secondary Goal: Prevent things like passing Element cells to writing
//   routines that may potentially produce antiforms in that cell.
//
// * Tertiary Goal: Detect things like superfluous Is_Antiform() calls
//   being made on Elements.
//
// The primary goal is achieved by choosing Element as a most-derived type
// instead of a base type.  The next two goals are trickier, and require a
// smart pointer class to wrap the pointers and invert the class hierarchy
// in terms of what are accepted for initialization (see Sink() and Need()).
//
// Additionally, the Cell* class is differentiated by not allowing you to
// ask for its "type".  This makes it useful in passing to routines that
// are supposed to act agnostically regarding the quoting level of the cell,
// such as molding...where the quoting level is accounted for by the core
// molding process, and mold callbacks are only supposed to account for the
// cell payloads.
//