Fallback
Microsoft tried your solution, it's called condtionnal compilation. Now, they are removing that from HTML because it's a bad pattern.
A better solution would be to change the way a code break when he encounter a syntax error.
let supportsBlockLambda = true;
try { let l = {|| true}; } catch (ex) { supportsBlockLambda=false; }
function b() { return return 3; }
alert("Hello"); let x;
if (supportsBlockLambda) {
x = {|| 3};
} else {
x = function() { return 3; }
}
could compile like this :
// browser does't support "block-lambda", nor "return return"
let supportsBlockLambda = true;
try { throw new InvalidProgramException(); } catch (ex) {
supportsBlockLambda=false; }
function b() { throw new InvalidProgramException(); }
alert("Hello"); let x;
if (supportsBlockLambda) {
throw new InvalidProgramException();
} else {
x = function() { return 3; }
}
or this :
// browser does support "block-lambda", but not "return return"
let supportsBlockLambda = true;
try { let l = {|| true}; } catch (ex) { supportsBlockLambda=false; }
function b() { throw new InvalidProgramException(); }
alert("Hello"); let x;
if (supportsBlockLambda) {
x = {|| 3};
} else {
x = function() { return 3; }
}
The idea would be that each time the parser hit an issue, it replaces the current block by a { throw new InvalidProgramException(); } but continue to parse. It may also write a warning into the console. This is roughly how CSS is working. The idea is not of me, but from Ian Hickson if I remember correctly.
-----Message d'origine---
On Fri, Feb 10, 2012 at 11:51 AM, François REMY <fremycompany_pub at yahoo.fr> wrote:
The idea would be that each time the parser hit an issue, it replaces the current block by a { throw new InvalidProgramException(); } but continue to parse. It may also write a warning into the console. This is roughly how CSS is working. The idea is not of me, but from Ian Hickson if I remember correctly.
I can appreciate the try-catch block (vs functions or modules). But you can't just replace a block of unknown code because you won't know where it ends. So you'll need some kind of delimiter, regardless.
A block starts and ends with a bracket pair. The parser should just continue to parse tokens (strings, identifiers, operators, ...) until it reach the final bracket of the group. All of the newly introduced syntax will not break the principle of {} parity, expept quasi-literals that could, in theory. But, if they does, they'll make the next block broken, until the core program block will break, too.
/script block/ { module A {
function getBracket() {
return convert`}`;
}
}
}
would compile, in a browser that don't support quasis, as
/script block/ { throw new InvalidProgramException(); }
Function B ends just before the second , which is invalid in the module; the module then ends where the function should have ended, and is replace by a throw-block given the
error. Then, the closing statement of the module
is reached and found invalid, so the whole script is marked invalid and
replaced by a throw-block.
This can't solve all problems but we can reasonably think that any newly introduced syntax after quasi-literals will not break the bracket parity by introducing a context where they not delimiters. A parser that recognizes all tokens where brackets are not delimiter will have no problem to find where the blocks are ending.
In order to avoid the web to break, this would however need to be an opt-in behavior (ie: "use block-syntax").
-----Message d'origine---
There's currently no way of introducing anything new into the language that breaks syntax. I think that point has been made very clearly with harmony/es6. Can we introduce a way to make these transitions easier in the future?
CSS has a very simple way of gracefully ignore rules it doesn't know. In CSS you can specify rules and features and whatever the engine doesn't understand, it will ignore gracefully and continue. While this obviously won't work out of the box for js. But I think we can work around it. In CSS this is scoped to individual rules. In js there's no notion of rules (and "lines" won't work either) but you could talk about functions or modules (or blocks?) in the same way. I think modules are a bit too big scoped for this but I'd like to discuss the generic idea first. We can always bikeshed over the syntax later. I'll work with function-like syntax for now.
Say a browser wanted to support a new keyword. Right now, that's impossible to even experiment with unless you write an entire engine in js and have a duplicate source code for fallback. You simply can't do something like
x ||= y;
without breaking the code permanently and without exception. We can only add stuff that fits perfectly within the existing grammar. There's also no way for a vendor to implement this as an experiment because it will still break in older versions of the browser or other vendors. So what if we could do something like this?
function foo(x,y){ "if es7"; return x ||= y; "/es7"; } // the "/directive" serves as a delimiter because otherwise you'd never know where to stop parsing when you don't support it if (!foo) { var foo = function(){ x = x || y; return x; } };
So now you have a way to experiment with the new extension and a simple way of extending the language permanently, while still having backwards compat (at least up to the point that we introduce this system), graceful failures and a proper way of figuring out a fallback. If a vendor did not support the feature asked for it could declare
foo
as null or something allowing easy support detection.The declared function should work within the existing js language of course and should be callable from within js with the regular mechanics. This includes stuff like call, apply, and bind. Whatever the function itself does with the arguments or the context is up to the implementation. Whatever it returns should again be a proper js value. This allows you to easily fallback to an "es5" function because the fingerprint would be the same (and besides, I have no idea how else this could work otherwise anyways).
This also could allow introduction of vendor prefixes. However, for now I'd like to focus on the general principle though and leave the prefix discussion for later.
Some syntax possibilities:
function foo(){ "es7"; x ||= y; } function foo(){ "es7"; x ||= y; "/es7"; } condFunction foo(){ "es7"; x ||= y; } condFunction "es7" foo(){ x ||= y; } condFunction "es7" foo(){ x ||= y; } "es7";
Personally, I'd prefer a function-scoped capability over a module-scoped capability because I think I'd like to apply this to smaller snippets rather than an entire module. Or maybe both.
Open things for me:
If we think such a system is viable and we can find a simple way of introducing it to the language, we should do it sooner than later. The sooner it's in, the less "stuck" we will be in the future.