I'm realizing that one way or another, I've been talking about these two choices:
-
don't project the binding from the container, but keep the binding that was on the item if it had one
-
project the binding from the container if it doesn't have a binding, else keep it as-is
I'm kind of skeptical how useful (1) really is. You're at risk of getting unbound material, but you don't always get unbound material. What sort of situations motivate this?
If you really have a situation that's "(1)-like"...then it seems most of the time...the container itself should be unbound. If that wasn't the case and the container had a binding, what exactly is your justification for ignoring it and not using (2)?
So this is steering me toward thinking that (2) would be the default, hence your hardening here could simply be:
harden-bindings: lambda [block] [
map-each value block [value]
]
But recognizing (1) as probably-useless has a counterpoint: what if you explicitly want unbound material? How about that's where we use the quote mark?
no-bindings: lambda [block] [
map-each 'value block [value]
]
The idea that you can trust what you get back isn't bound helps squelch stray bindings where they aren't meaningful (at the risk of ignoring a binding that was meaningful). There's aspects of ignoring binding that makes me uneasy, but at least if you're the chokepoint for this decision you stop spreading the meaninglessness past the point where you didn't intend it.
And this has a certain technical advantage... iterators only need to have one return modality. The thing that's feeding the MAP-EACH or whatever always propagates bindings where it makes sense, and then it's the SET assignment argument that tells the variable whether to rip it off or not. So you're not having to a feed a "don't propagate bindings" flag into the informational request, it's dropped uniformly on assignment.
>> var1: $x
== x ; bound
>> var1
== x ; bound
>> ['var2]: $x
== x ; bound
>> var2
== x ; unbound
(Note that obviously can't be 'var2: var1 because the quote would quote the SET-WORD, hence the SET-BLOCK is needed as a container).
Tying This Back To PARSE Rules
Above I said I'm leaning to a default where:
-
FOR-EACH always propagates binding from container (or leaves binding as is, if something already has it)
-
If that's not what you want it's your job to either:
-
Unbind the container before asking for elements out of it
-
Unbind the thing you get back after the bindings are composed
- We help make this easier with quoted iterative variables requesting the unbind
-
So let's look at the original problem in PARSE (rewritten to use FENCE!...)
I feel like the default might be that you get the binding propagation and that "just works".
If you want to strip off the binding (let's say to avoid the OBJECT! bound keys ambiguity) you would say:
>> parse [word: 10] {
word: unbind set-word! val: integer! (
make object! compose [(word): val]
)
}
>> parse [word: 10] { ; ...alternatively
word: set-word! val: integer! (
make object! compose [(unbind word): val]
)
}
Or since this is an assignment context, you might prefer the quoted-assign trick I suggest, for cuing SET to strip the binding:
>> parse [word: 10] {
['word]: set-word! val: integer! (
make object! compose [(word): val]
)
}
Is This... Err... "Bind The Galaxy"?
![]()
Whatever this is... leads to a more historical-Rebol-compatible model, spreading bindings more places (just one step at a time).
But if you have discipline of using quoted blocks as containers when bindings aren't actually meaningful, that would probably mitigate a lot of the damage.
The one nit is that Sigil composition issue: no obvious way to say "reuse variable, but reuse it ^META".
for-each var [a b c] [...] ; bind propagate, new var
for-each $var [a b c] [...] ; bind propagate, reuse var
for-each 'var [a b c] [...] ; unbind result, new var
for-each '$var [a b c] [...] ; unbind result, reuse var
for-each ^var [a b c] [...] ; bind propagate, new var, meta
for-each '^var [a b c] [...] ; unbind result, new var, meta
??? ; bind propagate, reuse var, meta
??? ; unbind result, reuse var, meta
Not the end of the world if reuse is distribute across BLOCK!s used for iteration vars:
for-each $[^var] [a b c] [...] ; bind propagate, reuse var, meta
for-each '$[^var] [a b c] [...] ; unbind result, reuse var, meta
I feel a bit less bad about that than I would saying meta is distributive:
for-each ^[$var] [a b c] [...] ; bind propagate, reuse var, meta
for-each '^[$var] [a b c] [...] ; unbind result, reuse var, meta
Something about that puts me off. I guess because we're used to binding being controlled invisibly from "outside", but not meta semantics. So I probably would avoid implementing that.