Quoting Decisions In Practice: NULL Refinements

#1

I'm moving along with the unused-refinements-are-null concept, and absorbing the consequences.

One thing that it breaks was a way we'd been testing for a refinement being used or not in a JavaScript native. Previously one could write:

reb.Did(reb.ArgR('refine-no-args'))

That used "normal reb.Did" which did not quote its splices, as opposed to reb.DidQ which does quote splices. For a reminder of what that's all about--and why I struggled over not quoting being the default and needing the Q to get the quotes--please review the reasoning here:

Beta/One Quoting in the API decision: rebValue() and rebValueQ()

Despite not using Q, the normal reb.Did worked before because the two possibilities were BLANK! (_) or the refinement itself (/refine-no-args), both of which were inert. Now they're going to both be active. Refinement-style-paths are like a GET-WORD! that's null-tolerant...they evaluate. And nulls actually cannot be seen literally in the evaluator at all: they're considered errors if they ever come up, which is relatively esoteric to do when not using the API (though you can, e.g. eval null)

Long story short: if you're going to use plain reb.Did(), you have to splice quoted:

reb.Did(reb.Q(reb.ArgR('refine-no-args')))

Or you can use the form of Did that adds a quote level on its splices by default:

reb.DidQ(reb.ArgR('refine-no-args'))

Does this suggest refinements with no args should be LOGIC!?

One potential conclusion we might reach is that refinements which have no argument are forced very particularly to a state of truth or falsehood...that they are a special case.

That doesn't feel as "neat" to me as having their specialness be that when they are used, the truthy value they pick encodes the word of the refinement name itself. Then when they are unused they are null just like any other type of refinement would be. This makes chaining easier.

I think my feeling is no, and I like the current direction, where all unused refinements are null.

Are we starting to need a helper library?

I really wanted to keep the focus on a small API. But even with that, should we have some standardized helpers on top of the low level ones? Something like a rebx namespace?

if (rebx.HasRefineArg('some-refinement')) { ... }

I'm a bit torn on this question. I feel like you won't get very far in using the API if you don't understand what it's doing, and such things obscure the mechanics.

While the issues of the Q vs. not might seem like they're artifacts of the API, they are not. You'd see them anywhere you are trying something like a do compose [...] in plain Rebol. In R3-Alpha you had to say:

do compose [some-word: quote (expression-that-makes-a-word)]

Fortunately Ren-C has a lighter notation with generalized quoting:

do compose [some-word: '(expression-that-makes-a-word)]

This method even works with null, which a function like LIT (aka R3-Alpha's QUOTE) couldn't do...because there'd be no way to hold the null in a BLOCK!.

>> compose [lit (null)]
== [lit]  ; uh oh, the group!'s block slot just vaporized

>> compose ['(null)]
== [']  ; slot didn't completely vaporize, left the quote

>> do [']
; null

So all that's really happening is that the API is putting you face-to-face with the nuts and bolts of the language. reb.Q() on each splice site is a bit bulkier than an apostrophe, but that's why it was folded into the operator as reb.DidQ() if you are happy having it applied to all sites. I've suggested that a certain type of person might tend to be a "Q"-ist and always use the Q forms, then reb.U() to call out the places to unquote.

Anyway, I'm curious what API users think. @BrianOtto, @giuliolunati, @gchiu ?