`do { }` vs `do { } while ();` confusion
I'm worried that these two similar constructs are going to cause a lot of confusion in JS developer land. I foresee people being very confused with the differences, especially if a do block / do while loop is very log and they miss the end of it.
Also, I can see confusion / problems come up because of ASI, namely code like this:
do {
// something
}
while(...)
If the loop was written poorly, I could see it becoming infinite. I could also see the other side, the above code being an intentionally a do block followed by a while loop.
This would only be a problem when used in a statement position - but the value of do expressions is to use them in expression position, where a do..while loop is invalid. In your above example, without the while it'd be an error, no?
hmm...
do { 1; } while (1) { console.log(1); }
is valid code, so do blocks would break web compatability, or at least be weird to look at.
...without the
whileit'd be an error, no?
Currently, it would be, but expressions are valid where statements are, but not vice versa.
Valid statement:
1;
Therefore, "do blocks" should be valid anywhere:
do { 1; }
Maybe there's a better name for these?
"do expressions" are designed to be similar to "block expressions": you cannot use them to begin a statement.
// invalid
{ foo() {} };
// valid
({ foo() {} });
// invalid
do { 1 };
// valid
(do { 1 });
Oh, my apologies for not noticing that. Then as was already said, this problem wouldn't occur without attempting to put a do while in a rhs position.
I do think the syntax is poorly named. I understand the usefulness of creating a temporary scope, but perhaps let x = run {'val'} would be less ambiguous?
The reason for using do is that it's already a keyword which makes parsing the new syntax much easier.
I don't think do is a good name to use. Actually it increase the burden. You can not use it as the start of ExpressionStatement cause it will conflict with do statement.
If we change to another keyword we can avoid this problem, no need to add ( ) outside.
Why would want to use do at the start of an expressionstatement? Just use a block instead.
@pitaj Parsing the new syntax for current JS programmers? Future JS programmers? Parsers? It has a very different use from do while, so I think it's likely do is more ambiguous for all three groups.
The concrete advantage to using an existing keyword is that a “no line terminator here” restriction wouldn’t be necessary (to avoid ASI issues).
@ljharb I was under the impression run and a bunch of other standard words were already marked as keywords, but had no use in JS. But I just looked at that list, and run isn't on it. What about eval { 'val' }? Since eval() is provided by the language, it would be easy to overload. It's a less ambiguous overload for the parser, and it also conveys to coders much more how the new syntax would work since it has the same behavior as eval().
there’s no way anyone would want to make more uses of the word that shall not be named.
@mikestopcontinues eval is also not a keyword. let eval = () => 0; eval('1'); /* 0 */ works fine.
OK, I'll stand by run or some other new keyword. If the functionality isn't worth the trouble of a new word, is it really worth being added to the language?
Adding a new keyword causes backwards compatibility issues, new keywords are very unlikely to be accepted.
Especially something as common as run
What about how get and set are contextual keywords? eval could be the same: if it's followed by a '{', then it could create a block expression.
@00ff0000red that's already valid syntax, due to ASI:
var anyNonKeyword = 3;
var foo = anyNonKeyword
{ /* this is a block */ }
that would break if anyNonKeyword { suddenly became "the result of the block", unless there was an NLTH restriction.
That still causes ambiguous grammar when considered with other language features and automatic semicolon insertion.
const f = () => eval
{
12;
}
Currently this is an arrow function declaration followed by a block. But it becomes ambiguous under your proposal. If you replace eval with do it is unambiguous.
Personally I don't think ASI is very important in this specific case, even we use do, the mainstream coding style will be always do {(no newline between do and {), so there is no much difference in real-world practice.
Actually, the inconvenience of do expression can't be the start of ExpressionStatement may be worse:
foobar
do { ... } |> f // syntax error!
So you are forced to write
foobar // <- ASI!
(do { ... }) |> f
and introduce a real ASI hazard.
Just spitballing.
"use expr"; // modifies current context so that if statements becomes if expressions
const conditional = if (age<21) { false } else { true }
// the if expression can be spec'ed elsewhere however you want
I feel like this is the most non-breaking way to introduce new expression syntaxes without overloading keywords or introducing new ones.
Yeah, actually I think we don't have to stick on the do keyword. Why not a new one just like in pattern matching.
`match` <No LineTerminator here> Block
For example, we can use
`expr` <No LineTerminator here> Block
And some of my friends usually think "do expression" is "do notation" (Haskell) in JS
For example, we can use
`expr` <No LineTerminator here> Block
Yup, that's a possibility I'm considering. (If I go with that, I'll might also change async do { to just async {.)
That also removes the limit of do expression cannot appear as an ExpressionStatement strat
Technically true, though there is no reason to do that ever.
Yeah I like the idea of expr {...}, and maybe we could just use eval {...}, so people could simply understand it as sugar for eval("{ ... }") (though we make the eval {...} more stricter to avoid some confusion).
It's very much not sugar for that, so eval would be a very misleading name. eval's connotation is "string input", which is not the case here.
Whether it's a sugar or not, is depend on the final semantic we choose. If the semantic is closed enough,let's say, if almost all do {CODE} could be replaced by eval("{CODE}") and return the same result except those throw syntax errors, it will be treated as sugar anyway by most people in the community.
eval's connotation is "string input"
I think it's just like the case of import x from mod and import(mod).
Note I only think eval {} is a possible solution if the semantic is closed enough. If not, it of coz should not use eval.
...we could just use eval {...}, so people could simply understand it as sugar for eval("{ ... }")
Sugar for eval... that legitimately sounds awful, and I would lose all faith in ECMAScript and TC39, should the members of TC39 go forward with that idea.
But let's not have a full blown debate about eval and it's semantics, those are not what are being proposed here.