Generalize do-expressions to statements in general?

Andreas Rossberg rossberg at google.com
Tue Jul 14 07:31:30 UTC 2015


I don't see why you need parens at all, see my previous post. But I
wouldn't make the do-less forms the base syntax,; rather, only short-hands
for the general thing. In particular, because the ability to have an actual
block inside an expression is one primary motivation for having
do-expressions in the first place.

...Ah, it's 2015, and we still have to come up with ways to overcome the
archaic statement/expression distinction from the stone ages. :)

/Andreas


On 14 July 2015 at 01:33, Mark S. Miller <erights at google.com> wrote:

> Interesting. Got me thinking. Here's an alternate proposal I'll call "do
> expressions without the 'do'."
>
> At <
> https://people.mozilla.org/~jorendorff/es6-draft.html#sec-expression-statement>
> we have the syntax of the expression statement. Ignoring sloppy "let"
> nonsense, this says that an expression statement cannot begin with "{",
> "function", or "class".
>
> At <
> https://people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-language-statements-and-declarations>
> are the legal ES6 statements. Note that most of these begin with a keyword
> that cannot possibly be legal at the beginning of an expression. Therefore,
> adding all these initial-statement-keywords to the list of things that
> cannot begin an expression statement would break nothing. They already
> cannot begin an expression statement.
>
> With the expression statement prohibition in place, now we can allow all
> these forms to be expressions. As with "{", "function", or "class", if you
> want to state such an expression in expression-statement position, surround
> it with parens.
>
> Because all these new forms will look bizarre and confusing, at least at
> first, let's say these always need surrounding parens to be expressions. I
> think that would help minimize confusion.
>
> If we do this, the oddest duck is "{", since it begins an object literal
> expression. This proposal gives us no straightforward way to express an
> block expression. "function" and "class" are less odd, since their existing
> expression forms mean what you almost might expect by this new rule -- even
> though they are initial-declaration-keywords rather than
> initial-statement-keywords.
>
> The remaining initial-declaration-keywords are "let" and "const". We
> already made "let" insane regarding these issues in sloppy mode, so I'm
> going to ignore that. But let's consider "const" and strict "let". These
> already cannot appear at the beginning of an expression, so it would not
> break anything to add them to the prohibition list for the beginning of
> expression statements.
>
> No current expression can add any binding to the scope in which the
> expression appears. Let's examine the consequences of having parens --
> rather than containing a "{"-block to create a nested scope with a value
> (which would conflict with object literals), instead simply define a
> block-like nested scope with a value. This would allow declarations and
> statements within the parens, much like the current "do" proposal. It would
> even be consistent enough with the existing semantics of paren-surrounded
> function and class expressions: Someone who sees these as a function or
> class declaration within its own nested scope, whose value was the value
> being declared, would rarely be surprised by the subtle difference between
> that story and the current semantics.
>
> Having parens accept a list of declarations and statements rather than
> just an expressions seems like a radical change that must break something,
> but I can't find a problem. Am I missing something?
>
> Examples inline:
>
>
>
> On Mon, Jul 13, 2015 at 5:47 PM, Isiah Meadows <impinball at gmail.com>
> wrote:
>
>> I was reading a recent thread
>> <https://esdiscuss.org/topic/allow-try-catch-blocks-to-return-a-value> where
>> do-expressions simplified a common try-catch use case, and I was wondering
>> if `do` could be simplified to an expression? It would allow for this to be
>> solved very easily, but also add a lot more flexibility in this proposal,
>> as well as avoiding some ugly nested braces.
>>
>> I know it would cause an ambiguity with `do-while` loops, but that could
>> be resolved with a single token lookahead of "if the next token is the
>> keyword `while`, then the block body is the body of a do-while loop, else
>> it is the body of the block statement in a `do` expression".
>>
>> As for the EBNF, do-expressions could be parsed with a goal symbol of
>> either `+While` or `-While`, with do-while statements spec-wise effectively
>> being treated as do-expressions without an init part run repetitively, but
>> mandated to be statements.
>>
>> ```js
>> // Do expression
>> let foo = do {
>>   foo(0)
>> };
>>
>
> let foo = (foo(0));
>
> This seems as broken as the original. In both cases, unless I'm missing
> something, this is a TDZ violation when the right side evaluates foo.
> Mistake?
>
>
>>
>> let tried = do try {
>>   foo(0)
>> } catch (e) {
>>   throw e
>> };
>>
>
> let tried = (try { foo(0) } catch (e) { throw e });
>
>
>
>>
>> // Do-while statement
>> let i = 0;
>> do {
>>   foo(i)
>> } while (i++ < 10);
>>
>> // Combined:
>> let i = 0;
>> let foo9 = do do {
>>   foo(i) // can have side effects, foo9 = foo(9)
>> } while (i++ < 10);
>> ```
>>
>
> let i = 0;
> let foo9 = (do { foo(i) } while (i++ < 10));
>
>
>
>>
>> Another example of where this could come in handy: simplifying
>> asynchronous code.
>>
>> ```js
>> function readConfig() {
>>   fs.readFileAsync('config.json', 'utf8')
>>     .then(JSON.parse)
>>     .then(contents => do if (contents.unexpectedProperty) {
>>       throw new Error('Bad property') // rejects the promise
>>     } else {
>>       doSomething(contents)
>>     })
>>     .catch(err => process.domain.emit('err', error))
>> }
>>
>
> ...
> .then(contents => (if (contents.unexpectedProperty) {
> ...
> }))
> ...
>
>
>>
>> // With only block statement
>> function readConfig() {
>>   fs.readFileAsync('config.json', 'utf8')
>>     .then(JSON.parse)
>>     .then(contents => do {
>>       if (contents.unexpectedProperty) {
>>         throw new Error('Bad property') // rejects the promise
>>       } else {
>>         doSomething(contents)
>>       }
>>     })
>>     .catch(err => process.domain.emit('err', error))
>> }
>>
>> // Without do-expressions
>> function readConfig() {
>>   fs.readFileAsync('config.json', 'utf8')
>>     .then(JSON.parse)
>>     .then(contents => {
>>       if (contents.unexpectedProperty) {
>>         throw new Error('Bad property') // rejects the promise
>>       } else {
>>         doSomething(contents)
>>       }
>>     })
>>     .catch(err => process.domain.emit('err', error))
>> }
>> ```
>>
>> As you can see, the more general version does simplify things a little.
>>
>> Also, if-statements look better than long ternaries IMHO, and are less
>> repetitive than their counterpart, repeated assignment (us lazy typists...):
>>
>> ```js
>> let foo = do if (someCondition) {
>>   value
>> } else if (someOtherCondition) {
>>   value + 1
>> } else if (someEdgeCase) {
>>   addressEdgeCase(value)
>> } else {
>>   value
>> }
>> ```
>>
>
> let foo = (if (someCondition) {
> ...
> })
>
>
>
>>
>> --
>> Isiah Meadows
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
>>
>
>
> --
>     Cheers,
>     --MarkM
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150714/1d486553/attachment.html>


More information about the es-discuss mailing list