Repurposing THE-XXX! (@xxx) For Undecorated Values

Historical Rebol used quoted words (LIT-WORD!) to pass variables to functions, with the idea the function could then later set or get those variables. The quote level was removed under evaluation, meaning the function got a WORD!

rebol2>> whatever: 10

rebol2>> foo: func [var [word!]] [print [var "is" get var]]

rebol2>> foo 'whatever
whatever is 10

But we now have a general rule that quoting insulates whatever you quote from binding. So you have to add the binding, by saying something like foo in [] 'whatever

This isn't the only option, if the callee was willing to receive something other than WORD!. We also have THE-WORD!, which currently evaluates to the bound version of itself.

>> @whatever
== @whatever

>> foo: func [var [the-word!]] [print [var "is" get var]]

>> foo @whatever
@whatever is 10

This gets you the binding you want, but now you have a decorated word, which would be annoying if FOO is trying to do something besides GET/SET the value. Plain words are the norm.

If you wanted an undecorated word, you could use the standalone @ operator... which gives back its argument literally:

>> @ a
== a

@ a is a little bit uglier than 'a but not as bad as in [] 'a

>> foo: func [var [word!]] [print [var "is" get var]]

>> foo @ whatever
whatever is 10

But the spacing just throws it off to where it no longer looks like a single argument. And it doesn't look like one because it isn't (e.g. can't fill exactly one slot in the API or a COMPOSE). @ is also a function under the current design (you can redefine it), so calling it incurs some overhead.

Or... We Could Say that @ Things Evaluate And Drop The @

>> @word
== word  ; bound

>> @(print "Hello")
== (print "Hello")  ; bound

>> @[print "Hello"]
== [print "Hello"]  ; bound, less useful as plain block would have done that

This would make @word and @ word symmetrical, which is appealing.

But it's at the cost of losing the idea of a category of ANY-WORD! that stays somewhat as-is.

How Big A Loss Is The "Inert" WORD!/GROUP!/TUPLE!/PATH!

In practice, the idea of inert words hasn't lived up to my hopes for them. I thought maybe since they would be shielded from multiple phases of reducing, they might become popular for some kind of enumerated types. But being a bit ugly, that's not materialized.

If it gives you any idea of how not-popular the application has been, I changed the evaluator behavior and it only required one change in UPARSE to boot:

  (if spec.1 = '@pending [
        assert [spec.2 = [<opt> block!]]
        autopipe: false  ; they're asking to handle pending themselves
        spread reduce [@pending spec.2]  ; <-- this has to be '@pending
        elide spec: my skip 2
    ] else [
        autopipe: true  ; they didn't mention pending, handle automatically
        spread [@pending [<opt> block!]]
    ])

They're still free to be used in dialects for whatever purpose (here we see them marking output parameters in the function spec dialect). And they serve a good purpose in PARSE for "treat this value literally":

>> block: [some "a"]

>> parse ["a" "a"] [block]
== "a"

>> parse [[some "a"] [some "a"]] [some @block]
== [some "a"]

But it seems that with the new binding model, they have a higher calling in the evaluator... for producing undecorated bound things!

If you want wordlike things that do not reduce, there's blank-headed paths like /FOO ("refinement"). They have a binding and can be looked up. Today, blank-headed tuples like .FOO don't evaluate, but I'm aiming to say that they do--and that they do member lookup in methods. Or... y'know... you could put the word in a block! [foo]

Note That Decorated Types Have To Use @

There's no such thing as a THE-SET-BLOCK! (and I don't imagine there ever will be).

 >> @[x y]:
 ** Error: That doesn't exist

 >> @ [x y]:
 == [x y]:

So it's only the plain WORD!, TUPLE!, PATH!, GROUP! and (redundantly) BLOCK! that you can do this without a space.

But it's likely much rarer to be generating such material bound in isolation.

In any case, preliminary looking at the results, I think this is a solid change and much needed in the new binding world.

2 posts were split to a new topic: Meaning of META-BLOCK! ^[...]

I’m not sure I love this, as presented. Under the prior behaviour, there was a nice symmetry between words and blocks:

Words Blocks
Evaluates to unbound self quoted WORD! quoted BLOCK!
Evaluates to bound self THE-WORD! BLOCK! (and THE-BLOCK!)
Evaluates to result WORD! GROUP!

This change disrupts the concept. Now, THE-WORD! transmutes to another data type under evaluation, while a BLOCK! stays the same type. Not that that’s bad, precisely… but in my experience, this kind of inelegance generally means that there’s a more powerful design waiting to be discovered.

