Block Creation Vs. Evaluation

There was a "bug" (or quirk) that the "wave of constness" was not preserved by quoting:

It was giving this behavior:

foo: func [x <local> block] [
   block: []
   return append block x
]

>> foo 10
** Access Error: CONST or iterative value (see MUTABLE): []

bar: func [x <local> block] [
    block: '[]
    return append block x
]

>> bar 10
== [10]

>> bar 20
== [10 20]

When you used a quote, you were effectively getting the behavior as if you had fetched the block from a variable. This shielded it from the propagation of <const> that was coming along the body.

This wound up being useful in cases like the API. I've mentioned before that generalized quoting is your recourse when you are splicing C variable values, and don't get the "fetched-value-is-unevaluated" protection that being able to use a WORD! variable gets you. But it also gets you the ability to bypass the const protection as a variable would allow.

Consider the ordinary code:

 >> block: [a b c]

 >> repeat 2 [append block 'd]  ; first form
 == [a b c d d]

 >> repeat 2 [append [a b c] 'd]  ; second form
 ** Access Error: CONST or iterative value (see MUTABLE): [a b c]

Then consider the API code:

 REBVAL* block = rebValue("[a b c]");

 rebElide("repeat 2 [append", block, "'d]");  // errors

While the intent is the first form, the interpreter experiences it as the second form.

So I kept the behavior of quoting being able to bypass this, instead of propagating the const bit:

 REBVAL* block = rebValue("[a b c]");

 rebElide("repeat 2 [append", rebQ(block), "'d]");  // works

But that means that this also works:

 >> repeat 2 [append '[a b c] 'd]
 [a b c d d]

It does open some doors to bugs, e.g. if a function does return '(1 + 2) then that group will be mutable... and if the caller appends to it then you get self-modifying code to make return '(1 + 2 + 3) or whatever. :frowning:

But, it's a tradeoff. You could try to teach people to use the (1 + 2) and save quoting for special cases (today quoted arguments still get the constness, maybe they shouldn't and behave with parity to QUOTED!). Another possibility might be to shift to a "const by default" system where you'd have to make your return values or arguments <mutable>, else they'd be const.

This has actually crossed my mind before... inspired by the const issue... whether quoting could be used as a kind of "binding shield".

Basically the idea of extending things so that quoting always acts the same as if you fetched the thing that's quoted from a variable. It would preserve what binding it has on it, if any. In a model where things are unbound by default, this would mean they'd frequently be unbound if they originated in source (vs. from a QUOTE or rebQ() operation).

By and large I do find it appealing if "stray" bindings are less common. There are a lot of cases where you want to pass a word somewhere and it's really just meant as a word and not a variable, but you pick up bindings anyway. Same with blocks that come with all kinds of bindings you don't actually want.

But generalizing this breaks a lot of current understandings. This is expected to work, today:

>> foo: lambda [x] [get 'x]

>> foo 10
== 10

New models with completely new understandings might say you need an operator to put a binding on:

>> foo: lambda [x] [get 'x]

>> foo 10
** Error: x is unbound

>> foo: lambda [x] [get bind-to-current 'x]

>> foo 10
== 10

(If BIND-TO-CURRENT is needed often it should have a snappier name. Perhaps this arity-1 operator would even be common enough to take over the name BIND... who knows.)

Or perhaps--again--using the instead of quoting would act different (and again, maybe a bad idea if quoted args don't have parity).

Anyway, just connecting it to a previous issue where quoting stepped in as a surrogate for reference by variable.