Generalize do-expressions to statements in general?

Mark S. Miller erights at google.com
Tue Jul 14 16:18:58 UTC 2015


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

> On 14 July 2015 at 16:48, Mark S. Miller <erights at google.com> wrote:
>
>> 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.
>>
>
> I would very much dislike introducing a second syntax for blocks, though
> -- which is essentially what you are suggesting. Especially when curly
> braces provide a much better visual clue for the extent of a scope than
> innocent plain parens do. It's the natural expectation for a C-like
> language, too.
>

I can see that. I'm torn.




>
> In the design of any modern language the whole notion of block of course
> is totally obsolete. But JavaScript has its C heritage, and I doubt bolting
> on something alien would make it a prettier language.
>
> ...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 ;)
>>
>
> Well, statements vs expressions was already found unnecessary before OO,
> in the early 60s -- consider Algol 68. (Let alone Lisp, which is late 50s.)
>


For that part specifically, sure. Gedanken also had full indefinite extent
lexical closures. It might have been the first to do so -- Lisp was
dynamically scoped at the time and Actors had not yet been invented. I've
always been puzzled why Gedanken has not gotten more attention --
especially since it was mainly by John Reynolds. Check it out -- you'll be
impressed.




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


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


More information about the es-discuss mailing list