QUOTED! arrives (formerly known as "lit bit")

I'm pleased to say that the day has come that you can use apostrophe to escape any value...not just WORD! and PATH!. And since I said any value, that means you can also quote quoted values...as deeply as you like!

>> '(1 + 2)
== (1 + 2)

>> ''(1 + 2)
== '(1 + 2)

>> quoted? first [''{double quoted text!}]
== #[true]

>> quotes of first ['''''''''''''''''<whoa!>]
== 17

It opens up a whole new box of parts for dialecting, and makes your every day code shorter and clearer (when used properly). But don't be fooled by the fact that you can use a stupid number of quotes if you need: This isn't a frivolous thing, and being able to truly escape any value--hence including any quoted value--is integral to the feature.

If you are a heavy user of COMPOSE-ing code and then DO-ing it, you would be more likely to appreciate the efficiency and literacy of:

 do compose [... some-var: '(foo) ...]

But what you realize if you program at the API level (like C or JavaScript) is that every execution acts like "a do of a COMPOSE". Since API representations of values spliced in from non-Rebol code are not fetched through a WORD!, libRebol users wind up putting "QUOTE" (now THE) calls everywhere--slowing things down, junking it up, and fundamentally changing the types and shape of what you're working with. If you write sophisticated enough Rebol routines you've almost certainly run up against this problem too--but it's an issue on nearly every call into the API.

The quoting implementation is optimized to the point of being nearly free for depths less than 4, using something I call "in-situ escaping". Because of how I've made the C++ build check it at compile time, it significantly reduces the risks of such a tricky performance hack. Another very neat design point allows these new "QUOTED!s" to efficiently participate in binding...if their "contained" item is bindable.

Be warned, this is a radical change!

I'm sure you'll love it when all is said and done. But it changes the typeclass membership of LIT-WORD! and LIT-PATH!. They are no longer ANY-WORD! or ANY-PATH!, but instances of a new fully generalized quoted type. This will cause some speedbumps.

Since you could do things like GET on a LIT-WORD!, or APPEND to a LIT-PATH!, I've tried to set up some mechanisms for the cases I thought of. I even threw in some new weirder ones, like letting you add directly to a quoted integer and get a quoted integer at the same level back:

 >> add the '''''1 2
 == '''''3

I didn't see a good general rule for this. It seems FIND on a quoted BLOCK! should return a position in the quoted block that is still quoted. But SELECTing or PICKing a value out of the block should ignore the container's quoting. It just seems like it has to be done on a case-by-case basis, for the semantics that make sense for the operation.

Basic Mechanics

To get the number of quote levels, use QUOTES OF. To get rid of any quoting present on any value, use NOQUOTE.

>> quotes of first ['''{triply quoted string}]
== 3

