Usage is making me wonder if this is the best use for @, or if it would be more helpful for literal non-matching... e.g. synthesizing values like a GROUP! would.
>> uparse "a" [collect [keep @[keep some], keep <any>]]
== [keep some "a"]
>> uparse "a" [collect [keep @ 'keep, keep <any>]]
== [keep "a"]
Doing this kind of thing right now has several ugly alternatives:
>> uparse "a" [collect [keep ^('keep), keep <any>]]
== [keep "a"]
>> uparse "a" [collect [keep ([keep]), keep <any>]]
== [keep "a"]
"What's so ugly about keep ([keep])", you might ask. The problem isn't with it when it's written literally like that. It's when you're trying to build a rule with COMPOSE and the contention over GROUP! and trying to nest inside of it becomes a bummer.
Let's say you're trying to build a KEEP parse rule inside a parse rule (which I am actually at this moment trying to do in the whitespace interpreter project):
uparse ... [... collect [
keep (compose [keep (to word! name)])
Okay so that gives you [keep Binky]. Not what you wanted. You can quote it...
keep (compose [keep '(to word! name)])
Now you've got [keep 'Binky]. But you're not trying to match Binky in the input, you're trying to synthesize it out of thin air. Need it in a GROUP!... let's just go ahead and use the wacky engroup operator to do that:
keep (compose [keep (engroup quote to word! name)])
So are we set, we've got [keep ('Binky)]? Well, no... because KEEP demands evaluative values be quoted, and that quote is vaporized during the group evaluation so KEEP is getting a plain Binky word. D'oh, so we need another quote on there somehow. The ^ operator is one way, there are others that are more verbose:
keep (compose [keep ^(engroup quote to word! name)])
Notably you can't say keep '(engroup quote to word! name) because that becomes keep '('Binky), which would look literally for a match in the input of a GROUP! like ('Binky).
What A Freaking PITA... ...We Can Do Better!
If @ were used for literalization and synthesis of a non-match, we can make something less head-scratchy:
keep (compose [keep @ '(to word! name)])
Poof. If you wanted to quote inside the group you could do that too (effectively putting the "ONLY" on the value, as you would for keeping a block literally):
keep (compose [keep @(quote to word! name)])
The key to seeing why this breaks us out of the problem is that it lets us get at literal values without a GROUP!, which means we aren't trying to COMPOSE inside of COMPOSE groups.
And there are lots of places we could benefit from this, I think.
repeat ([2 3]) rule => repeat @[2 3] rule
Some situations might be a bit unsettling for people who don't see the @ as being quoting, e.g.:
repeat (2) rule => repeat @ 2 rule
The 2 might look a bit too attached to the rule, if you don't realize the @ takes one unit of quoted parameterization to its right... not a full combinatorized parser like [2 rule]
But, I think it's just something you would have to get used to. Like I say, it's actually this "let the @ stand off as a separate value" form that breaks the Gordian knot, when it comes to deeper forms of composition.
What Would @(...) Mean In This Context?
I guess it would just have to mean literally that GROUP, e.g. a synonym for @ (...)?
Because if it evaluated the group, what would it do to the result that would make it "more literal" than it already was? You have ^(...) to add a quote level already--which wouldn't make sense for @.
It's an odd thing, but not useless. After all, keep @[a b] is only one less character than keep ([a b])... though it would be more efficient due to knowing it doesn't need to run the evaluator on a GROUP!. And keep @('foo) saves an additional character due to not needing to quote the group, as in keep ('('foo)).
Hm, so actually, I can see that being rather useful.
How To Literal Match If Not With @ ?
We were back to the drawing board on this when @ started acting like META, but then got it back when the ^XXX types came around...
And now we're back again.
You can splice the value in as a quoted rule, so at least there are options... :(quote var) or :(^var)
>> block: [some "a"]
>> uparse [[some "a"] [some "a"]] [some :(quote block)]
== [some "a"]
Not my favorite, but by no means incoherent.
Though I'd already noticed one nagging missing point on literal matches, which was how to literally match a fetched block as a splice.
block: [a b]
uparse [a b a b] [some ?operation? (block)]
@block was a literal match, it had [[a b] [a b]] covered, but not this. I don't know what that means, but it just pointed out to me there was something else afoot.
Still available at the moment are .foo and .(foo), and /foo and /(foo) -- though I don't like those for this purpose.
I've been eyeing $foo $[foo] $(foo) etc for reasons like getting environment variables in shell dialects, and because I honestly think the potential here goes pretty far beyond MONEY!. It would take some reckoning in the type system to get those available.
If $ becomes synonymous with "substitution", then might be that $(foo) makes more sense for "substitute this expansion as a rule" than GET-BLOCK! does, which might let the GET-XXX! variations mean "use this as a literal value".
For Now I Need to Change @ So Code Gets Better... so...