The goal here is to make pleasing parts that compose well, letting novices build clever language constructs out of solid-feeling bricks. By this point I shouldn't have to stress that this is really, really hard to make simple.
But...slogging through it some more...
As I've mentioned, this doesn't make everything easier...it introduces a level of unwrapping whether you are dealing with something that was isotopic or if it was plain.
But the fact is that fully generic quoting--including of isotopic things--isn't just useful, it's foundational. And things like the META-WORD! fuse together accessing a potentially isotopic value with a quoting operation. If QUOTE and META were not mostly the same thing conceptually, we'd be hard pressed to make a quoted isotope... even if we allowed QUOTE to take isotopes as parameters, because getting those isotopes would be a challenge.
To demonstrate: let's say META decided to go its own way and not give back something that would evaluate back to the isotope. But QUOTE decided it would take isotopes, and give you back something that evaluated to an isotope (effectively becoming "UNEVAL"):
== something ; isotope
>> meta ~something~
>> quote ~something~
So META's result can be used without unboxing it, while QUOTE's result is ready to be poked into a COMPOSE or something of that sort. Different purposes, so maybe okay, right?
But there's a META-WORD! but no corresponding operator that means "make quoted" (a quoted word just means "be quoted"). So you've got a pretty big feature gap
>> x: ~something~
== something ; isotope
>> quote x
** Error: x is a WORD! isotope, use ^x to access it
You'd have to get awkward:
>> quote unmeta ^x
>> quote get/any 'x
This shows a pretty big pain point for having the ^META meanings and QUOTE meanings divergent... not just the mechanical pain, but the conceptual pain of understanding what the heck is going on here.
So I'm getting fairly certain we'll have to accept the situation of saying that ^META of isotopes are wrapped up in the thing I am currently calling "BAD!" (but clearly it shouldn't be called that)
>> x: ~something~
== ~something~ ; isotope
...But What About The NULL Situation?
When I suggested making ^META of something like a word isotope be a ~word~ instead of plain word, I claimed it had an advantage...such as by making it easier to use in something like COMPOSE to reconstitute the original variable back:
do compose [var: (temp)]
But there's the twist that META of NULL is NULL. So that COMPOSE will complain about it (which is good, it forces you to decide what you really meant, and if that was a special case missing handling).
You can't solve this by putting a quote mark on the group in the compose, because that will mess up non-NULL values:
do compose [var: '(temp)] ; if var starts as foo, it will wind up as 'foo
Quoting produces a standalone tick mark instead of a NULL for a NULL input, which provides the necessary properties of being both reified (so it can be in a block) and reproducing null in evaluation:
>> quote null
But a quoted null (single tick mark) does not trigger ELSE and isn't falsey.
For Completeness: What About A (NULL => ') Construct?
If QUOTE and META were similar in all other aspects, we could add a missing construct to fill in this gap. Something that turned NULL into a single quote and passed through all BAD! and QUOTED! as-is.
Blunt name for starters...nullquote. For the moment, we will imagine it's tailored to fit this situation, and will warn you if the thing it's spitting out as a result can't be UNMETA'd:
>> nullquote null
>> nullquote first ['3]
>> nullquote <something>
** Error: NULLQUOTE only accepts BAD!, QUOTED!, and NULL
The concept would be you could combine this with
^var to turn it into a generalized "unevaluate" operation that could also work in COMPOSE scenarios:
temp: nullquote ^var
do compose [var: (temp)]
do compose [var: (nullquote temp)]
You could also try being explicit:
do compose [var: (temp else [the '])]
do compose [var: (temp else [''])]
do compose [var: (temp else [quote null])]
The current idea behind REIFY is that it would produce ~null~ out of a NULL. It doesn't produce a quoted null because it's trying to put in a placeholder that represents the idea of a non-quoted null (which isn't possible, so that's why it makes a visibly jarring choice). But in the case of these assignments under evaluation, it would produce an isotopic null and do the correct thing.
What About The Reverse... (' => NULL) ... nulldequote?
If we made the ^META operations never return NULL, then that produces a different situation:
>> x: null
>> nulldequote ^x
It should be obvious that the other way around is preferable.
META'ing defaulting NULL => NULL has systemic benefits. I'll play the broken record about FOR-BOTH:
for-both: lambda ['var blk1 blk2 body] [
unmeta all [
meta for-each (var) blk1 body
meta for-each (var) blk2 body
Just in general, being able to find out if an operation--even a ^META one--is NULL is a good test.
It's clear that diligence is needed to reduce the total number of concepts in play--to the extent possible--without sacrificing functionality.
I was a bit spooked by the possibility of having three distinct concepts: QUOTING, META, and UNEVAL... all doing similar but slightly different things.
- Unifying QUOTE and UNEVAL has the somewhat uncomfortable consequence of saying that the result of QUOTE isn't always a QUOTED! thing.
So I suggested making META on isotopes give back something that when evaluated would produce the isotope back.
This meant the META of something like an isotopic WORD! could not be a WORD!, because that would not evaluate back to an isotope...it would have to be in a wrapper (the BAD! wrapper, indicated by
~<leading and trailing tildes>~
This allowed NULL to be the only falsey output from META, so long as this wrapper type would be truthy even if it was a wrapped BLANK! or wrapped LOGIC! false.
That mostly nixes the need for UNEVAL, but with a loophole surrounding NULL behavior when the unevaluated thing was to be used in something like a COMPOSE
As an edge case, it doesn't seem too horrific...especially since NULLs now tend to raise errors to draw attention to improper uses.
If you can turn a NULL into a quoted null (
') one way or another, the loophole is filled in the cases where it matters.
Now Needed: More Techniques, and Better Names
So if ^META is going to be coming back BAD! or QUOTED! or NULL... then there's going to have to be some ways of dealing with this.
I feel like there needs to be some sort of mechanic to make this pattern easier:
x: ^(some operation)
switch type of x [
null [...process null case...]
bad! [x: unbad x, ...process isotope case...]
quoted! [x: unquote x, ...process normal case...]
(And of course UNBAD is terrible. I wondered if maybe ~asdf~ could be called a "semiquoted" or "quasiquoted" WORD!, so that UNQUOTE could be used to remove the ~'s from it. But that probably just makes things more confusing instead of less (and introduces '''~asdf~ as a "quoted quasiquoted word")
We've got a few options:
(some operation) then ^x -> [
if quoted? x [
x: unquote x, ...process normal case...
] else [
x: unbad x, ...process isotope case...
] else [
...process null case...
Anyway, this looks like where things are at right now, and is what has to be improved upon. The rationale feels pretty sound. Guess it just needs to develop from here.