>> noquote first ['''<some-tag>]
== <some-tag>

All that happens with multiply quoted types is that each time the evaluator sees it, it will peel off one quote level:

 >> ''(1 + 2)
 == '(1 + 2)

 >> '(1 + 2)
 == (1 + 2)

 >> (1 + 2)
 == 3

This means inert types which are singly quoted get evaluated and lose the distinction from the plain inert type. So if you have a function that takes an evaluated argument (e.g. foo: func [x] [...]) you can't provide special behavior for foo '[block] that is different from foo [block]. The only way a called function will see the bit is if it quotes the argument, or if it's inside a dialect block (like a PARSE rule).

You can, however, get special behavior for foo ''[block], as it will receive a singly quoted block as an argument. And of course, it's now more practical to escape GROUP!s, so it might be worth it to start defining distinct behavior when groups are used since they'll be so easy to pass! (I have some ideas about this.)

Name Switcheroo: QUOTE => THE

QUOTE now adds a quoting level to whatever it gets as an argument, with that argument being evaluated normally:

 >> x: 1
 == 1

 >> quote x
 == '1

 >> the x
 == x
1 Like

Generic quoting has proven to be pervasive and powerful; and a particularly strong ally in the API for controlling the evaluation of slots. But in the Red Gitter channel there was recently skepticism--which I would call a good example of "groupthink" to be avoided. :-/

The triggering remark was that they were discussing some extremely convoluted and non-generic ways of doing literal branches. So @draegtun offered up Ren-C's solution:

Using QUOTE-ing offers consistency, so no need for variations of IF, EITHER, CASE, etc

>> case [false '[a] true '[b] false '[c]]
== [b]

The Redders were rather unappreciative (preferring perhaps to self-congratulate on the "genius" of Rebol2 ALSO--which is both trivial and confuses every piece of code it's used in. ELIDE and invisibles trounce it.)

From Gabriele:

"blocks are already un-evaluated by default. The non-quoted version would be paren! . I don't find that kind of "quoting" to be a sane solution, sorry.

The issue is when you are putting something in a slot and you don't know what it's going to be. Maybe it's a WORD!, maybe it's a PATH!, and maybe it's a BLOCK!. Or what if you have a PARSE rule, and you want to match a BLOCK! literally instead of treating something as a rule? And @draegtun has shown you yet another use.

Yet somehow, coming up with alternate names like either* or refinements that have to be put on every control construct is more "sane"? (Uh, nope, sorry!)

From Gregg Irwin:

Quoting, to prevent evaluation, should be the rare exception, same as lit args. We have blocks, we are unevaluated by default, and funcs that evaluate them because that's the most useful thing to do for those functions .

We don't want to be more Lispy. Lit-word syntax is specific to words, not other values.

What we need to look at are real-world use cases, where you want to prevent evaluation, and compare those.

He brings up looking at real-world use cases. But here they are looking at a real world case and proposing wild workarounds. I don't think it's too much to ask to study the corpus of analysis here (e.g. how to do splicing in the API). And it's worth the time to do that study--I wouldn't be saying it if it weren't important--and I don't see how it's helpful to invoke a tautology like "Lit-word syntax is specific to words, not other values" just because Rebol2 did that.

What about the simple example in Rebol2/Red of obj: make object! [x: quote (1 + 2)] and then mold obj giving back make object! [x: (1 + 2)]? It's not a perfect example because code that makes a value isn't the same as a literal notation for a single value--but, it does show that wishing to point out spots to protect from evaluation is a common need.

From Nenad

Exactly. Another function for selective evaluation is clearly a simpler option than resorting to new esoteric lexical forms.

Funny to think that a generalized quoting facility is an "esoteric" form. It seems to be pretty regular to me...

(I would argue that there is a bit of a tunnel vision here because he knows how hard it is to make any such changes.)

So I guess... generic quoting (and soft-quoted branches) are a Ren-C exclusive, powering the libRebol API.

Or maybe they'll see the light. Time will tell. :-/

1 Like

My inclination with reflection is that types return the same no matter the quotedness:

>> type of 1
== integer!

>> type of '1
== integer!

And that the quotedness should be determined separately:

>> quotes of 1
== _ (or null, or whatever the flow blanking mechanism is)

>> quotes of '1
== 1

>> quotes of ''1
== 2 ; etc.

In Parse, you would need to match the exact number of quotes:

parse [1 '1 ''1 '''1] [
    '1
    quoted '1
    quoted quoted '1
    quoted quoted quoted '1
]

The return of quoted datatypes seems problematic.

Unrelated: is that a quoted number I see on the last example here?

We can discuss that, but... I'd strongly advise against it. Firstly I'll point out that does create incompatibility with history:

rebol2>> type? 'x
== word!

rebol2>> type? quote 'x
== lit-word!

rebol2>> (type? 'x) = (type? quote 'x)
== false

And that's even with a pretty lax equality operator:

rebol2>> equal? 'x quote 'x
== true

Of course incompatibility is not something I worry about too much (unless there's no way to implement a compatible behavior!)

But my technical concern is that if you conflate a quoted type with its underlying type, then you would be winding up by default with dialects that inadvertently treat quoted things and non-quoted things identically. This just seems a bit chaotic to me, and it also will create problems down the road when one decides to start using quotes creatively in dialects.

Imagine that people using your dialect realize you haven't checked both the type and the quotedness.
So as a user, they don't bother stripping off the quotes when they are generating code (since an INTEGER! will accept a quoted INTEGER! anyway, why should they?) But then later, imagine you do decide you would like to distinguish the meaning of quoting to have it mean something distinct.

Added to all of this, the design has been very particular for performance so that the type-plus-quotedness check is efficient. It would be something of a setback if not done that way.

I think a big thing to resolve here is the nature of DATATYPE!. Given the desire for extensibility, the difficulty of coming up with a representation for them, and how they've been historically conflated with WORD!s...it makes me wonder if they should be WORD!s (PATH!s, TUPLE!s, BLOCK!s...?) How many routines are polymorphic, in the sense of taking something that may be a datatype, or may be a WORD! ?

This has been sort of a pattern internally to Ren-C; to take a lot of various things that were their own enumerations (e.g. action IDs) and go ahead and just use WORD!s for those IDs.

Yum, sounds good! +1