Beta/One Quoting in the API decision: rebRun() and rebRunQ()


#1

In the question of whether or not APIs should quote spliced values, the answer is...

Only if you ask it to ...but... make it really easy to ask.

Previously this was considered to be undesirable:

const char *spelling = rebSpell(rebQ(word));

To mitigate that, instead go ahead and offer a "Q" version of every variadic evaluating API:

const char *spelling = rebSpellQ(word)

You can choose either way, and you can also switch back and forth with rebQ() to add a quoting level to your splices and rebU() to remove one.

Looks good enough AND makes people feel in control

People may grumble when rebRun("append [copy reduce]", word); acts like:

do compose [append [copy reduce] (word)]

They may have forgotten they were in C (or JavaScript, or whatever), and at runtime in that language the variable names are gone. There is no "word"...just as "word" is gone after the COMPOSE is finished.

Though they may grumble...at least they'd understand. On the other hand, they might get pretty mad if they get ['10 '20] out of the following if x and y are plain INTEGER!

REBVAL *coordinate = rebRun("reduce [", x, y, "]");

This is why I think the Q is important to have somewhere . If they'd used rebRunQ(), they feel more like "oh, I asked for that...didn't I!"

So the way I look at it is that if someone studies the issues and decides they want to use the Q APIs by default and never touch the non-Q ones, then that's their business. If someone goes the other way, they can do that. But I think looking at what kind of code you're writing and choosing appropriately makes the most sense...which was the direction I'd been headed in.

COMPOSE may be able to benefit from this, too

COMPOSE can use .QUOTE (today, /QUOTE) as a "predicate" to tell it to quote the things it put in slots:

 >> word: first [print]
 >> compose .quote [append [copy reduce] (word)]
 == [append [copy reduce] 'print]

Though it's pretty lightweight as is to say '(...) on your slots instead of just (...), you still might want a specialization for this if you had a lot of them. Maybe called composeQ to line up with the API (I've wondered about doing mixed-case names like that, as I'm not crazy about compose-q or composeq or qcompose.)

In any case, working with compose lately got me seeing it through the lens of "how much better do you expect a C API to a language to be than the language itself." I think it's gotten to right about where it can be, and at this point there's more value in having the API stable than in tweaking it more. So this is what I'm ready to commit to for Beta/One.


Quoting Ergonomics in the API -- Solved with Terminology?
Should WORD!, PATH!, FUNCTION! be "live" by default in rebDo()
#2

Something I mentioned in chat is that a feature that put this decision over the top for me was soft-quoted-branching.

Quick refresher: that's the feature which means you get this behavior:

>> if true [1 + 2]
== 3

>> if true '[1 + 2]
== [1 + 2]

This means that within the framework of the language, you understand that branches of conditionals are one of those things you understand to be quoted by default. If they weren't, they wouldn't be able to see the quote... it would be stripped off when they received their evaluative parameter.

I like this feature for many reasons. It makes EITHER and IF...ELSE fully interchangeable, and not only can your code be shorter, but it can be more performant in CPU and GC/memory usage. I mentioned in chat that where you used to have to write:

 compose/deep/only [if condition [(block)]]

You can get this with the updated COMPOSE splicing rule simply by saying:

 compose [if condition '(block)]

So far, I think the benefits more than outweigh any drawbacks. But consider what you expect now when you say:

rebRun("if condition", some_block);

I don't know about you, but I expect that to run the block as code. My eyes don't pick up any quoting going on here. So if the API were doing it automatically based on some expectation that every slot will be evaluative, that's going to be wrong. It would seem like a bug.

But what if I wrote either of:

rebRunQ("if condition", some_block);
rebRun("if condition", rebQ(some_block));

If I wound up with the block as-is in this case, it would feel like a feature. I'm in control of where the quotes are showing up, so when the language construct acts like there's a quote there I'm not surprised.

So if anyone is unhappy that rebRun("first", some_group) runs the GROUP! as if it had been written there literally...they can blame soft quoted branches for it. They may decide to become a Q-ist, and always use rebRunQ, rebSpellQ, etc. "just to be safe". Until they find out it isn't really safe in any general sense, and then perhaps they'll do what I'm suggesting...to use the right mix of operators for the job, based on what kind of code you're executing.