This wasn't always the case...
Originally, ANY-VALUE! did not include NULL
Typesets were implemented as bitsets based on the cell type byte. Nulls had a distinguished type byte of zero. Then the corresponding bit for null was not set in ANY-VALUE!.
So an ANY-VALUE! was considered everything but a null. If you wanted a function to be able to take a null argument, you'd have to say [<opt> any-value!]
Back then I claimed that NULL was a "non-valued state", with the terminology that "it's not a value, because you can't put it in a block".
Nevertheless, the variables at the time could hold nulls. This runs up against my new competing terminological idea: "values are anything you can put into variables."
Excluding Isotopes By Default Seems Appealing
When users reach for a type constraint to say "I purposefully mean to accept many values", it's nice if isotopes aren't included in that.
NULL exclusion is a good one, with a historical basis. Much of the reason for existence of NULL is that it's rejected by most functions (outside of things like IF which are deliberately testing for nulls). Functions generally shouldn't take them.
Another big-new-cool reason is that it would exclude "ACTIONs". If you can't get the activated form of an action by default, your code doesn't have to be paranoid about putting in GET-WORD!s everywhere to disable them.
(NONEs are already excluded from normal parameters now, you have to use a ^META parameter to get them.)
Excluding isotopes would exclude things like logic ~true~ and ~false~, along with splices and other things. I don't have a problem personally with routines that care about logic needing to explicitly add LOGIC? to the type constraints.
The Code Sticks Us With Value*'s Meaning
All things being equal, I might say that "variables can hold values -or- stable isotopes (or voids)" would be good for being able to say ANY-VALUE? instead of ANY-ELEMENT?
But 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".
The implication here is that all the places that say [<opt> any-value?]
right now are redundant...that ANY-VALUE? includes null, and if you want to exclude nulls you have to use ANY-ELEMENT? and add in the isotopes you want from there.
Biggest Cold-Feet Issue: ACTION Isotopes By Default
I started reviewing this point when I was right on the brink of turning all the [<opt> any-value?]
into [any-value?]
and just moving along...saying what's done is done.
But this means you can be receiving something that will execute when you reference it by word...as a common default.
A bit part of me wants to say that functions won't accept frame isotopes (e.g. actions) and you have to ^META them, the same way you have to do with void isotopes representing unset values.
But features break. COLLECT is implemented by passing the block you give it to a function with an argument KEEP, and passes the keeper function as that parameter. The function builder takes care of the binding. I also use the feature in UPARSE, where the incoming PARSERs are live actions as well as parameters.
Some comfort can be taken that at least this is a place where you have a type block to enforce the policy of your choosing. What worried me more about things like code enumerating blocks is you didn't get that choice.
Long Story Being Short: ANY-VALUE? Includes Isotopes
It does make me wonder, though, if a different parameter convention for accepting "live" actions is needed. GET-WORD! was once proposed as a good visual cue:
foo: func [value [any-value?]] [...] ; would reject ACTIONs despite ANY-VALUE?
bar: func [:value [any-value?]] [...] ; accepts the live action
This would attack the problem without saying that a live action wasn't an ANY-VALUE... it would just be prohibited by some other rule (much like how NONE is prohibited for another reason contextual to parameters).
But that's a separate design question, and for now the definition stands:
A "Value" is any state that can be stored in a variable.