Closed JHawkley closed 6 years ago
(if (true) "foo"; else "bar";)
is a syntax error because if-statements are not expressions. This is the reason we're creating do-expressions.
But, if (true) "foo"; else "bar";
evaluates to "foo"
. We're also aware of other TCP violations, there should be open issues for them.
Yes, specifically:
The proposal currently asserts the following:
do { <expr>; }
is equivalent to<expr>
So, then these two blocks should evaluate to the same value.do { if (true) "foo"; else "bar"; }
(if (true) "foo"; else "bar";)
Because if (true) "foo"; else "bar";
is not an expression, the claim you've highlighted from the readme does not apply.
So, then why should if-else
suddenly act like an expression in a do
block?
I'm not what you mean by "act like an expression".
The if-else
construct is a statement and not an expression. It shouldn't evaluate to anything, whether in a do
block or not. According to the claim I highlighted from the readme, using if-else
in a do
as done in the examples I provided should be an error, since it is not an expression.
So, am I to understand that this proposal is actually to create a special dialect of ECMAScript that only exists between the brackets of a do
block? A dialect where the various flow-control structures are expressions instead of statements?
@JHawkley it already does; see eval("if (true) 'foo'; else 'bar';")
.
The code I provided was not intended to be executed in the context of eval()
; the specialized behavior of eval()
should not apply. When executed in a normal context, ECMAScript does not behave the way it does in eval()
.
"should not apply" seems like an arbitrary designation; eval is part of the language.
eval()
is a function. It is defined within the language.
According to the claim I highlighted from the readme, using if-else in a do as done in the examples I provided should be an error, since it is not an expression.
No, the claim you highlighted does not say anything about statements, positive or negative. But it is immediately followed by one:
(do { <stmt> };)
equivalent to{ <stmt> }
which holds.
So, am I to understand that this proposal is actually to create a special dialect of ECMAScript that only exists between the brackets of a do block? A dialect where the various flow-control structures are expressions instead of statements?
They continue to be statements. They have completion values, which is already the case, but are not usable in expression position. But more broadly, it is true that the goal of this proposal is to allow you to use statements in expression position by wrapping them in do
, yes.
eval()
is a function. It is defined within the language.
Separately, no, eval
is magic and cannot be defined within the language; it must be a core part of it, although its magic isn't really the relevant part here.
No, the claim you highlighted does not say anything about statements, positive or negative.
Indeed. But a statement is still not an expression, so the current behavior of statements within a do
is an assumption and that assumption goes against how the rest of the language operates. Is that not a problem?
(do { <stmt> };)
is equivalent to{ <stmt> }
I wasn't surrounding my do
in parenthesis. Why would this assertion apply?
Separately, no,
eval
is magic and cannot be defined within the language; it must be a core part of it, although its magic isn't really the relevant part here.
To be clear, my argument is: eval
executes ECMAScript code differently from the way the language actually specifies it should be executed. eval
's behavior of evaluating a statement to its completion-value is not part of the semantics of the language itself.
The existence of eval
may be specified by the language, but its behavior is a special-case. The whole rest of the language does not work as it does in an eval()
context.
Using this one thing to justify the behavior of do
while the rest of the language screams "no!" seems foolish. And it still wouldn't correct the TCP violations. In fact, this behavior would be their cause.
is not part of the semantics of the language itself.
Yes, it is; the spec includes Completion Records from evaluating every statement; it's just that it's currently only exposed for non-expressions in eval
.
Yes, the spec includes the notion of completion values. But the issue is that other part: "it's just that it's currently only exposed for non-expressions in eval
."
Developers don't build their applications in an eval
context, though, so the behavior of code in that context would not be familiar to them. And to have a block where the semantics and behavior of the language suddenly change is irresponsible. This is why TCP is touted as a big deal in these proposals; to avoid bad language design like this.
Why not just expose completion-values for non-expressions everywhere? If the different semantics are applied everywhere, there is no TCP violation. As was already stated elsewhere, we don't need do
if we do this.
There could probably be a feature for opting in to it (and other breaking language features) with something like an import evalLanguageFeatures
at the top of your source file. That would keep the proposal from breaking existing code on the internet, and we would get something better than just do
.
The only other thing I would be comfortable with is to just formalize IIFEs. These are already the idiomatic way to solve the problem that do
is trying to solve anyways.
It isn't as deep into expression-oriented programming as some people would like, but the language would continue to work the same and all the semantics and behaviors that a developer is already familiar with remain unchanged.
All the problems with implementing the current do
syntax go away when you just make everything work the same way arrow-functions already work, and having to type return
isn't that bad.
Something like:
const input = 2;
const command = do => {
switch(input) {
case 1: return "run";
case 2: return "jump";
default: return "idle";
}
}
This would be a very familiar syntax for any developer familiar with arrow-functions. And to avoid function-call overhead, you just assert that the body of the do =>
must be inline-optimized in the proposal.
But, this discussion has so far not convinced me that the current proposal would be a good change for ECMAScript. I definitely want something like this; I'm a Scala developer so I know how nice expression-oriented programming is, but the current implementation feels too hazardous. The do
block is just too "special".
One of these two options would be much more responsible.
The point you bring up is valid, but not a violation of TCP. do expressions do not change any semantics of the surrounding language. It's literally like Return the result of evaluating BlockStatement
.
The real issue you're having is that to most users of JS, statements have no "return" value. But even in this case, it has always been exposed in one way or another. The language has eval
and Realm.eval
, node has vm
, etc.
Aye, I now agree. After analyzing it for a day, I realize my misunderstanding.
Since I no longer consider this an issue, I suppose I'll close this.
However, I did come to some new conclusions and wrote up a document describing them. I'll put these into a new issue, though. It's a long read, but I hope it will provide useful information.
Based on what I've seen of the Babel plugin and the examples provided here, I feel that the
do
expression is causing a massive violation of Tennant's Correspondence Principle. Let me go through an exercise to try and explain my concerns.The proposal currently asserts the following:
do { <expr>; }
is equivalent to<expr>
So, then these two blocks should evaluate to the same value.
do { if (true) "foo"; else "bar"; }
(if (true) "foo"; else "bar";)
But they do not. Refactoring the contents of the
do
out changes the behavior of theif-else
statement. Inside thedo
it evaluates to "foo" while outside thedo
it becomes a syntax error.This would mean that:
do { if (<cond>) <a>; else <b>; }
is not equivalent toif (<cond>) <a>; else <b>;
...and that is a contradiction of the original assertion.Am I just misunderstanding something here?