I've been suffering through an attempt to bring sanity to bear upon the MAKE and TO matrix.
While doing so, I noticed that there are a lot of MAKE instructions that do "joining".
In R3-Alpha and Red:
>> make binary! [1 2 #{DECAFBAD} 3 4]
== #{0102DECAFBAD0304}
Historical MAKE is so arbitrary in how it interprets its arguments that it made me feel that it would be better if JOIN allowed a datatype as its first argument...
JOIN typically makes a new series out of what you give it:
>> bin: #{AABB}
>> join bin #{CCDD}
== #{AABBCCDD} ; made new series, didn't impact BIN
>> bin
== #{AABB}
But if you could pass a datatype as the first argument, you could relieve MAKE of the behavior...and make it clearer in the source what's actually happening:
>> join binary! [1 2 #{DECAFBAD} 3 4]
== #{0102DECAFBAD0304}
Since that seemed nice, I started to map it out, and ran into a bit of a problem:
If "datatypes" are an ANY-WORD! or ANY-LIST! or anything like that, how do you know you're not trying to JOIN with it?
A WORD! should be able to join:
>> join 'var 10
== var10
A BLOCK! should be able to join...
>> join [a b] 10
== [a b 10]
Okay, but when you say join binary! what is BINARY!? Well right now, it's:
>> binary!
== &[binary]
Well that looks like it's in-band for the kinds of things JOIN operates on:
>> join &[binary] 10
== &[binary 10]
An Antiform Would Push DATATYPE? Out-Of-Band...
It's possible that we could come up with a new non-antiform for types. But the lexical space is extremely saturated.
If we were just reaching for something on the basis of looks, antiform FENCE! is nice:
>> binary!
== ~{binary}~ ; anti
This Has Come Up Before...
Historical Rebol let you use datatypes in FIND:
rebol2>> find [a b c 10 20] integer!
== [10 20]
But then this conflates with when you actually literally have the INTEGER! datatype in the block.
>> find compose [a (integer!) b c 10 20] integer!
== [integer! b c 10 20]
So in the early days of antiforms, I proposed antiform datatypes as a solution... but at that time I was thinking that there'd be non-antiform DATATYPE!, and it's just that when it was an antiform it would be a "MATCHER".
The proposal I'm thinking of now is different, in saying that DATATYPE? only exists as a (stable) antiform state.
There are tradeoffs, in that datatypes can't be put in blocks directly. But the mechanics of working with quasiforms and antiforms via things like REIFY and DEGRADE are much smoother now. And really, putting datatypes in blocks doesn't happen that often (I've had to put splices in blocks but really never datatypes).
Being completely out of band with things you can find in a block has advantages. JOIN isn't unique in reaping benefits from being able to say that datatypes aren't things.
And it unifies datatypes with typecheckers being antiforms, because typecheckers are frame antiforms, e.g. actions:
>> find [1 3 5 8 10] even?/
; first in pack of length 2
== [8 10]
So I don't think it's a crazy idea.
I'm hesitant to surrender FENCE! antiforms to the purpose so soon after their invention. But there isn't anything undecorated left.
Not that "no decorated antiforms" is some unbreakable rule, it was a guideline.
Using & would put it in the family of TYPE-XXX! and be cheaper than FENCE! if you made new instances:
>> binary!
== ~&binary~ ; anti
A random bad idea would be to make them keywords, and just have DATATYPE? be smart enough to filter out ~null~ or ~void~ or ~okay~ or whatever else (the symbol could carry a flag)
>> binary!
== ~binary~ ; anti
>> null
== ~null~ ; anti
>> datatype? binary!
== ~okay~ ; anti
>> datatype? null
== ~null~ ; anti
I can pretty clearly say that's bad.
Or, Pick Some Non-Antiform Notation
Status quo might look like:
>> binary!
== #[binary]
Despite it looking sort of like a block, it wouldn't be. But we're squandering some of our notational space no matter what. The ~&type~
is the discount route.
Either Way, DATATYPE? Can't be ANY-WORD! or ANY-LIST!
It needs to be out of band one way or another. I'll have to think about it.