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
This requires JS programmers to learn a bunch of trivia #21
Comments
The developer console has the same semantics, and I’d argue that most programmers do know/learn that in practice. |
Surely you're not arguing that programmers already know that |
The completion values of loops have been discussed elsewhere. The completion value of behavior of every other statement with the exception of function or class declarations are, in my opinion, very intuitive. |
Well, maybe we just disagree, but I was born without any real intuition for whether All that stuff seems pretty arbitrary, to me. Maybe in practice it just won't come up. I agree it's loops and declarations that will actually surprise people the most often. |
It would be clearer if do-expressions had an explicit syntax to mark the produced value, à la let name = do {
for (const book of books) {
if (isRecommended(book)) {
use book.title;
}
}
}; |
@claudepache that misses the point of do expressions almost entirely. |
Here's a fun case. Empty Blocks usually don't affect the value: eval("3; {} {} {}"); // returns 3 But if you "comment one out" using eval("3; {} {} if (false) {}"); // returns undefined I love it: an empty statement has no side effects, thank goodness—unless you make it not execute... |
It isn't the not executed block that has a side-effect (the completion value) it is the This is part of ES6 "completion reform" championed originally by @dherman. Essentially completion reform was intended to make completion values easier to understand/analyze by ensuring that all statement list elements either always produce a normal completion value or never produce a normal completion value I(they just propagate the previous completion value). Previously |
Thanks for the background, Allen. As long as Edit: I don't mean to argue with anyone or blame anyone by saying that. Just a bit stuck for how to proceed from here... |
Well |
You're right, it's more static than I thought. Is the idea that in |
That's the behavior I'd expect from that particular code. |
For So: |
So
|
Perhaps a better equivalence: {1; 2 && do { break; }} is equivalent to: {1;
if (2) {break}
} is equivalent to: {1;
if (2) break
} |
This is where some new design is required. Currently (I believe) there is no way for a subexpression to evaluate to The do expression So, here is how we might achieve this. Let's assume for now that subexpressions that evaluate to But, we would also need to look carefully at all the uses of GetValue (and there are a lot) to find any cases where we wouldn't want to do that conversion. There is one that immediately comes to mind: ExpressionStatement evaluation:
To maintain Tennant's correspondence with Block it would need to change to:
|
Just to clarify: it seems like in this comment you're changing your mind, saying that the result should be undefined, not 1. Right? If so, I think I agree this is the only design consistent with completion value reform. Without this, we're right back in the situation described here:
because there would be cases like |
Heh! The very first instance of GetValue() in the spec is in array initializers. I assume we want Design decisions around every corner! |
Interesting, referring back to the spec. I see that the behavior of In 2015: { 1;
if (true) break;
} produces the completion value for the block: [[Type]: break, [[Value]]: 1 and { 1;
if (true) ;
} produces the completion value for the block: [[Type]: Normal, [[Value]]: undefined. In the ES2018 draft, they respectively produce: The latter is consistent with what Jason is saying. However, the algorithm conventions for manipulating completion results changed between those versions of the spec. It isn't obvious to me that the semantic change was intentional or a bug introduced when rewriting using the new conventions. It probably should be researched to see if the 2015 behavior had been identified as a bug and the change was an intentional fix. The bigger question is what (if anything) was the intent of "completion reform" for cases like this. It's pretty clear that a goal of completion reform was that during linear progress, each statement either always or never produces a new completion value. But abrupt completions are really a separate dimensions of completion value propagation, so it isn't obvious that the always/never rule should apply to it. In seems strange to me that: { 1;
break;
} would have a completion value of { 1;
if (true) break;
} has a completion value of |
I agree! But consider: while (true) {
1;
if (cond) break;
break;
} In the current draft, the value of this block is undefined either way. What did it do in ES2015? I think it would be 1 if cond is true, and undefined if cond is false. This seems strange. So both semantics have some strange cases. I think if I could pin down what we mean by "static" here, I would understand better where the strangeness is coming from. |
So, the if behavior was a ES2016 breaking change tc39/ecma262#1085 (comment) |
I think pretty much all of the trivia goes away if we change abrupt break and continue completion so that they always have [[Value]]: undefined. |
That would of course be a breaking change. It would make the rules a snap to reason about, though:
|
Oh, I guess actually getting the above rules would involve changing |
Strictly speaking, we're not required to use the same completion value semantics that |
Another fun example of how evaluating to the last expression will generate a lot of confussion const result = do {
switch (val) {
case 'foo': 1;
case 'bar': 2;
}
} What does |
Intuitively, the only options are either “always undefined”, or “1, 2, or undefined, based on What confusion am i missing? |
@ljharb, I assume the point is that there's no |
Aha, fair - although that’s a hazard of switch statements themselves, and not something new for do expressions. |
What do you mean by intuitive? The idea of having to insert code into a repl in order to know what it does seems like a pretty bad experience as a developer.
This is not a hazard for switch statements, it just shows a limitation in the proposal, being unable to resolve to a given expression without any ambiguity leads to a number of possible mistakes, take for instance the extremely failed |
There isn’t any ambiguity with switch; it’s just not intuitive, because switch itself in JS is an unintuitive construct. |
@eloytoro We have eslint, and this kind of code will be warned. For me, do expression helps me to build template engine that non-coder can understand and use. |
Eslint Is not part of the spec, it can't be used as an argument for
negligence
…On Fri, Apr 27, 2018, 8:34 AM lin onetwo ***@***.***> wrote:
@eloytoro <https://github.com/eloytoro> We have eslint, and this kind of
code will be warned.
For me, do expression helps me to build template engine that non-coder can
understand and use.
Non-coder may be difficult to understand let and const, but they can
easily understand "oh value inside { } is just what I will get"
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#21 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ADwSBX5yJ8OA9hR5YGC_fAnMFHrNB_YSks5tsx5-gaJpZM4RjL_Y>
.
|
One possible solution for this is to syntactically require that the last statement in the Or, at least, not a loop or declaration. |
As it stands, you can be a very good JS programmer and not even know that statements produce values.
If
do
expressions are added, programmers will have to understand the rules in some detail:In the spec, the way these statements produce a value is all faux-functional, but to programmers, that
book.title
is implicitly stored in a nameless variable, and implicitly read out of it later. That's spooky—and while it's trueeval
already has all these rules baked into it today, in practice nobody has to know.The text was updated successfully, but these errors were encountered: