The way this feature is accomplished today isn't by deep-walking the block and binding SET-WORD!s and WORD!s differently. It's with a specifier "instruction" that is put in the chain that says "propagate a binding, but only for SET-WORD!s". It propagates down through the evaluator as each block is extracted.
There's no user exposure of that feature... it's something that only SPECIALIZE (and the esoteric APPLIQUE) can do at the moment.
@bradrn has stated aspirations to make environments/specifiers "first class values", so instructions like this for hole-punching, or narrowing the categories of words...would make those values weirder.
I've thought to conceptualize "coalesced binding" (now manifest as IN) as basically just a function: I give you something that may or may not be bound already with some shape of that binding, you give me a newly shaped thing. This newly shaped thing is a "binding machine" that needs to know how to do two things:
-
coalesce with another environment
-
turn a value into something that can be looked up with GET and assigned with SET, or leave it alone
We'll be discussing this more... but it's within the realm of possibility to say that the ultimate desired implementation will be able to target discerned binding in the vein of this "SET-WORD!-only" rule.
Regardless, the mechanism from today should keep working when I take a second shot at "pure virtual binding" using the new perspectives @bradrn and I have been hammering out. (The trick will be rewriting all the usermode dialects to use IN everywhere...correctly.)
But Just Because We Can Do This, Should SPECIALIZE Do It?
I wanted to break this out into a separate topic to specifically discuss the question of if this is a good idea or not.
I've pointed out something that doesn't have such a good look:
>> value: [d e]
>> ap5: specialize :append [
value: 5
assert [value = 5] ; assertion failure, but I just set it!
]
We're embracing the idea that in a pinch, you can go to the level of binding things explicitly wherever you want to, but should basic constructs avoid being this weird?
There are places in other languages where kind of parallel things are seen as advantageous:
struct Foo {
int x;
Foo (int x) : x {x} {
// x {x} initializes member x with constructor's argument
}
};
I'm kind of ambivalent about it in SPECIALIZE. It's sneaky. But when I think about striking it, I think about how annoying the need for a COMPOSE is.