Right now, when you capture a binding to a WORD!, if it's not bound at that moment, it won't be found later:
>> word: $foo ; imagine foo is not defined anywhere
== foo
>> get word
** Error: foo is not bound
>> set (extend lib 'foo) 1020 ; add a FOO definition to lib module
== 1020
>> get word
** Error: foo is not bound
This is because at the moment you wrote $foo
, it looked in the current context (which in a case like this in the console includes LIB)... and didn't find it.
Since it didn't find it at the $foo
evaluation moment, the word you got was unbound. It won't magically become bound if something comes along later.
Should Things Like WORD! Store Binding Environments?
It would change things. Would it change them for good?
Function Arg Survival Needs It Due To FRAME! Lensing
This is what brought the issue to my attention right now (though I've been thinking about it a fairly long time).
The model in which WORD! gets bound to a FRAME! only stores the pointer to the frame, not the Lens that's in effect.
The only way you can know if your "lens" should account for if the function is actually running a body or not is by information carried in the binding chain. Knowing only the final result--a pointer to the list of variables--is not enough. You need the specifics of how the result was reached--to know which phase should be considered for lookup of the word.
Implementation Simplification
A win is that there isn't a distinct "kind" of binding for WORD!s that's different from BLOCK!s. Everything that stores binding would do so in a common way. That simplifies code.
Optimization Loss Of Multiple Gets
It does mean losing an optimization on if GET of the same bound word is done multiple times.
The ability to leverage that optimization has become punishingly rare. Basically every WORD! in the "mostly-unbound world" already can't do much in the way of optimized lookups.
You'd get the benefit that the laziness would help if you didn't actually need to do the lookup resolution. The "binding capture" is cheap-as-free (just pokes a pointer into a Cell).
This Leaks Much More Information... But...
I've written about binding leakage before. This does make it worse, if you pass someone a WORD! that's bound you'd be passing a lot more information.
But it's always been a problem. Passing someone a WORD! bound into an object has allowed you to get that object and crawl arbitrarily far, if you have a BINDING OF tool to extract it.
I rarely get bent out of shape over the security implications of these things. But more just the garbage collector implications.
But I can't get too worked up over if you pass [a] instead of just a having all the same problems.
Biggest Potential Complaint: Lost Invariant
I showed a case where how it would work could change:
>> word: $foo ; imagine foo is not defined anywhere
== foo
>> get word
** Error: foo is not bound
>> set (extend lib 'foo) 1020 ; add a FOO definition to lib module
== 1020
>> get word
== 1020 ; would work if $foo bound to an environment chain
But this has further risks. Consider this:
>> lib.foo: 1337
== 1337
>> get word
== 1337
>> foo: 304 ; created in user context (or should be...)
== 304
>> get word
== 304
>> lib.word
== 1337
When you wrote $foo
you caused it to bind into an environment chain that had the USER context first, then USER inherited LIB. So it searches USER first, then LIB.
When something was added to LIB, it saw it. But later, something added to USER could override it.
Bug, or feature?
It's a bit hard to tell. When you said $foo
you effectively said "bind into the current wacky environment, whatever that is". Had you said bind lib 'foo
you could have asked for something more specific.
Data Point: TUPLE! Must Capture Environment
When you write get $.foo
, then what happens in that capture of environment has to put something onto .foo
that's enough for GET to find what it needs.
And the current thinking is that .foo
acts like this.foo
-- looking up whatever THIS is defined to in the environment.
We don't want to put logic into the code behind $.foo
that does the capture of THIS and pokes it in some secret location of the tuple binding. It needs to just associate the whole environment with .foo
.
This really starts to make it look like WORD! should line up with everything else and capture an environment, not just do a lookup and consider that the final answer.
If this isn't what you want, the BIND operator should offer a more specific language for describing the exact bind resolving invariant you need.
This is casual, cheap (well, it cheapens the definition of what $ does to be basically free), and fixes a problem with function argument indefinite lifetime.