Generalize do-expressions to statements in general?

Mark S. Miller erights at google.com
Tue Jul 14 14:48:51 UTC 2015


On Tue, Jul 14, 2015 at 2:31 AM, Andreas Rossberg <rossberg at google.com>
wrote:

> 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. Take a look at my full proposal. The bizarre observation is that
extending the syntax of parens to contain approx a block-body, and
extending its meaning to creating a block-like scope for evaluating that
block-body, in addition to returning a value. In that case, we simply don't
need the "do" expression at all. Don't propose something unnecessarily
complex just because you expect that something simpler would be too
controversial. If it actually is too controversial, that's another matter.


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

Between Gedanken, Smalltalk, and Actors, almost everything we do in oo
dynamic language design was already conceived right by the early '70s.
Retrofitting without breaking things takes much longer than invention ;)




>
> /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
>>
>>
>


-- 
    Cheers,
    --MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150714/ae84da7c/attachment-0001.html>


More information about the es-discuss mailing list