Different Environment Lookup for WORD! vs. SET-WORD!

In thinking about whether or not there's something intrinsically necessary about a per-block (or per-group) differentiation of word-binding, I thought of a case where I'd used this.

In SPECIALIZE, it only binds the SET-WORD!s to the frame you are specializing. Regular words (and anything else, refinements, etc.) are left bound as they were:

>> value: [d e]

>> apde: specialize :append [
       value: value  ; SET-WORD! is bound to specialization's frame
   ]

>> apde [a b c]
== [a b c [d e]]

This saves you from the COMPOSE step you'd need otherwise, which is what you have to do with objects:

>> value: [d e]

>> obj: make object! [value: value]
** Script Error: value is ~ isotope

>> obj: make object! compose [value: (value)]
== make object! [
    value: [d e]
]

I'm not sure it's a critical feature to preserve. If it turned out some alternative conception of binding wouldn't permit the inconsistency... and that conception offered some great advantage... I'd be willing to lose it. There are legitimate arguments against doing such trickery behind people's backs, maybe the COMPOSE helps you keep it straight:

>> value: [d e]

>> ap5: specialize :append [
       value: 5
       assert [value = 5]  ; assertion failure, but I just set it!
   ]

But I wanted to catalog it as a case of "arbitrary binding logic" that's in place at present.

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:

  1. coalesce with another environment

  2. 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.

One thing you’ve glossed over… at least in my presentation, individual words can still receive bindings! (Even though they’re unbound by default.) So it’s still possible to traverse over the BLOCK binding all the SET-WORD!s as you go. It would require a traverse, but it’s possible if this is really something worth preserving.

1 Like

2 posts were merged into an existing topic: What Dialects Need From Binding

So the way this is done now, SPECIALIZE uses the same logic as things like APPLY and CONSTRUCT. It does the evaluations one step at a time, with its own lookup for the keys--as opposed to throwing it to EVAL and binding to figure out.

That's because it has turned out that having this be done through binding is incompatible with the premise of how "parts-of-speech" are handled by SET and GET.

I noticed there was a problem early on, because the way that the layering was working, the moment of asking the binding question was happening after the "setness" had been stripped off of the word.

But now this is the norm: you are expected to use the structure of the cells themselves as the "API" for asking for their values. So you are adding and removing decorations to say "I want the undecayed version" by adding a caret (^META)

So saying "decoration changes the binding" no longer seems a good idea.

If you want to throw code over the fence and say "Hey, evaluator, you take care of this" vs. implementing your own dialected evaluation, I think this is what you have to do.

But as COMPOSE gets more powerful I think that will be the escape hatch. If you are willing to "phase" your design then value: (value) or value: $value can be the cue to a pass which uses one set of bindings before passing through to the evaluator.

Put another way: Contorting binding itself to anticipate this would result in something harder to characterize than just using explicit phases.