In case I haven't said it enough (mostly to myself), I'll say it one (?) more time: calling API functions which splice in values is very much like DO COMPOSE.
REBVAL *result = rebValue("some code", value1, "more code", value2);
result: do compose [some code (value1) more code (value2)]
This sets you up for pain when value1/value2 are evaluative types and you did not intend them to be evaluated.
When using COMPOSE in the language, we're lucky these days to have nice generic quoting. If value1 is an ACTION! you want to run, and value2 is a WORD! you want to pass by value... you can apply the quotes where you want them:
result: do compose [some code (value1) more code '(value2)]
Writing this in the API, you can use
REBVAL *result = rebValue("some code", value1, "more code", rebQ(value2));
Do We Need both rebValue and rebValueQ?
The most foundational and pure API to give people is one that does what they asked. If you wanted a quote, you ask for it. If you don't, the value is left as-is. If it's in a context where you didn't want it to be evaluative and it evaluates...that's just a bug and you need to fix it.
I resolved to make operations like rebValue, rebElide, rebUnboxInteger etc. not add quotes for this reason. But to try and be "helpful", I thought to introduce auto-quoting versions like rebValueQ, rebElideQ, and rebUnboxIntegerQ that quote unless you explicitly ask for the quote to be removed with rebU().
If you had a lot of rebQ() calls in a given invocation, there was a performance advantage to making the Q-version of the call. And visually you weren't taking up as much space on a line.
But I'm on the fence looking at it in practice. This doubles a lot of the API interface, and now it doubles the doubling because there's C++ wrappers and C wrappers. In an API that was designed to be minimal and elegant, it undermines that elegance by appearing redundant.
One problem is that it adds an element of choice, which is a tax. Every time you make a call you wonder if you used the right one. And when you're in the middle of looking at a line of arguments you always have to scan back and wonder "is this in a Q call or a non-Q call".
Is removing rebValueQ and friends the right move?
There's a perceptual cost to making the API look larger and more cryptic. There's already a lot to learn, and then this cost is something anyone looking at the API or code samples will have to pay.
The performance advantage is fairly negligible compared to the overall cost of scanning/binding an API call. In cases that have no text portions to scan or bind, there's nearly zero advantage (for esoteric technical reasons I won't go into).
I'm not convinced the pain is reduced by that much by offering the alternatives. The main pain you are saved from is that when you have a lot of parameters you want to quote, you're saving characters.
If it seems too verbose to use Q a lot, there's always even more abbreviated shorthands:
#define Q rebQ REBVAL *result = rebValue("some code", value1, "more code", Q(value2));
And the function of rebQ() actually is not to quote a single item, but to be able to enter "quoting mode" for an arbitrary number of items. It applies to splices, not loaded code from c strings. So if you need it rarely, you could say:
REBVAL *result = rebValue(rebQ("some code", value1, "more code", value2));
Obviously I have some conflicted feelings about this, or they wouldn't exist. But I'm leaning toward killing off the rebValueQ, rebElideQ, etc. variants. Mostly because I think it will make the API and callsites appear more orthogonal to those reading it.