Much of Rebol's appeal lies in its diverse collection of parts. One of the parts that has been coveted for a while is @foo.

Questions have arisen on what to call this. AT! has been suggested, though this can get a bit confused with the AT operation in the language. (TO-AT also looks kind of strange).

Other concepts have been to retake HANDLE!, call it IDENTITY! or simply merge it with EMAIL!...which is what Red has done:

>> type? @foo
== email!

>> any-string? @foo
== true

While simply seeing it as an extension of EMAIL! has come up as an option, other questions have been asked about whether it should be an ANY-WORD! and have binding. I liked the idea of it having binding, because I like it being able to be thought of as "pointing" at something. For instance, being used to implicate a parameter in a FAIL:

foo: func [bar [integer!]] [
    if bar >= 1020 [fail @bar "Must be less than 1020"]

But now I think that @ may be able to serve a greater purpose for us as a modifier that is like GET-XXX! and SET-XXX!, in that you can apply it to WORD!, PATH!, GROUP!, and BLOCK!. It would fulfill some of the motivation of having another ANY-WORD! type without being mixed up with the things that made ISSUE! a bad ANY-WORD! (e.g. we knew #123-45 should be an ISSUE, while :123-45 wouldn't be a valid GET-WORD!)

And it can provide an otherwise missing ability: Irreducibility.

Irreduciblity: the missing link for enumerated types

There's a problem I have pointed out that shows up when you try and use a WORD! in an enumeration.

which: pick [foo baz bar] n
do compose [
     select data (which)

The words look nice, but they're just one evaluative step away from being looked up as variables. You can quote things--now anything you want. But that only delays the problem...once you evaluate the apostrophe vanishes, and now you're back to the same problem again. You're always trying to compensate for how many evaluations you are doing to line up the quoting levels.

If you imagine the AT-XXX! types not evaluating, they do something you couldn't do before, unless you used an ISSUE!. But as I say, the applications of ISSUE! don't really fit with WORD!s and symbol comparisons. And # is already overtaxed in the language for so many things, that if we suddenly became prescriptive about using it for symbolic enumerations then that would be a whole lot of #'s.

This would not only soften the blow of losing bindings in ISSUE!, but it would open doors to questions like what @(...) or @[...] could mean in dialects. The @ could be the instruction for PARSE to match things literally:

>> block: [some "a"]

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

In the main evaluator, AT-BLOCK! is a bit redundant, given that blocks don't evaluate in the first place. But who knows what uses they might have.

AT-PATH! could play a pivotal part in NewPath, because plain PATH!s are too "lively", and if you compose them places they will evaluate. Quoting is not enough. You need full-on-dead, and this does it.

It looks to be ticking several design boxes for this long-desired feature. Think I'm liking it. Anyone have thoughts?

I like it too. I'll be curious to see what @rgchris thinks.

1 Like

Probably likes ISSUE!s coming back as ANY-STRING!, probably likes the concept, may not like the name.

AT doesn't sound very meaningful. And :foo and foo: are not COLON-WORD! and WORD-COLON! respectively. Putting AT in the name might be too much in the vein of PAREN!.

Having @foo be "SYMBOL!" or something like that would be a more appealing name, but it needs to connote its membership in its family. Needs to be short. SYM- for "symbolic"? (SYM-WORD! SYM-BLOCK!...) LINK, to be that it's pointing to something? LINK-WORD!, LINK-BLOCK!...

Weirder: DEAD-WORD!, DEAD-BLOCK! :-/

I feel kind of like maybe going long and calling them SYMBOL-WORD!, SYMBOL-BLOCK!, ... might be all right. It's not that long in the scheme of things, remember REFINEMENT? Just one more character for symbol, zero more for inert:


What I don't like about INERT is that it says too much about what the thing does, and that's really up to the dialect. SYMBOL-WORD! doesn't really say anything about behavior.

We should have ways to define shorthands, so if people wanted to in their own code abbreivate it they could, perhaps to just SYMBOL!. But I'd like the official name to keep it squarely in the WORD! class (or whatever class it's attached to).

For visibility, I wonder if @(...) makes more sense for PARSE rule splicing than the more slight **:(...)

parse ["a" @(either mode = <bmode> '[some "b"] '[any "c"]) "d"]

parse ["a" :(either mode = <bmode> '[some "b"] '[any "c"]) "d"]

Especially when things are broken up across multiple lines, I think the colon could be easy to overlook.

Also: since @(...) would have no meaning other than "inert group" in the evaluator, this isn't competing with an existing definition the way a GET-GROUP! would be, as being associated directly with a GET operation.

As with most such things, probably best to just make it and see what happens. Shouldn't be too hard, slight variation on code that already exists for GET-GROUP! and GET-BLOCK!

Sounds good. Namingwise I like symbol-foo best so far. (or sym-...)

1 Like

I had another wacky half-baked fleeting thought that I thought I'd write down.

We've been looking for a way to represent datatypes symbolically. Today it's an ugly problem:

>> stuff: compose [(integer!) integer!]
== [integer! integer!]

>> type of first stuff
== datatype!

>> type of second stuff
== word!

Because we don't know what all people might want to use @ for, I haven't thought this entirely through, but what if...

>> stuff: compose [(integer!) integer!]
== [@integer integer!]

And what if it were a TYPE-WORD!

It's only a little step toward thinking of it as also being able to be an "enumerated type word", which makes its name have a bit of a double-meaning...

How does this apply to TYPE-BLOCK!, and TYPE-GROUP!, and TYPE-PATH!? Don't know, but we need some sort of notation for compound types that can't be summarized by a symbol. It could be a language: @[matrix integer [32 32]] coming back as the TYPE OF a 32x32 matrix? :-/

Just brainstorming here, I'm completely making things up. But if conflating WORD!s and enums was a thing people did before. How bad is conflating datatypes with enumerations? They'd collide sometimes, but there's only so many types...applications collide.

Does interfere with some creative uses of them, though. e.g.

 >> parse [1 1 1] [copy x some @integer]
 >> x
 == 1 1 1

PARSE would basically be surrendering creative uses. Because if copy x some integer! needs to work, and integer!: @integer, then so do literal things.

Anyway, just saying that TYPE- is a short prefix, starts with a different letter than SET- and GET-, and could maybe give a reason this inert non-reducing type exists. You could do what you wanted with it, unless it would break you by conflating with datatypes.

(Mentioning these things--even if they're bad ideas--can sometimes lead to good ones...)

IIRC you thought about removing "." from valid word characters. I think having @ domain could be a frequent wish, e.g. @hostilefork.com .

That would suggest that TUPLE! is in the class with PATH! in offering the SET-, GET-, and @ variants.


Just in terms of how things work it would be difficult to offer SET-, GET-, @ versions of everything. We run out of bits. It also creates a zoo of types that may do more harm than good, and ambiguities in scanning (PATH! and TUPLE! will be limited in what types they can store). But I think the similarity to PATH! as a kind of container makes it sensible to offer this, and you raise a good point that people would want such a form.

At the risk of causing mass hysteria...I actually kind of like the names LIT-WORD!, LIT-BLOCK!, LIT-PATH!, and LIT-GROUP!. It's not just that I want a different letter than S (SYM/SET). But it's also that. :slight_smile:

I've laid out what the difference is between quoting and literalness. And what's being described here for these items, having them be evaluator-inert, suggests you'd never need to say lit @foo. Because it's already as literal as it can get.

The challenging bit of this would be that:

>> lit foo
== foo  ; literally the word foo...not the LIT-WORD! @foo

>> to lit-word! 'foo
== @foo  ; you'd have to do something like this

But then again, we don't expect set foo to give back foo:, that's not what the operator does.

I'm admittedly partial to the 'HANDLE!' usage of @, so I'd mourn that as a loss. My initial superficial reaction is a little queasiness at an additional symbol kicking around and adding that cognitive load, but I would need to think about what it adds to the language and where it'd produce some wins before settling on that as a judgement.

It's not clear what this is in reference to...you mean you are partial to the name HANDLE! ?

My initial superficial reaction is a little queasiness at an additional symbol kicking around and adding that cognitive load

I'd be more worried if it weren't providing a clear mechanical purpose.

Having them be LIT-WORD!, LIT-PATH!, LIT-GROUP!, LIT-BLOCK! feels right. We're still keeping the type load about the same, with one QUOTED! datatype as a kind of "container". All in all I think the load is actually going down.

Not the name 'HANDLE!' for sure, just the notation primarily referencing the @123abc user name convention.

If then you mean it not being an ANY-STRING! thus limiting the range of legal names to valid formations of ANY-WORD!...you'd have some workarounds e.g. @["123abc"]

This points the way toward saying that to text! @["123abc"] needs to be 123abc, perhaps FORM-ing as @123abc I think we can push the shape around a bit to make this stuff easier for real-world scenarios.

After looking at this a while, I think my heart is set on LIT-WORD!, LIT-PATH!, LIT-GROUP!, and LIT-BLOCK!... even with the confusion it might cause for historical code.

The GET/SET/LIT has a good rhythm to it and helps explain the behavior of the type in the default evaluator.

I like the spread across letters (no SET/SYM)...and that each is a related operation in its own right in the language (LIT is a basic function, SYM is not).

So this is where I'm leaning toward right now...

1 Like

So...a year later...the SYM has never quite settled for me. I still have an aversion to the SET/SYM both being S, and beyond it... I just... don't like it.

I notice that Red has chosen to call @foo a REF!

red>> type? @foo
== ref!

So another possibility would be to say that these are REF-WORD!, REF-PATH!, REF-GROUP!, and REF-BLOCK!.

The GET, SET, REF have a rhythm (shared E) that is comparable in pleasing-ness to GET SET LIT (shared T).

but REF has an unfortunate overlap with REFINEMENT which is still a word in play (though as a type constraint on paths, not as an independent type).

I dunno. LIT really still feels like it was the right word for the wrong datatype.

Usages seem to strongly favor LIT, though not necessarily in the way originally conceived.

Originally I thought the important thing about @word was that it was inert in the evaluator.

>> @foo
== @foo

But a lot of the time, that isn't what's happening with it. Enough so to question if having it ever act like that is a good idea.

Instead, it's being used many places to mean do an evaluation (or lookup), where the product of the evaluation (or lookup) should be taken more literally.

This concept originated with "modal parameters"

>> data: [d e f]

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

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

But the concept is broadening into dialects like PARSE:

>> block: [some "a"]

>> parse [[some "a"] [some "a"]] [2 block]
; null   (tried to apply block as a rule)

>> parse [[some "a"] [some "a"]] [2 @block]
== [[some "a"] [some "a"]]  ; matched literally

And it's giving an escape mechanism to things like being able to RETURN an expression, even one that evaporates:

>> foo: func [] [return (comment "hi")]  ; same as `func [] [return]`
>> bar: func [] [return @(comment "hi")]  ; different mechanics

>> 10 foo
== ~void~

>> 10 bar
== 10

For a time it was used to also subvert voidification of branches. But once voidification was deemed to put too much of a burden onto cases that weren't using ELSE/THEN, it shifted further into the margins as avoiding "null isotopification":

>> if true [null] else [print "This else clause mutually excluded"]
; null-2

>> if true @[null] else [print "No mutual exclusion"]
No mutual exclusion

If Inertness Isn't The Game, Should It Ever Be?

If it's not INERT-WORD! or INERT-GROUP! in behavior, we might ask what the value of this is:

>> @foo
== @foo

Originally the value was that you could pass it to a non-quoted parameter and get it inertly for processing.

foo: func [x] [
   switch type of x [
        sym-word! [print ["Got a sym-word for processing:" mold x]]
        integer! [print ["Got an integer for processing:" x]]

>> foo 10 + 20
Got an integer for processing: 30

>> foo @append
Got a sym-word for processing: @append

If non-modal-parameter usages were errors, you wouldn't be able to do that. And if you ask for that to be a modal parameter, you won't see the argument "as is".

Another lens for looking at this question would be to ask how modal behaviors work when specialized:

>> apo: :append/only

>> block: [d e]
>> apo [a b c] @block
; == ???

There's no longer a switch for using the modal parameter to flip. So is it now going to just add @block? Should it be an error? (I lean to saying that a modal parameter that's had its modal control specialized out should likely error, or at least error if the mode is inconsistent with the refinement it controls)

One may disagree about the necessity of this particular modal parameter use for APPEND. But I've given more examples that show where it's helpful. And I feel like maybe the stakes are that if you don't use it in the evaluator to control a mode, you have to quote it or it errors.

This kills of the idea of thinking of this as an inert type, which seems like where the evolution has landed. Of course, you always have '@foo with a tick mark if the function you're calling doesn't quote. It's important to remember that generalized quoting is there to pick up the slack, and maybe you should stick with #foo and %foo and <foo> and "foo" for inertness.

(I haven't decided if .foo for predicates warrants a similar "it's probably better to error vs. be inert" treatment, considering how widespread they may be; you'd like to know if you used a predicate where one wasn't expected... maybe /foo should go with this strategy as well, forcing you to '.foo and '/foo with generalized quoting if the thing you're calling wasn't looking for them.)

1 Like

On the Unisys mainframe the AT symbol @ was called 'masterspace'. Could call something like @block thus 'masterblock'.

With ^ looking like it's going to take over for "literalization", this idea is back in the game.

Given how ^ is a standalone value type and operator, I would think @ could be the same. In the evaluator at large, lone @ itself could mean "give me a value literally", e.g. JUST THE

>> @ foo
== foo

>> @ (1 + 2)
== (1 + 2)

Which seems pretty coherent with its "just give me that back" behavior for its attached types.

>> @word
== @word

>> @(gr o up)
== @(gr o up)

This inert nature is ideal for DATATYPE! purposes. Whether they are expressed as functions that match types as a predicate or as a literal type name, you don't want datatypes to evaluate to anything.

I'll just point out that with value synthesis working how it does in UPARSE now, ([...]) takes care of this, e.g.

>> uparse "aa" [collect some ["a" keep ([found an a])]]
== [found an a found an a]

You'd only save one character. So that would mean more creative ideas might come into play for how these types would be used.

Clearly datatypes are a more pressing need.

There needs to be some juggling of the designs before we have room for the 5 types in the list, given the scarcity of bits in cell layouts. It's doable without breaking the world, though...

1 Like

There's a new option, which would be THE-WORD!, THE-BLOCK!, THE-GROUP!, THE-PATH!, THE-TUPLE!, and perhaps even @ as plain old THE!

This would go along with the idea that THE is the operation which means "as is"

>> the x
== x

>> @ x
== x

>> @x
== @x

>> type of @x
== the-word!

>> type of @
== the!

It's three letters and a complete word. It fits into the overall idea of inertness.

There is also DATA-WORD!, DATA-BLOCK!, etc. but then we don't want to call @ a DATA!... and it's kind of a puzzle as to what you mean by DATA (isn't everything data?)

1 Like

So I've wired things around such that parameter bits are shuffled around to new places that had opened up...and we can now use all 64 type slots for datatypes.

Hence the @word, @pa.th, @tu.p.le, @[bl o ck], and @(gr o up) are back...and I've given them names of THE-XXX!.

>> @ word
== word

>> @word
== @word

>> @pa/th
== @pa/th

>> @tu.p.le
== @tu.p.le

>> @[bl o ck]
== @[bl o ck]

>> @(gr o up)
== @(gr o up)

So that's back and working, and these types exist alongside the META-XXX! types. Next I'll be bringing back functionality, e.g. for @ branches not doing the null isotope stuff:

>> if true [null]
== ~null~  ; isotope

>> if true [null] else [print "Else"]
== ~null~  ; isotope

>> if true @[null]
; null

>> if true @[null] else [print "Else"]

So you have control to say if you actually do want a true NULL back even though a branch ran. Then there is the case I mention about how you can match things literally in PARSE instead of using them as rules:

>> block: [some "a"]

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

(had you written just some block it would have been trying to match some number of "a", not the block [some "a"])

As I've argued in this thread, a truly inert and bound value types fills in gaps in the parts box. So it's good to have them back.


I noticed something about this that made me a bit bummed, which is that I kind of feel the @ notation is a good shorthand for getting variables. But just because you use that shorthand you don't want to avoid isotopification:

>> x: 10, y: null

>> if true @x else [print  "else"]
== 10

>> if true @y else [print "else"]
else  ; d'oh...

That made me wonder if for this rare and BLOCK!-specific case, /[...] might be a better choice for "don't isotopify a branch":

 >> if true [null]
 == ~null~  ; isotope

 >> if true /[null]
 ; null

The idea of "leading slash means optionally NULL" is kind of congruent with refinements--which are NULL if not specified.

It's a feature that very few people are going to need to be using, whereas I think people might become partial to the @var notation...because it has the nice property of combining the BLOCK! with the GET-WORD!, e.g. a stand-in for the longer and dottier [:var]

if true @var1 else @var2

if true [:var1] else [:var2]

I'm not sure what the implication would be for @(...) and @[...] as branches at that point, however.

We should remember that @(...) is non-evaluative; it won't evaluate unless someone asks it to:

>> @(1 + 2)
== @(1 + 2)

Which means maybe this could be a cleaner resolution to the weirder "get-group escaping" stuff:

>> either true (print "A1", [print "A2"]) (print "B1", [print "B2"])

>> either true @(print "A1" [print "A2"]) @(print "B1", [print "B2"])

With more datatypes in the mix and an ever-evolving set of precedent, I think maybe we can start getting some closure on the issues I've brought up, like saying that a FOR-EACH will not give you back an ACTION! value unless you use a GET-WORD!...because things can get screwed up so easily when code is not written to be ready to take actions.

The landscape really has changed with ^META-XXX and @THE-XXX, so old difficult questions might start to look like they have easier answers now. It's nice when that happens!