This also reminds me of when I asked why BLOCK!s aren’t quoted GROUP!s. You responded that, given Rebol’s evaluation model, it’s helpful to have a data type which evaluates to itself, rather than transmuting into something else under evaluation. But now, THE-WORD!s get precisely that undesirable behaviour! So why does that reasoning apply to BLOCK!s rather than GROUP!s? Again, it feels inconsistent.

But, regardless, let’s continue this train of thought… what does the above table look like if you do incorporate this change?

Words Blocks
Evaluates to unbound self quoted WORD! quoted BLOCK!
Evaluates to bound self ??? BLOCK! (and THE-BLOCK!)
Evaluates to bound other THE-WORD! ???
Evaluates to result WORD! GROUP!

So, it yields two unfilled cells. What could go in them?

To me, the form of the table suggests an intriguing possibility for the bottom-right unfilled cell: what if we moved THE-BLOCK! there? It would mean that a THE-BLOCK! evaluates to a bound GROUP! — filling the gap I noticed in the above thread. It’s not as obviously useful as THE-WORD!, but it might have some use somewhere. And at the very least, it would make THE-BLOCK! something more than a redundant duplicate of BLOCK!, which is nice.

And what of the top-left cell? That one stumps me a bit. It used to be THE-WORD!, but of course we’ve repurposed that now, largely due to uselessness. In any case, I think it can be simulated by a BLOCK! containing a single WORD!, so I’m not overly worried about it.

THE-BLOCK! evaluates to BLOCK!, so it is not evaluating to a bound self... it is evaluating to a bound "other"...

...and THE-GROUP! evaluating to bound group makes more sense to me. It looks pretty consistent, and it is useful in practice.

My original intention for @xxx behavior was indeed the by-product of me thinking "what's missing", and there wasn't any word/path/group (it predated generic tuple) that evaluated to itself. I thought @ forms might be useful for enumerations, so you didn't have to fret over quoting it everywhere.

colors: [@red @blue @green]

obj: make object! compose [color: (colors.2)]  ; vs. [color: the (colors.2)]

But the ugliness made it unpopular for such purposes. It also predated generic quoting in things like COMPOSE. It's less an issue today, as you can compose [color: '(colors.2)].

Attempts to leverage meaning from its unevaluatedness were made. For a period of time, it was tried as an ill-fated solution to /ONLY, via a weird kind of parameter that could sense if the callsite was literally using an @, variations of:

>> append [a b c] [d e]
== [a b c d e]

>> append [a b c] @[d e]
== [a b c [d e]]

>> var: @[d e]
>> append [a b c] var
== [a b c @[d e]]

Isotopes came and blew that nonsense out of the water...if you want to read a struggle in cognitive dissonance: "Every Thought On Array Splicing Has Been Had :exploding_head:".

(There was also a brief detour prior to the creation of ^meta words when @word acted like meta, but was quickly deemed to warrant a new more semiotic type.)

What I can tell you is just that in practice, the need for bound plain words, tuples, and groups is now very pronounced... and the need for an inert form of word has barely ever registered.

Words don't exist in a vacuum, they are carried in containers. And when you put a bunch of words and other values in a block, every value becomes inert. Having an unevaluative decorated type that evaluates to itself wound up to be a mostly unused thing... and it just seems to mean you've created a case of not utilizing the evaluator when you had the chance.

So the justification for the "asymmetry" you may sense in the design can be understood as a product of this container vs. not-a-container asymmetry.

I have a large data set of code and tests to draw upon to say that the proposed behavior is very useful, so I'm pretty bullish on it at the moment.

Oh, I didn’t realise this was the case. And I didn’t know THE-GROUP! exists either! So that means it’s all symmetrical anyway, and I had absolutely nothing to worry about.

1 Like

So the consequences of this decision have rippled around.

So this may all align with the idea that under evaluation, the @ operator is just a function that takes its argument as an @-parameter.

@: lambda [@arg] [arg]

So it's fundamentally related to quoting...and binding. There's a bit of an dissonance with the usage in PARSE, where it's related to binding but doesn't have anything to do with "quoting". This is probably fine, it's just taking me a little while to shift the parts around and feel like everything is rational about that distinction.

But maybe these should be BIND-XXX! and not THE-XXX!. We may want THE to just take a quoted argument that doesn't bind, and that would shake this up. Not enough words to describe the nuances, here...

2 Likes