At this exact moment...TYPE OF any isotope is an error, while both TYPE OF NULL and TYPE OF VOID give back NULL.
OF is a generic operation (the enfix form of REFLECT that quotes the word on its left. It may be desirable to honor the VOID-in-NULL out convention for all the other reflectors that aren't TYPE... and it's desirable to error on NULL more generically.
>> label of null ** Error: You didn't give anything >> label of maybe null ; MAYBE NULL is VOID ; null
So if type of follows the same pattern as other xxx of, we'd surmise that you don't use TYPE OF to discern NULL and VOID. It errors on NULL input and gives you back NULL if you MAYBE it.
But what happens when you ask:
>> spread [d e] == ~(d e)~ ; isotope >> type of spread [d e] ???
The Original Plan Was No Arguments Received Isotopes
In the original conception, function frames weren't capable of holding isotopes in the arguments. You physically could not receive a parameter that was an isotope.
I was also looking at the idea that some isotope forms--such as isotopic ERROR!--would be completely impossible to get into a variable, ever.
The only workaround was if a function used the ^META parameter convention, in which case an isotope would come in as a QUASI! form of the value...while normal values would come in as quoted:
>> detector: func [^x] [print ["Meta of X:" mold x]] >> detector [d e] Meta of X: '[d e] >> detector spread [d e] Meta of X: ~(d e)~
Ultimately I backed down on this, instead allowing you to use type filtering to narrow which isotopes you'd be willing to accept:
>> splicetaker: func [x [any-value! ~group!~]] [ append [a b c] :x ] >> splicetaker [d e] == [a b c [d e]] >> splicetaker spread [d e] == [a b c d e]
So this creates a new typeset feature that is hacked in terribly (the presence of any ~xxx!~ currently means the same thing as ~any-value!~, but it's documentation for when the feature gets better). And it means that the ^META parameter convention is basically something that can be implemented on top of this, by doing something like
x: my meta at the beginning of the function.
A primary driver behind this change was that operations which wanted to do things like ADAPT a function frame were having to become sensitive to whether a parameter was ^META or not, and it was more of a headache than having to use the :get-word accessors on variables. It seemed that standardizing the frame in a way that permitted isotopes as currency made more sense than having arguments be sometimes-meta'd, sometimes not.
What if OF (REFLECT) Didn't Take Isotopes?
So we could say that if you think you have an isotope in your hand, you're responsible for ^META-ing it yourself:
>> metatyper: func [x [any-value! ~group!~]] [ print ["Metatype of X is" type of ^x] ] >> metatyper [d e] == @['block] ; the TYPE OF received a QUOTED!, so e.g. answer incorporates quoted >> metatyper spread [d e] == @[~block~] ; got QUASI!, so TYPE OF answer incorporates quasi
On the plus side of such an approach, we don't have to invent any type representations for isotopes.