-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fully explain variable redefinition across inputs #3
Comments
cc @ajklein |
cc @ak239 |
My goals are to have variable bindings with a few specific use cases:
If you can use the history to declare a variable once, it should operate the same way when run a second time. For example, having multiple imports
If you run some code such as
A REPL such as DevTools or Node should be somewhat safe as a sandbox allowing creation of any necessary bindings. If people wish to affect global scope there are alternative ways to do so, such as indirect eval. This is particularly relevant if combined with the ability to clobber bindings. It would be very new behavior for a With those in mind, I would prefer the use a new Environment Record to store bindings created at top level of REPL input. |
Thanks for proposing this, this has been one of my biggest pet peeves for a long time. First, a clarification. Does "just work" mean clobbering semantics? It will override the old binding in the environment? The shadowing semantics is the most ergonomic to me.
The biggest con I see, given that the REPL in the browser is often used in conjunction with debugging facilities, giving an N-line REPL a >=N-length environment chain might be unfortunate. I don't think that's a very big deal. |
That matches what I had in mind too. Just chaining toplevel let scopes (ScriptContext in V8) but allowing shadowing for REPL seems fine. |
IDK, somewhat flexible as long as we have a reasonable explanation.
That was my expectation, yes. It would give TDZs proper behavior in isolated inputs. |
I just realized the one surprising thing about chaining environments is it'll make REPL> |
That's what I was suggesting above. Shadowing needs to be allowed. Even for repeated |
@hashseed Oh sorry! That's what you meant by "shadowing". |
One thing that I am a bit concerned about mentally with shadowing and forming an environment record chain is closures: // input 1
let _ = 1;
function print() {
console.log(_);
} // input 2
let _ = 2;
print(); With shadowing I would think the example will output |
Shadowing would only affect toplevel let. Toplevel var are global properties. |
@hashseed changed my example to use |
Guess what I would say is... don't use let if you don't want sensible scoping :) |
Honestly I can see people being divided on whether the example should print 1 or 2. Both intuitions are somewhat reasonable, but I think I lean towards what @hashseed said. The group of people who expect let to be able to redeclare previous lets in the same scope when using a REPL must keep two sets of semantics in mind. It’s definitely more cognitive burden. |
With the model of nesting / shadowing, we just discussed a potential issue that we need to resolve around REPL being sloppy by default and allowing < 0
> 0
< 1
> 1 {
"use strict";
0
{
1
}
} Would have the input |
Something interesting about the function createChainableEvalContext(script) {
function ___createScript(script) {
return `
let ___nextChainedEval;
const __value = eval(\`
{
___nextChainedEval = s => eval(___createScript(s));
undefined; // If script is empty then eval() value should be
// undefined not ___nextChainedEval
${ script }
}
\`);
({ nextChainEval: ___nextChainedEval, result: __value });
`
}
return eval(___createScript(script))
}
class ChainEval {
_chain = [createChainableEvalContext(``).nextChainEval];
eval(script) {
const currentChainEval = this._chain[this._chain.length - 1];
const { result, nextChainEval } = currentChainEval(script);
this._chain.push(nextChainEval)
return result;
}
popEval() {
if (this._chain.length > 1) {
this._chain.pop();
}
}
}
const chainEval = new ChainEval();
chainEval.eval(`let x = 12;`);
chainEval.eval(`let x = 30;`);
chainEval.eval(`x // 1`); // 30
chainEval.popEval() // pop `x // 1`
chainEval.popEval() // pop `let x = 30`
chainEval.eval(`x // 2`) // 12
chainEval.popEval() // pop `x // 2`
chainEval.popEval() // pop `let x = 12`
chainEval.eval(`x`) // ReferenceError |
Should What purpose does the |
Yes I updated it.
I added a comment, basically in the case the script is just whitespace or comments it makes sure that it doesn't |
I achieved this by creating a new environment record type which is a subtype of module environment records. It exists only to override two of the methods, As far as I can tell, this matches the semantics asked for above: This also means we don't run into nested strict issues: It is also a module environment, so it inherits top-level await! Changes to engine262 here, which sorta match changes to spec steps: engine262/engine262@8c107a9 |
Some differences in expectations found from talking to @hashseed
We should specify why variable bindings should work and be more specific in what ways they do work during a single input and across multiple REPL Source Text Records.
The text was updated successfully, but these errors were encountered: