Emergent Behavior of @x @x/y @x.y @[...], @(...)

UPDATE: The mechanisms that were under the name of @ as discussed in this post are now under the name of ^. It is kept as is here for historical reference.

A trend has emerged in how I want to use the @ variants when it comes to evaluation.

This trend is that they convey a "bit" on the type somehow, to mean it should be treated literally. I've been trying to cook up all kinds of indirect ways to getting toward that goal (e.g. "modal parameters"). But the true answer looks like just making the evaluator behavior for these types add a quote level:

>> x: 1020

>> @x
== '1020

>> @(10 + 20)
== '30

>> @[a b c]
== '[a b c]

And I'm now committed to finding a way that a lone @ would act like the QUOTE operator, so you don't have to use a GROUP! if you are in a situation where you don't need one.

>> @ 300 + 4
== '304

So @ will be an instance of a unique datatype with behavior coming from the evaluator. It would make a lousy WORD! as you wouldn't know how to interpret @@/foo (is that the PATH! representation of [@@ foo] or the SYM-PATH! representation of [@ foo] ?) But you shouldn't be able to override it, anyway.

Naming Is Pinned Down: LIT-XXX! Makes Perfect Sense

I say that @ is a "literalizing operator" because it gives you the ability to make parameter conventions that distinguish values into two behaviors:

  • Non-literal behaviors, which cannot truly receive an arbitrary value. The behavior can be applied to 3, but not '3 or ''3

  • Literal behaviors, which can receive an arbitrary value. These can apply to 3, '3, and ''3 etc. to any level of quoting.

Imagine a function that is append-like, but where appending a plain integer means to append that many blanks:

>> my-append [a b c] 3
== [a b c _ _ _]

Then imagine it regards being quoted as a flag meaning distinct handling should be used, such as handling literally:

>> my-append [a b c] @ 3
== [a b c 3]  ; literalism triggered by argument '3

>> my-append [a b c] @ the '3
== [a b c '3]  ; literalism triggered by argument ''3

>> my-append [a b c] @ the ''3
== [a b c ''3]  ; literalism triggered by argument '''3

So the idea that quoted things are "literal" has to do with this contrast of being able to represent anything, while the unquoted things can only represent the set of things that are unquoted.

The trend is that the distinction is used just like this: you get some sort of special behavior out of the plain form, and then the unquoted thing is taken literally.

The Final (?) Answer To The APPEND/ONLY Debacle

When I've gone down this road before, I've suggested that APPEND to a BLOCK! only accept BLOCK! and QUOTED!. And when you add the quoted thing, it removes one quote level.

At source level, append [a b c] '[d e] => [a b c [d e]] won't work because the quote level is boiled off during evaluation.

  • Changing that evaluator behavior is not an option...it is the whole point of the feature.
  • We could change the parameter convention of APPEND...and I've tried to do that in creative ways ("modal parameters").

But I think append [a b c] @[d e] is a winner. It strikes me as much more learnable than having to find out what /ONLY is.

Once you grasp this simple connection between @ and quoting, you open a lot of doors. I know @rgchris might be unhappy that it's a symbol. But append/only [a b c] [d e] has a symbol in it... it's a /. Then it has a cryptic term after it which has no systemic basis.

(You can always write append [a b c] quote [d e f]. It's one more space character than APPEND/ONLY, but I think it looks nicer.)

Special Consideration For Invisibility

We need a signal in the case of @(...) when the contents of the group evaluate and disappear completely. Right now I'm choosing ~invisible~:

>> @(comment "special")
== ~invisible~

Making it a VOID! (are we ready to call that BAD-WORD! yet or not?) helps make it "ornery" in case you're writing code that isn't specially geared up for handling invisibility.

Having some signal is is an important element of being able to proxy information about invisibility via quoting...so that invisible operations can chain to each other if they want to.

In addition, there's a bit of a twist with NULL. By default, it just passes through NULL.

>> @(null)
; null

But if it receives what the system thinks of as the "NULL-2" isotope as input, you get something different... a "quoted null":

>> @(if true [null])
== '

Then this provides that chaining property:

>> unquote just '
; null-2

>> unquote null
; null

Using true null for the literalized ordinary null has the benefit of being falsey, while every quoted value is truthy. That's convenient!

The Behavior in PARSE is Aligned Nicely

Pieces have slotted into place on this, and the behavior is very parallel:

>> value: [some integer!]

>> parse? [[some integer!] [some integer!]] [some @value]
== #[true]

It's pleasing how this acts like if you'd written:

>> parse? [[some integer!] [some integer!]] [some '[some integer!]]
== #[true]

That's makes parity with the evaluator behavior of @ being a quoting operator.

Note: KEEP presents a bit of an odd edge case, because the @ gets consumed by the rule matching process:

>> block: [a b]

>> parse [[a b] [a b]] [collect [some keep @block]]
== [a b a b]

The @block rule just evaluated to [a b]. So the collected thing is not quoted. How are you supposed to append the block as-is?

This makes a case for still having /ONLY, which would disable both the block-splicing rule and the quote removal rule.

Modal Parameters Are Dying... Meet Literal Parameters

We owe modal parameters a great debt for leading down the design path to this point. Let us thank them for their service, and send them on their way. :slight_smile:

Now there's a higher calling for @arg in your argument lists:

>> isotope-seer: func [@arg "literal parameter"] [print [mold arg]]

>> isotope-seer 10
== '10

>> isotope-seer null
; null

>> isotope-seer (if true [null])
== '

A-Ha! The transient isotope status is revealed to us. This parameter convention would be used by things like ELSE and THEN to give them the insight they need to decide whether to take action or not.

It was already the case that no block could store an isotope null...because it was a null. But some variables were still getting isotopes. Now that loophole is closed--there are no isotopes outside of the evaluator engine itself. Functions like THEN and ELSE can react to them, and you can use other literal-parameter-taking functions to test the status (like the clever THEN? and ELSE? tests). But null-2 never makes it into variables.

Brilliant, yes?

I'll point out that the feature of modal parameters was first prototyped in usermode. It may be that similar features of triggering an associated refinement based on patterns noticed at the callsite would be interesting for users to implement in some cases. But it feels very good to make that experimental code for the adventurous...instead of having it implemented in the core, and to have critical features depend on it!

There Are Still Details To Work Out, But...

I'm just going to go ahead and continue my self-congratulatory streak by saying "this is awesome".

It may look simple at the end when all is said and done. But simplicity is hard.

Remember folks: Design. Takes. Time.