Re: return when desugaring to closures
Followup to discuss some open issues from the thread, evident in the message cited below: 1. Unifying var scope and let scope at top level of a function could be done, with tolerable restrictions: given let x at top level in a function body, existence of formal parameter x, and redeclaration of let x by top-level var x, are static errors. 2. Unifying var scope and let scope at top level in global code cannot work since var x creates a DontDelete property of the global object if no property x exists, else redeclares an existing property x having arbitrary attributes. We could make let at top level in global code an error. Making such a let bind in an implicit block seems bad because inconsistent with 1, because implicit, and because it's more complicated to implement and specify than making global top-level let an error. Any pragma of the use lexical scope kind: http://wiki.ecmascript.org/doku.php?id=strawman:lexical_scope would remove this restriction. Comments? /be On Oct 13, 2008, at 6:41 PM, Brendan Eich wrote: On Oct 13, 2008, at 6:08 PM, Waldemar Horwat wrote: Brendan Eich wrote: Because presumably the let x:t became var x:t and var can't have types? Why can't var have a type annotation? Because a function can have many var declarations for the same variable and because you can use the variable before any of the var declarations are evaluated. You can work out the implications easily enough. This has been brought up at meetings before. Of course, ES4 had var x:t all over, but as you noted, different rules for evaluating t. Still, with new syntax comes the opportunity for reformed semantics, including restrictions on any bad var abusages we would like to move away from. So my question remains (amended to be clear about what is the same): why does this mean we cannot equate let and var scope at the top level? I'm not talking about allowing them to mix in bad ways, or mix at all (see below). I'm talking about not having an implicit block around top-level function and global code. You wrote that you'd like let to become var (and unlike let as used within an independent block) if used at the top level of a function: Ok, sorry for being unclear. I do not mean to translate top-level 'let' to 'var' and free let bindings from necessary restrictions. I do mean that let binds in the variable object, and let usage restricts other usage. We can forbid mixed var x and let x at top level. We can require the same single definition for any let x at any level. We can forbid arguments usage if arguments[i] could alias a let binding, since let is new (along with rest and optional parameters to enable arguments deprecation). But must we have an implicit block around programs and function bodies that contain let declarations, distinct from the variable scope (object)? What did you mean by had better fail to compile? Other than the type annotation, there is nothing about function f() { x = 15; ... var t = some_runtime_expression; ... var x:t = ... } that ought to fail to compile. The assignment to x in that temporal dead zone before t's initializer has been evaluated. Why is this different if you s/var x/let x/? /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Brendan Eich wrote: Followup to discuss some open issues from the thread, evident in the message cited below: 1. Unifying var scope and let scope at top level of a function could be done, with tolerable restrictions: given let x at top level in a function body, existence of formal parameter x, and redeclaration of let x by top-level var x, are static errors. 2. Unifying var scope and let scope at top level in global code cannot work since var x creates a DontDelete property of the global object if no property x exists, else redeclares an existing property x having arbitrary attributes. We could make let at top level in global code an error. Making such a let bind in an implicit block seems bad because inconsistent with 1, because implicit, and because it's more complicated to implement and specify than making global top-level let an error. Any pragma of the use lexical scope kind: http://wiki.ecmascript.org/doku.php?id=strawman:lexical_scope would remove this restriction. Comments? Presumably the same rationale applies to const and this means that we can't use const at the top level either, not even in ES3.1? What does ES3.1 do if you have both const x and var x at the top level? Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Tue, Oct 21, 2008 at 3:42 PM, Waldemar Horwat [EMAIL PROTECTED] wrote: What does ES3.1 do if you have both const x and var x at the top level? It just so happens that we discussed top level const in this morning's ES3.1 phone meeting. It never occurred to us to ban top level const. We did discuss making top level const scoped just to the program unit vs adding a property to the global object. We also discussed the const read barrier issue. Of your taxonomy of four choices, we had been writing the spec on the assumption of dynamic dead zone, but we think we would prefer static dead zone if its spec is simple enough and if it's simple enough to understand in practice. Given the number of choices and the complexity of choosing among them, especially our ignorance of the implications of static dead zone, we decided to pull const from ES3.1. If we got in wrong in ES3.1, we couldn't fix it later. All block scoping constructs -- const, let, and nested function declarations -- now await Harmony. -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Mark S. Miller wrote: On Tue, Oct 21, 2008 at 3:42 PM, Waldemar Horwat [EMAIL PROTECTED] wrote: What does ES3.1 do if you have both const x and var x at the top level? It just so happens that we discussed top level const in this morning's ES3.1 phone meeting. It never occurred to us to ban top level const. We did discuss making top level const scoped just to the program unit vs adding a property to the global object. We also discussed the const read barrier issue. Of your taxonomy of four choices, we had been writing the spec on the assumption of dynamic dead zone, but we think we would prefer static dead zone if its spec is simple enough and if it's simple enough to understand in practice. Given the number of choices and the complexity of choosing among them, especially our ignorance of the implications of static dead zone, we decided to pull const from ES3.1. I understand the implications of static dead zone; I just worked through them for Jacaranda 0.4. It is definitely possible to guarantee static initialization safety for 'let' and 'const' declarations, checkable using a simple and efficient algorithm (linear in the size of the block), and without any read or write barriers. The resulting function hoisting behaviour is a fairly simple generalization of ES3. I'll post a fleshed-out proposal either tomorrow or Thursday. If we got in wrong in ES3.1, we couldn't fix it later. That's true, but I still hope to convince you and the group that we can get it right for ES3.1. -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 21, 2008, at 5:56 PM, David-Sarah Hopwood wrote: If we got in wrong in ES3.1, we couldn't fix it later. That's true, but I still hope to convince you and the group that we can get it right for ES3.1. With zero implementations due to lack of a spec that hangs together in full, ES3.1 is already at risk for finalization next spring. Keep pushing mission-creep (or jump) and you'll fall into 2010. That is not in the interest of anyone using or implementing the language. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Eric Suen wrote: I think top-down parser has no issue to parse following code: function() { }(); but this is not a valid statement because: ExpressionStatement ::= [lookahead ! {{, function}] Expression ; There is a good reason for that. It's because of semicolon insertion. You'd break things if you tried to fix it. Folks write code such as: function f() { ... } (a + b) and this would turn the expression into a function call. Another issue is that the function name (the f in this case) does entirely different things depending on whether it's a function expression or a function definition. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Brendan Eich wrote: On Oct 16, 2008, at 7:04 PM, Waldemar Horwat wrote: The parser is required to backtrack until it either finds an expansion of the grammar that doesn't generate a syntax error or until it discovers that they all do. You can choose to make additional syntax errors as per chapter 16, but that does not relieve you of the backtracking requirement. You're right that a bottom up parser will have a reduce-reduce conflict. For a top-down parser, it's not an issue. Other examples: What does the following do? for (a = let (b = c) b in d) ... SyntaxError because no ; after first expression in for (;;) loop head. It can't be a SyntaxError. It's a perfectly valid for-in statement. Is this a perfectly valid for-in statement? for (a = b in c); Not according to ES3's grammar. An assignment expression is not valid on the left of the for-in's in: /IterationStatement /*:* * ...* * for ( */LeftHandSideExpression /*in */Expression /*) */Statement/ * for ( var */VariableDeclarationNoIn /*in */Expression /*) */Statement/ LeftHandSideExpression does not produce an unparenthesized AssignmentExpression, and if you parenthesize then PutValue will throw on the non-Reference result of the assignment, the ReferenceError at runtime which again can become SyntaxError at compile time. I accidentally took out the var in editing the message. It should have been for (var a = ...) Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Maciej Stachowiak wrote: I think it might be better to write the official ES3.1 grammar in this way, even though it is a little annoying and repetitive, so it can more readily be verified that the language grammar has no ambiguities by running through a parser-generator like yacc or bison or boost::spirit. I machine-verified the ES3 grammar directly and have a parser that supports the negative lookaheads. I'm using those in addition to the NoIn markers because otherwise there'd be an exponential blowup in the number of productions in the grammar to handle all the cases in ES4. Yacc or bison or such are insufficient. You also need to verify that semicolon insertion is sound and that there exist no grammar states combining a / division in some branches with a / regexp in other branches in the same spot. This would break the lexer because you'd need to look ahead to resolve the ambiguity but can't because lexing depends on how you resolve that token. Also, the ES4 grammar turned out to be LR(1), not LALR(1) as needed by yacc. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 17, 2008, at 11:05 AM, Waldemar Horwat wrote: Brendan Eich wrote: Is this a perfectly valid for-in statement? for (a = b in c); Not according to ES3's grammar. An assignment expression is not valid on the left of the for-in's in: /IterationStatement /*:* * ...* * for ( */LeftHandSideExpression /*in */Expression /*) */ Statement/ * for ( var */VariableDeclarationNoIn /*in */Expression /*) (Note VariableDeclarationNoIn after var.) */Statement/ LeftHandSideExpression does not produce an unparenthesized AssignmentExpression, and if you parenthesize then PutValue will throw on the non-Reference result of the assignment, the ReferenceError at runtime which again can become SyntaxError at compile time. I accidentally took out the var in editing the message. It should have been for (var a = ...) Waldemar _ In that case the -NoIn sub-grammar should apply, extended to LetExpressionNoIn. So for (let (a = b) c in d); would do what you want. We would have to change SpiderMonkey for this edge case, but that is our problem (and unlikely to break anyone). /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 17, 2008, at 11:30 AM, Brendan Eich wrote: In that case the -NoIn sub-grammar should apply, extended to LetExpressionNoIn. So for (let (a = b) c in d); Sorry, of course that should have been for (var a = let (b = c) b in d); /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 16, 2008, at 3:40 PM, Brendan Eich wrote: For now I favor the lambda expression form as well as the lambda block form. Good thing I added For now. Since I'm convinced by Waldemar's argument against ambiguity, I have to drop support for the lambda expression form. It could be rescued, maybe -- but at what cost? I'd rather get on with harmonious lambda and let work. In that light, having let {...} be sugar for lambda () { ... } () is good. Making lambda's parenthesized formal parameter list optional still rubs me the wrong way. It's unambiguous given the block's braces. The symmetry break with function means lambda{x}() returns x where function(){return x}() (in an expression context) has matching () for formals and actuals. But others reasonably object to the extra insignificant parens. So I'll go with the flow and endorse lambda {...} for nullary lambda -- for now. :-) /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 17, 2008, at 11:17 AM, Waldemar Horwat wrote: Maciej Stachowiak wrote: As to the else issue, I don't think that ambiguity can be avoided, but bison lets you solve that with %nonassoc, which is a sound disambiguation mechanism. It can. I have a machine-validated ES3 (and ES4 from earlier proposals) grammar that contains no ambiguities and no handwaving. The if-else rule is handled by having a marker on Statement productions just like there is a NoIn marker on Expression productions. See: http://www.mozilla.org/js/language/old-es4/core/statements.html#N-IfStatement I need a grammar with no ambiguities to do things like verify that semicolon insertion works and that the / regexp-vs-division resolution is always uniquely resolvable in favor of one or the other: you don't ever want a parser state which combines rules that have division with rules that have a regexp in the same spot because the lookahead depends on how you lex the /-token. In that case I would definitely prefer to see the official spec have an unambiguous grammar. (Your grammar still does include the [lookahead∉{function, {}] construct which can be handled in an analogous way to NoIn.) Regards, Maciej ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
The parser is required to backtrack until it either finds an expansion of the grammar that doesn't generate a syntax error or until it discovers that they all do. You can choose to make additional syntax errors as per chapter 16, but that does not relieve you of the backtracking requirement. You're right that a bottom up parser will have a reduce-reduce conflict. For a top-down parser, it's not an issue. Are you sure about that, because you using hand written top-down parser, Is it confirmed by a top-down parser generator like ANTLR? I think top-down parser has no issue to parse following code: function() { }(); but this is not a valid statement because: ExpressionStatement ::= [lookahead ! {{, function}] Expression ; This kind question I asked long time ago, but no one answed, https://mail.mozilla.org/pipermail/es-discuss/2008-July/006640.html and now seems write a bottom up JS parser is mission impossible. Regards, Eric Suen ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 17, 2008, at 7:36 PM, Eric Suen wrote: Are you sure about that, because you using hand written top-down parser, Is it confirmed by a top-down parser generator like ANTLR? I haven't used ANTLR or any other LL(*) parser generator, no. I found http://code.google.com/p/antlr-javascript/ and wondered if anyone on the list has evaluated it. I think top-down parser has no issue to parse following code: function() { }(); Agreed. but this is not a valid statement because: ExpressionStatement ::= [lookahead ! {{, function}] Expression ; That's a good example. See https://bugzilla.mozilla.org/show_bug.cgi?id=376052 if you are interested in why people want to evaluate such a statement. This kind question I asked long time ago, but no one answed, https://mail.mozilla.org/pipermail/es-discuss/2008-July/006640.html Sorry, Jeff may have missed your question. I don't understand it myself (no one ever proposed any syntax of the form x = function () return y). and now seems write a bottom up JS parser is mission impossible. Why do you say that? Maciej already cited the Bison grammar that WebKit's JS engine uses. Waldemar has an out of date but ambitious bottom-up grammar checker. The question before us is what to standardize and how to check it. An extension like let expressions is not a foregone conclusion, likewise expression closures (which some folks in TC39 at the Oslo meeting favored). If we don't want ambiguities that create prove-a- negative hazards, we won't standardize these as-is. For example, we could require parenthesization of the AssignmentExpression body; or we could do something else, such as the braced lambda body that's been discussed here recently. If you mean bottom-up parsing of JS1.7 as implemented in SpiderMonkey is impossible, I still wouldn't go that far. C and C++ are not easy to parse bottom-up, but even ignoring lexer/parser feedback, it can be done with enough hacking, AFAIK. The C-like languages including JS do not inherit bottom-up grammar bones from bad old C. The C top-down grammar (dmr's original C compiler used recursive descent and operator precedence parsing) shows through pretty clearly. I agree we shouldn't make things worse. I don't think all hope is lost. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Brendan Eich wrote: On Oct 15, 2008, at 2:36 PM, Waldemar Horwat wrote: There is no such thing as a let expression. Let expressions http://developer.mozilla.org/en/New_in_JavaScript_1.7#let_expressions in JS1.7 (Firefox 2+), based on the ES4 proposal http://wiki.ecmascript.org/doku.php?id=proposals:block_expressions. ES3-ish grammar: /LetExpression /*:* * let*/ /*( VariableDeclarationList/ /*) [lookahead ∉ {*{*}] AssignmentExpression** produced from PrimaryExpression. That's not a valid grammar. You can't have an AssignmentExpression terminating a PrimaryExpression. It leads to trouble such as: let a = b + c being interpreted as both: (let a = b) + c and: let a = (b + c) It's mandatory because the grammar and semicolon insertion rules say so. Btw, to properly terminate a lambda expression you'd need *two* semicolons. Here's why one would be insufficient: f = lambda(x) x; (a + b) c; would parse the body of the lambda as the expression x;, the (a + b) as an argument to the lambda, and the rest as applying to the result of calling the lambda. What you'd want to write instead would be: f = lambda(x) x;; Expression closures http://developer.mozilla.org/en/New_in_JavaScript_1.8#Expression_closures in JS1.8 (Firefox 3+) do not have this problem. Using ES3's notation: /FunctionDeclaration /*:* * function */Identifier /*( */FormalParameterList//opt /*) */FunctionBody/ /FunctionExpression /*:* * function */Identifier//opt /*( */FormalParameterList//opt /*) */FunctionBody/ /FormalParameterList /*:* / Identifier/ / FormalParameterList /*, */Identifier/ /FunctionBody /*:* / { *SourceElements */*}* * */[lookahead ∉ {*{*}] AssignmentExpression/ That's not a valid grammar. For example, a = function(x) b+c can parse as, among other things: a = (function(x) b) + c which is probably not what you want. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Brendan Eich wrote: On Oct 16, 2008, at 11:38 AM, Waldemar Horwat wrote: Brendan Eich wrote: That's not a valid grammar. It is unambiguous. How do you define valid? You can't have an AssignmentExpression terminating a PrimaryExpression. It leads to trouble such as: let a = b + c being interpreted as both: (let a = b) + c No, it clearly parses as: let a = (b + c) and we've shipped this for several releases, and it sees use -- it has not been problematic. Parenthesize if you mean (let a = b) + 1. Same goes for expression closures. Bottom up parsers will shift, top down will consume greedily. Huh? There is notion of shifting or consuming greedily in ES3. The ES3 grammar is unambiguous. I don't think you can come up with a consistent shift or greedy notion. The one you may be thinking of will happily accept code such as let (a = 5) x + y.foo = 2; yet the Firefox code gives a syntax error for it despite it being parsable by your grammar. So what's the real problem? I said it already. The problem is that you don't have a valid grammar. This one is invalid, so the code of the Firefox implementation is effectively the specification, and it's hard to reason about that. Other examples: What does the following do? for (a = let (b = c) b in d) ... vs. for (a = let (b = c) b in d;;) ... vs. for (a = let (b = c) b in d in d) ... Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Wed, Oct 15, 2008 at 4:49 PM, Dave Herman [EMAIL PROTECTED] wrote: In the grammar for proposed ES4, LetExpression was under PrimaryExpression. That's where I'm suggesting LambdaExpression might fit. IOW: PrimaryExpression ::= ... | LetExpression | LambdaExpression LetExpression ::= let ( LetBindingList ) CommaExpression LambdaExpression ::= lambda FunctionSignature LambdaExpressionBody [quotes inserted above] As we argue about the pros and cons of this proposed expression-body-based grammar and similar variations, does anyone see any problems at all with the following block-based alternative grammar for them? LetExpression ::= let (( LetBindingList ))? Block LambdaExpression ::= lambda FunctionSignature? Block The only potential advantage I have seen claimed for any of the other syntaxes over the above block-based syntax is the occasional savings of two curly brackets -- arguably in exchange for possible confusion. Are there any other problems with this block-based proposal? If we do adopt this form of lambda, would there be any remaining need for non-block functions? Everywhere one might think to write function(...) expr one could just write lambda (...) {expr} instead. -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 16, 2008, at 1:20 PM, Waldemar Horwat wrote: I don't think you can come up with a consistent shift or greedy notion. Funny, yacc has had one for decades, used to resolve dangling-else. The one you may be thinking of will happily accept code such as let (a = 5) x + y.foo = 2; yet the Firefox code gives a syntax error for it despite it being parsable by your grammar. Cut the misattribution of your ideas to me, based on misinterpretation of your experiment. Here's what's going on: Firefox (SpiderMonkey, shown here via its REPL) throws an error: js y = {} [object Object] js let (a = 5) x + y.foo = 2 typein:2: SyntaxError: invalid assignment left-hand side: typein:2: let (a = 5) x + y.foo = 2 typein:2: ..^ This is an error required by ES3, but it is not a syntax error according to the ES3 grammar -- it is an error due to semantic checking done in the spec by PutValue: 11.13.1 Simple Assignment ( = ) The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows: 1. Evaluate LeftHandSideExpression. 2. Evaluate AssignmentExpression. 3. Call GetValue(Result(2)). 4. Call PutValue(Result(1), Result(3)). 5. Return Result(3). 8.7.2 PutValue (V, W) 1. If Type(V) is not Reference, throw a ReferenceError exception. 2. Call GetBase(V). 3. If Result(2) is null, go to step 6. 4. Call the [[Put]] method of Result(2), passing GetPropertyName(V) for the property name and W for the value. 5. Return. 6. Call the [[Put]] method for the global object, passing GetPropertyName(V) for the property name and W for the value. 7. Return. SpiderMonkey historically used SyntaxError, not ReferenceError, and throw at compile-time. This pre-dates ES1. Another example not involving let expressions: js a + b = c typein:1: SyntaxError: invalid assignment left-hand side: typein:1: a + b = c typein:1: ..^ From ES3 chapter 16: An implementation may treat any instance of the following kinds of runtime errors as a syntax error and therefore report it early: • Improper uses of return, break, and continue. • Using the eval property other than via a direct call. • Errors in regular expression literals. • Attempts to call PutValue on a value that is not a reference (for example, executing the assignment statement 3=4). You may object that we should throw ReferenceError not SyntaxError -- that is not entirely clear from the chapter 16 wording, but it is at most a bug unrelated to our disagreement, and it doesn't prove any claim that primary expressions ending in assignment expressions are ambiguous or unusable. So what's the real problem? I said it already. The problem is that you don't have a valid grammar. You have not demonstrated that claim. This one is invalid, so the code of the Firefox implementation is effectively the specification, and it's hard to reason about that. It's easy, you can do it ;-). Other examples: What does the following do? for (a = let (b = c) b in d) ... SyntaxError because no ; after first expression in for (;;) loop head. vs. for (a = let (b = c) b in d;;) ... Valid syntax. vs. for (a = let (b = c) b in d in d) ... SyntaxError because no ; after first expression in for (;;) loop head. Yes, you can chain in (relational) expressions within an AssignmentExpression. No, users are not flummoxed, or as far as we can tell in over two years even bothered, by this. Yes, it can be specified unambiguously. /be___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 15, 2008, at 1:28 PM, Mark S. Miller wrote: On Wed, Oct 15, 2008 at 12:31 PM, Dave Herman [EMAIL PROTECTED] wrote: Do you prefer (lambda Formals Block | lambda Formals Expression)? [Personally I'm fine with that.] Or do you oppose any lambda expressions at all? Or did you have something else in mind? My preference is lambda Formals? Block Since an invocation of the closure evaluates to its completion value, the only savings of introducing the Expression form would be a pair of curlies. This is too small a benefit to justify another case in the grammar. Further, in exchange for always requiring the curlies, we can make the formals optional. Once we've got lambda, we'll start using them for control abstractions, in which case the no-parameter form will become quite common. I'd rather be able to leave out the parens than the curlies. YMMV. This is admittedly subjective, but you did ask about preferences. Hi Mark, That's fair and closely reasoned, but I question the likelihood of zero-argument lambdas emerging in control abstractions favored by all that many users. For now I favor the lambda expression form as well as the lambda block form. You're right that much of this is subjective, not just personal but also speculative about what users will tend to use. Expression closures are already insanely popular in JS1.8, of course used only in Mozilla-specific code. They'd be even more winning as lambdas, whether zero args is common or not (six letter keyword instead of eight), but losing the braces is part of the win. The two shifted chars are not easy on some peoples' hands (ask Python hackers who are spared such RSI-inducing chords). The visual weight is less, to boot. Definitely my two cents, no more (possibly fewer ;-). /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 16, 2008, at 3:33 PM, Brendan Eich wrote: SpiderMonkey historically used SyntaxError, not ReferenceError, and throw at compile-time. This pre-dates ES1. Another example not involving let expressions: js a + b = c typein:1: SyntaxError: invalid assignment left-hand side: typein:1: a + b = c typein:1: ..^ From ES3 chapter 16: An implementation may treat any instance of the following kinds of runtime errors as a syntax error and therefore report it early: • Improper uses of return, break, and continue. • Using the eval property other than via a direct call. • Errors in regular expression literals. • Attempts to call PutValue on a value that is not a reference (for example, executing the assignment statement 3=4). You may object that we should throw ReferenceError not SyntaxError -- that is not entirely clear from the chapter 16 wording, An implementation may treat any instance of the following kinds of runtime errors as a syntax error and therefore report it early seems clear enough: an implementation that does choose (may) to report the ReferenceError from 8.7.2 PutValue step 1 *must* treat that runtime error as a syntax error -- throw a SyntaxError, in other words. There's no SpiderMonkey bug here, and it would be incorrect to do other than change ReferenceError to SyntaxError when reporting the PutValue error at compile time. Again, this is not a grammatical error. The ES3 grammar is not enough to decide what syntax errors might result from feeding a given program source to an implementation. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Mark S. Miller wrote: On Wed, Oct 15, 2008 at 4:49 PM, Dave Herman [EMAIL PROTECTED] wrote: In the grammar for proposed ES4, LetExpression was under PrimaryExpression. That's where I'm suggesting LambdaExpression might fit. IOW: PrimaryExpression ::= ... | LetExpression | LambdaExpression LetExpression ::= let ( LetBindingList ) CommaExpression LambdaExpression ::= lambda FunctionSignature LambdaExpressionBody [quotes inserted above] As we argue about the pros and cons of this proposed expression-body-based grammar and similar variations, does anyone see any problems at all with the following block-based alternative grammar for them? LetExpression ::= let (( LetBindingList ))? Block LambdaExpression ::= lambda FunctionSignature? Block You read my mind; that is exactly what I was about to propose. Note also that (( LetBindingList )) and FunctionSignature are almost identical, and could probably be merged. In that case let ... { body } could be sugar for (lambda ... { body })() if the binding of default arguments to a lambda has the same semantics as the binding of variables in a let. (There are several reasonable choices for that semantics; I suggest reading http://www.cs.indiana.edu/~dyb/pubs/fixing-letrec.pdf to start with.) The degenerate syntax let {...} allowed by this grammar at first-sight doesn't seem very useful, until you realize that it has a similar effect (apart from not preventing hoisting of 'var' declarations past the 'let') to the Crockford module pattern (function() {...})(). -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Brendan Eich wrote: On Oct 16, 2008, at 1:20 PM, Waldemar Horwat wrote: I don't think you can come up with a consistent shift or greedy notion. Funny, yacc has had one for decades, used to resolve dangling-else. The one you may be thinking of will happily accept code such as let (a = 5) x + y.foo = 2; yet the Firefox code gives a syntax error for it despite it being parsable by your grammar. Cut the misattribution of your ideas to me, based on misinterpretation of your experiment. Here's what's going on: Firefox (SpiderMonkey, shown here via its REPL) throws an error: js y = {} [object Object] js let (a = 5) x + y.foo = 2 typein:2: SyntaxError: invalid assignment left-hand side: typein:2: let (a = 5) x + y.foo = 2 typein:2: ..^ This is an error required by ES3, but it is not a syntax error according to the ES3 grammar -- it is an error due to semantic checking done in the spec by PutValue: *11.13.1 Simple Assignment ( *= *)* The production /AssignmentExpression /*: */LeftHandSideExpression /*= */AssignmentExpression /is evaluated as follows: 1. Evaluate /LeftHandSideExpression/. 2. Evaluate /AssignmentExpression/. 3. Call GetValue(Result(2)). 4. Call PutValue(Result(1), Result(3)). 5. Return Result(3). *8.7.2 PutValue (V, W)* 1. If Type(/V/) is not Reference, throw a *ReferenceError *exception. 2. Call GetBase(/V/). 3. If Result(2) is *null*, go to step 6. 4. Call the [[Put]] method of Result(2), passing GetPropertyName(/V/) for the property name and W for the value. 5. Return. 6. Call the [[Put]] method for the global object, passing GetPropertyName(/V/) for the property name and W for the value. 7. Return. SpiderMonkey historically used SyntaxError, not ReferenceError, and throw at compile-time. This pre-dates ES1. Another example not involving let expressions: js a + b = c typein:1: SyntaxError: invalid assignment left-hand side: typein:1: a + b = c typein:1: ..^ From ES3 chapter 16: An implementation may treat any instance of the following kinds of runtime errors as a syntax error and therefore report it early: • Improper uses of *return*, *break*, and *continue*. • Using the *eval *property other than via a direct call. • Errors in regular expression literals. • Attempts to call PutValue on a value that is not a reference (for example, executing the assignment statement 3=4). You may object that we should throw ReferenceError not SyntaxError -- that is not entirely clear from the chapter 16 wording, but it is at most a bug unrelated to our disagreement, and it doesn't prove any claim that primary expressions ending in assignment expressions are ambiguous or unusable. According to the grammar you provided, this expression is valid. It means (let (a = 5) x + y).foo = 2 The parser is required to backtrack until it either finds an expansion of the grammar that doesn't generate a syntax error or until it discovers that they all do. You can choose to make additional syntax errors as per chapter 16, but that does not relieve you of the backtracking requirement. So what's the real problem? I said it already. The problem is that you don't have a valid grammar. You have not demonstrated that claim. Brendan, how many more times do I have to explain this? For example, the statement a = let (b = c) c + d; is ambiguous according to the grammar and can parse as both: a = (let (b = c) c) + d; and a = let (b = c) (c + d); There is nothing written that prevents an implementation from taking either view of it, and it would conform to the grammar. Other examples: What does the following do? for (a = let (b = c) b in d) ... SyntaxError because no ; after first expression in for (;;) loop head. It can't be a SyntaxError. It's a perfectly valid for-in statement. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Thu, Oct 16, 2008 at 5:49 PM, David-Sarah Hopwood [EMAIL PROTECTED] wrote: [lots of good stuff snipped] I agree. The degenerate syntax let {...} allowed by this grammar at first-sight doesn't seem very useful, until you realize that it has a similar effect (apart from not preventing hoisting of 'var' declarations past the 'let') to the Crockford module pattern (function() {...})(). Since non-var declarations will now be lexically block scoped, all these non-var good features but one of Crock's module pattern will also be present for simple blocks. Since lambda preserves TC, it must be thus. The one additional feature provided both by Crock's module pattern and by degenerate let is turn EcmaScript effectively into an expression language. Rather than saying ...(3 + function(){while(...){...}; return 4;}())... we'd now be able to say ...(3 + lambda{while(...){...}; 4}())... or even ...(3 + let{while(...){...}; 4})... Of course, if you want a begin/end that works for vars as well, you'd still have to use Crock's module pattern. -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 16, 2008, at 7:01 PM, Brendan Eich wrote: On Oct 16, 2008, at 3:33 PM, Brendan Eich wrote: On Oct 16, 2008, at 1:20 PM, Waldemar Horwat wrote: I don't think you can come up with a consistent shift or greedy notion. Funny, yacc has had one for decades, used to resolve dangling-else. Which ES1-3 also use: 12.5 The if Statement Syntax IfStatement : if ( Expression ) Statement else Statement if ( Expression ) Statement Each else for which the choice of associated if is ambiguous shall be associated with the nearest possible if that would otherwise have no corresponding else. The grammar is not enough in ES3 without let expressions. You need at least this shift-reduce conflict resolution rule (I'm ignoring ASI, / as division vs. regexp delimiter, etc.). Adding let expressions does not add novel requirements for extra-grammatical disambiguation rules. The ambiguity between blocks and object initialisers is another well- known case, resolved by negative lookahead: 12.4 Expression Statement Syntax ExpressionStatement : [lookahead ∉ {{, function}] Expression ; This one can be solved without negative lookahead, we have separate NoBF (no brace or function) and NoIn productions in our grammar to solve these without ambiguity: Expr: AssignmentExpr | Expr ',' AssignmentExpr ; ExprNoIn: AssignmentExprNoIn | ExprNoIn ',' AssignmentExprNoIn ; ExprNoBF: AssignmentExprNoBF | ExprNoBF ',' AssignmentExpr ; With the NoIn / NoBF variants propagated through the rest of the grammar if needed. Kind of brute force, but it does the job without ambiguity. JavaScriptCore's grammar runs through bison with no shift/ reduce or reduce/reduce conflicts. I think it might be better to write the official ES3.1 grammar in this way, even though it is a little annoying and repetitive, so it can more readily be verified that the language grammar has no ambiguities by running through a parser-generator like yacc or bison or boost::spirit. As to the else issue, I don't think that ambiguity can be avoided, but bison lets you solve that with %nonassoc, which is a sound disambiguation mechanism. Regards, Maciej ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Please specify what you are proposing. The one proposal I've seen is: Expression ::= ... | lambda Formals Statement Yes, that's what I meant, or at least what I thought Yuh-Ruey meant. This is not particularly useful because then even assign a lambda to a variable would be a syntax error, Why is that? and you'd introduce a bizarre asymmetry into the ExpressionNoIn case. Can you explain? The questions you'd have to answer are: - Where in the expression grammar would you introduce a lambda? I assume you mean what is the precedence. I'm not sure, although presumably it would be about the same as `let' expressions. - Is lambda a reserved word? This is a relevant question for any number of alternative syntaxes for lambda. I would think it would be the same as `let'. - How does it interact with semicolon insertion? As written, if the Statement were an expression statement then the semicolon would be mandatory. Why is that? If that were so then I would be absolutely opposed to Yuh-Ruey's syntax: the appeal of the idea was unifying the expression-body and statement-body form into one single definition. - Do you really want to have extra semicolons in the middle of statements? Now you're forced to accept things like the following: for (; lambda() x++;, lambda() y++;,; lambda() z++;) ... Thanks, these were the kinds of nastinesses I was worried about. Arbitrarily mixing statements into expression contexts does indeed appear to get pretty convoluted. Perhaps just enforcing the block form of lambda is enough to keep things from getting too mixed up, specifically, by enforcing syntactic delimiters between expressions. Overall, this syntax does not look promising. It's likely to get you into complexity trouble. Do you prefer (lambda Formals Block | lambda Formals Expression)? [Personally I'm fine with that.] Or do you oppose any lambda expressions at all? Or did you have something else in mind? Thanks, Dave ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Dave Herman wrote: Please specify what you are proposing. The one proposal I've seen is: Expression ::= ... | lambda Formals Statement Yes, that's what I meant, or at least what I thought Yuh-Ruey meant. This is not particularly useful because then even assign a lambda to a variable would be a syntax error, Why is that? Because there exists no sequence of grammar production expansions that would expand to a = lambda() x;. and you'd introduce a bizarre asymmetry into the ExpressionNoIn case. Can you explain? Look at how the ExpressionNoIn cases are handled in the existing grammar. They are necessary. If you have an expression that contains a statement that contains an expression that contains the keyword in then you'll introduce a grammatical ambiguity into for-in statements. The questions you'd have to answer are: - Where in the expression grammar would you introduce a lambda? I assume you mean what is the precedence. I'm not sure, although presumably it would be about the same as `let' expressions. There is no such thing as a let expression. I don't mean precedence. I mean which rule in the expression grammar would describe a lambda. Precedence is a side effect of that, but obviously not all syntax can be captured by the concept of precedence. - Is lambda a reserved word? This is a relevant question for any number of alternative syntaxes for lambda. I would think it would be the same as `let'. - How does it interact with semicolon insertion? As written, if the Statement were an expression statement then the semicolon would be mandatory. Why is that? If that were so then I would be absolutely opposed to Yuh-Ruey's syntax: the appeal of the idea was unifying the expression-body and statement-body form into one single definition. It's mandatory because the grammar and semicolon insertion rules say so. Btw, to properly terminate a lambda expression you'd need *two* semicolons. Here's why one would be insufficient: f = lambda(x) x; (a + b) c; would parse the body of the lambda as the expression x;, the (a + b) as an argument to the lambda, and the rest as applying to the result of calling the lambda. What you'd want to write instead would be: f = lambda(x) x;; - Do you really want to have extra semicolons in the middle of statements? Now you're forced to accept things like the following: for (; lambda() x++;, lambda() y++;,; lambda() z++;) ... Thanks, these were the kinds of nastinesses I was worried about. Arbitrarily mixing statements into expression contexts does indeed appear to get pretty convoluted. Perhaps just enforcing the block form of lambda is enough to keep things from getting too mixed up, specifically, by enforcing syntactic delimiters between expressions. Overall, this syntax does not look promising. It's likely to get you into complexity trouble. Do you prefer (lambda Formals Block | lambda Formals Expression)? Speaking purely from a syntactic perspective: - The block form is fine. - The expression form has problems. It's hard to distinguish an expression that's part of the lambda from an expression that's part of the surrounding expression. [Personally I'm fine with that.] Or do you oppose any lambda expressions at all? Or did you have something else in mind? I don't see enough of a benefit to warrant inclusion of a new lambda concept. It's just another way of doing what local functions can do. If there is a bug with local functions, then fix that one instead instead of sowing confusion by having two concepts. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Please specify what you are proposing. The one proposal I've seen is: Expression ::= ... | lambda Formals Statement Dave Herman wrote: Yes, that's what I meant, or at least what I thought Yuh-Ruey meant. This is not particularly useful because then even assign a lambda to a variable would be a syntax error, Why is that? 2008/10/15 Waldemar Horwat [EMAIL PROTECTED]: Because there exists no sequence of grammar production expansions that would expand to a = lambda() x;. Only if you disallow newlines in the lambda syntax. This is certainly allowed in ES3: a=lambda() x; Same goes for a=lambda {...} or a=lambda(...) {...} (...) or any number of similar plays with the semicolon insertion rules. -- David liorean Andersson ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 15, 2008, at 2:36 PM, Waldemar Horwat wrote: There is no such thing as a let expression. Let expressions in JS1.7 (Firefox 2+), based on the ES4 proposal. ES3- ish grammar: LetExpression : let ( VariableDeclarationList ) [lookahead ∉ {{}] AssignmentExpression produced from PrimaryExpression. It's mandatory because the grammar and semicolon insertion rules say so. Btw, to properly terminate a lambda expression you'd need *two* semicolons. Here's why one would be insufficient: f = lambda(x) x; (a + b) c; would parse the body of the lambda as the expression x;, the (a + b) as an argument to the lambda, and the rest as applying to the result of calling the lambda. What you'd want to write instead would be: f = lambda(x) x;; Expression closures in JS1.8 (Firefox 3+) do not have this problem. Using ES3's notation: FunctionDeclaration : function Identifier ( FormalParameterListopt ) FunctionBody FunctionExpression : function Identifieropt ( FormalParameterListopt ) FunctionBody FormalParameterList : Identifier FormalParameterList , Identifier FunctionBody : { SourceElements } [lookahead ∉ {{}] AssignmentExpression /be___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 15, 2008, at 6:13 PM, Brendan Eich wrote: On Oct 15, 2008, at 2:36 PM, Waldemar Horwat wrote: There is no such thing as a let expression. Let expressions in JS1.7 (Firefox 2+), based on the ES4 proposal. ES3-ish grammar: LetExpression : let ( VariableDeclarationList ) [lookahead ∉ {{}] AssignmentExpression Rather, LetExpression : let ( VariableDeclarationList ) AssignmentExpression /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 13, 2008, at 6:39 PM, Brendan Eich wrote: Users may be modeling closures as capturing bindings, not scope chains of mutable objects, one per for (let...) statement or explicitly braced block. If so, could we make let declaration capture this way? Again, I'm proceeding from real users' complaints, not idle wishes. Are you suggesting that closures over let capture bindings in the general case? I hope not. I think for loops are a special case, otherwise let is not the new var. ;) WRT for loops, it's important to remember that let provides an alternative that wasn't possible with var. Suppose for moment we do *not* rebind on every iteration: for (let i = 0; i objects.length; i++) { let j = i; objects[i].callback = function() { print(j); } } The syntactic overhead for such a workaround is much less than for var: for (var i = 0; i objects.length; i++) { objects[i].callback = buildCallback(i); } function buildCallback(i) { print(i); } The for/closures bug is definitely a newbie trap, but its pain is not its discovery, but the difficulty of working around it. To me this could be a winning argument against re-binding on each loop, since re-binding precludes the (admittedly dubious) use-case of updating inside the body: for (let i = 0; i 100; i++) { if (skipAhead) { i += 9; continue; } ... } ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
David-Sarah Hopwood wrote: Neil Mix wrote: The for/closures bug is definitely a newbie trap, but its pain is not its discovery, but the difficulty of working around it. To me this could be a winning argument against re-binding on each loop, since re-binding precludes the (admittedly dubious) use-case of updating inside the body: for (let i = 0; i 100; i++) { if (skipAhead) { i += 9; continue; } ... } The expansions that MarkM and I posted work correctly in that case. (Correctly means what a C programmer would expect :-) To be more specific, updates within the body take effect for the instance of the variable that is seen by the update expression for the next iteration.) What I meant to say was: Updates within the body affect the value of the variable instance that is seen by the update expression for the next iteration. -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 14, 2008, at 7:38 AM, Neil Mix wrote: On Oct 13, 2008, at 6:39 PM, Brendan Eich wrote: Users may be modeling closures as capturing bindings, not scope chains of mutable objects, one per for (let...) statement or explicitly braced block. If so, could we make let declaration capture this way? Again, I'm proceeding from real users' complaints, not idle wishes. Are you suggesting that closures over let capture bindings in the general case? I hope not. I think for loops are a special case, otherwise let is not the new var. ;) What about for-in loops? I'm proceeding from user expectations being confounded. Something needs help here, possibly not for (;;) loops -- but almost certainly for-in loops containing closures capturing the loop variable. WRT for loops, it's important to remember that let provides an alternative that wasn't possible with var. Suppose for moment we do *not* rebind on every iteration: for (let i = 0; i objects.length; i++) { let j = i; objects[i].callback = function() { print(j); } } Yes, this is in the bug I cited (https://bugzilla.mozilla.org/show_bug.cgi?id=449811 ). It is still a PITA. The syntactic overhead for such a workaround is much less than for var: for (var i = 0; i objects.length; i++) { objects[i].callback = buildCallback(i); } function buildCallback(i) { print(i); } True, which makes it even more painful in some ways. Why doesn't JS do it for me? Closer, almost-but-not-quite-right, is torturous. The for/closures bug is definitely a newbie trap, but its pain is not its discovery, but the difficulty of working around it. To me this could be a winning argument against re-binding on each loop, since re-binding precludes the (admittedly dubious) use-case of updating inside the body: for (let i = 0; i 100; i++) { if (skipAhead) { i += 9; continue; } ... } So we can have our cake and eat it too with a tail-recursive desugaring that forwards the previous iteration's binding value to the next iteration's binding. Of course any practical implementation will have to do more analysis than is currently done in the top open-source implementations I've looked at. Could be worth the cost (VM hackers can take the cost for the larger team of users). What do you think? /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
What about for-in loops? I'm proceeding from user expectations being confounded. Something needs help here, possibly not for (;;) loops -- but almost certainly for-in loops containing closures capturing the loop variable. Whatever is done should be consistent between for and for-in. My concern was that you were suggesting that a closure over *any* let variable binds the value and not the scope. i.e.: let x = 5; let f = function() { print(x); } x = 6; f(); // prints 5?? I don't think that's what you were proposing, but I wasn't entirely clear. So we can have our cake and eat it too with a tail-recursive desugaring that forwards the previous iteration's binding value to the next iteration's binding. Of course any practical implementation will have to do more analysis than is currently done in the top open- source implementations I've looked at. Could be worth the cost (VM hackers can take the cost for the larger team of users). What do you think? Yeah, I got lost in the thread and missed the subtlety of that follow- up. I'm having a hard time seeing any downside, other than perhaps a mild inconsistency between var and let behavior here. Worth it, just as long as any form with the general syntax of keyword ( ... let declaration/assigment ... ) { body } behaves in the same manner. (My point being that it's syntactically strange that let appearing outside of the braces is bound inside the braces --thinking like a language user, not a language implementer -- but that's good behavior as long as it's consistent.) ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On 2008-10-11, at 08:34EDT, David Herman wrote: Thank you for pointing out, though, that try/catch isn't so easily defined on top of return-to-label, since it still needs special handling for finally. The options are either to define a lower-level primitive underlying try/finally (akin to Scheme's dynamic-wind), or to leave exceptions -- or at least try/finally -- as primitive. I lean towards the latter; dynamic-wind is a subtle beast. [For those interested in dynamic-wind, Flatt et al's ICFP 07 paper has a nice investigation into its semantics:http://www.cs.utah.edu/plt/publications/icfp07-fyff.pdf ] My 2p: If introducing a lower-level primitive would mean the language could someday have handler-binding (the ability to handle a condition in the frame it is signaled in), I think that would be a big win. ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Brendan Eich wrote: What did you mean by had better fail to compile? Other than the type annotation, there is nothing about function f() { x = 15; ... var t = some_runtime_expression; ... var x:t = ... } that ought to fail to compile. The assignment to x in that temporal dead zone before t's initializer has been evaluated. Why is this different if you s/var x/let x/? Ah, ok. That's good news! For a long time I wanted to make statically detectable uses of a variable before its let or const declaration runs to be compile-time errors. You can't detect all of them due to closures, but you can detect most in practice. I'm glad you agree. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
David-Sarah Hopwood wrote: David-Sarah Hopwood wrote: Waldemar Horwat wrote: I am talking about let bindings. Lars brought up at that meeting. I did not find the use cases particularly convincing, but the dead zone is compelling. There are four ways to do this: A1. Lexical dead zone. References textually prior to a definition in the same block are an error. A2. Lexical window. References textually prior to a definition in the same block go to outer scope. B1. Temporal dead zone. References temporally prior to a definition in the same block are an error. B2. Temporal window. References temporally prior to a definition in the same block go to outer scope. Let's take a look at an example: let x = outer; function g() {return outer} { g(); function f() { ... x ... g ... g() ... } f(); var t = some_runtime_type; const x:t = inner; function g() { ... x ... } g(); f(); } B2 is bad because then the x inside g would sometimes refer to outer and sometimes to inner. A1 and A2 introduce extra complexity but doesn't solve the problem. I already suggested a solution to the problem based on a refinement of A1: http://www.mail-archive.com/es-discuss@mozilla.org/msg00899.html This post was more directly relevant: http://www.mail-archive.com/es-discuss@mozilla.org/msg00889.html This won't work for many reasons. Making the scope of a function depend on the details of what it refers to inside is too subtle of a rule to be practical. You'd need to examine the body of the entire function before you know when you can refer to its name from the outside. Also, it interacts badly with existing ES3 semantics. Here's an example: var f; f(5); const x = 2; function f(a) { const g = function() {return x;} if (a != 5) g(); if (false) x; return 3; } This had better work, or else you're violating ES3 semantics. There's nothing wrong with the call to f. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Neil Mix wrote: The for/closures bug is definitely a newbie trap, but its pain is not its discovery, but the difficulty of working around it. To me this could be a winning argument against re-binding on each loop, since re-binding precludes the (admittedly dubious) use-case of updating inside the body: for (let i = 0; i 100; i++) { if (skipAhead) { i += 9; continue; } ... } The expansions that MarkM and I posted work correctly in that case. (Correctly means what a C programmer would expect :-) To be more specific, updates within the body take effect for the instance of the variable that is seen by the update expression for the next iteration.) -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Mark S. Miller wrote: On Sun, Oct 12, 2008 at 7:55 PM, David-Sarah Hopwood [EMAIL PROTECTED] wrote: In ES3.1, this will mean that they normally require braces whenever a body can introduce variables. There are two classes of exceptions, shown by these examples: a) while (...) foo: var x = ...; b) while (...) for (var x = ...; ...; ...) {} I think these are bugs. a) can certainly be disallowed. There might possibly be existing code that is relying on b), confusing though it is, but it can be disallowed in strict mode at least. ES3.1 will disallow #a because a LabelledStatement can only contain a SubStatement. A variable declaration is a Statement but not a SubStatement. Note that although there is currently no plausible use for { foo: let x = ...; } because expressions cannot 'break' or 'continue' to a label outside the expression, that reason will no longer hold if/when we add lambdas. In that case ... might contain (after expansion) a lambda that says break foo; or continue foo;. But that doesn't really cause any problem since, - if the let is written explicitly by the programmer, it's easy enough to add extra braces in this (presumably rare) case; - if the let is introduced by an expansion, the expansion should include braces around it if it is intended to be a SubStatement. snip contribution added by cat walking on keyboard [Reminder to self: In order for Jacaranda to continue to be a subset of ES3.1, I will have to change it to reject label: var ..., which was allowed in draft 0.3.] Your point about #b is interesting. I do not know if it has previously been raised. Actually the while loop is a red herring in that example: for (var ...) implicitly introduces a block whether or not it is a substatement. This is a wart of C++/C99/Java syntax that we have to live with, since too much code relies on it. -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Mon, Oct 13, 2008 at 8:05 AM, David-Sarah Hopwood [EMAIL PROTECTED] wrote: for (var ...) implicitly introduces a block whether or not it is a substatement. This is a wart of C++/C99/Java syntax that we have to live with, since too much code relies on it. Yes, but how do we live with it? The only currently allowed case, var as above, is no problem, since it hoists to the function body anyway. But what about let. Do we all agree that in for (let x = ...) {...x...} ... x ... the x after the for loop does not refer to the x defined by the for loop? In that case, no problem with for being a SubStatement. A remaining interesting question is whether the for loop reassigns to a single per-for-loop-entry x, or whether it initializes a fresh per-iteration x. If the for loop body has a closure that captures x, it makes a difference. I recommend the per-iteration view. If we can agree quickly on per-iteration, then for (const x = ...) {...x...} should be allowed in ES3.1 (whether or not const hoists to block start). After ES3.1 for (const i :T[i] = ...) {...; a[i] = function(){...i...}; ...} would then mean what it should mean. Cool. -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Mon, Oct 13, 2008 at 11:43 AM, Mark S. Miller [EMAIL PROTECTED] wrote: On Mon, Oct 13, 2008 at 8:05 AM, David-Sarah Hopwood [EMAIL PROTECTED] wrote: for (var ...) implicitly introduces a block whether or not it is a substatement. This is a wart of C++/C99/Java syntax that we have to live with, since too much code relies on it. Yes, but how do we live with it? The only currently allowed case, var as above, is no problem, since it hoists to the function body anyway. But what about let. Do we all agree that in for (let x = ...) {...x...} ... x ... the x after the for loop does not refer to the x defined by the for loop? Yes. Definitely. In that case, no problem with for being a SubStatement. A remaining interesting question is whether the for loop reassigns to a single per-for-loop-entry x, or whether it initializes a fresh per-iteration x. If the for loop body has a closure that captures x, it makes a difference. I recommend the per-iteration view. I like your intention here -- I brought up the iteration variable / closure issue earlier in the thread -- but this seems rather messy. In the 'var' case, x must be the same throughout, no? function foo() { for (var x = ... ) { ... x ... } ... x ... } But then it becomes extremely awkward for the 'let' or 'const' case to use a fresh variable on every iteration. It ties the semantics of 'for' to its initialization clause in a very strange way. -Jon ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Mon, Oct 13, 2008 at 9:13 AM, Jon Zeppieri [EMAIL PROTECTED] wrote: I like your intention here -- I brought up the iteration variable / closure issue earlier in the thread -- but this seems rather messy. In the 'var' case, x must be the same throughout, no? yes. function foo() { for (var x = ... ) { ... x ... } ... x ... } But then it becomes extremely awkward for the 'let' or 'const' case to use a fresh variable on every iteration. It ties the semantics of 'for' to its initialization clause in a very strange way. I think it falls out of the natural desugaring of for to lambda with no special cases. The var itself causes its own hoisting and joining. -- Text by me above is hereby placed in the public domain Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Mon, Oct 13, 2008 at 12:47 PM, Mark Miller [EMAIL PROTECTED] wrote: On Mon, Oct 13, 2008 at 9:13 AM, Jon Zeppieri [EMAIL PROTECTED] wrote: But then it becomes extremely awkward for the 'let' or 'const' case to use a fresh variable on every iteration. It ties the semantics of 'for' to its initialization clause in a very strange way. I think it falls out of the natural desugaring of for to lambda with no special cases. The var itself causes its own hoisting and joining. Sorry -- I must be particularly dense today. What desugaring do you have in mind? -Jon ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Mark S. Miller wrote: On Mon, Oct 13, 2008 at 8:05 AM, David-Sarah Hopwood [EMAIL PROTECTED] wrote: for (var ...) implicitly introduces a block whether or not it is a substatement. This is a wart of C++/C99/Java syntax that we have to live with, since too much code relies on it. Yes, but how do we live with it? The only currently allowed case, var as above, is no problem, since it hoists to the function body anyway. Yes, I should have used for (let ...) as the example. But what about let. Do we all agree that in for (let x = ...) {...x...} ... x ... the x after the for loop does not refer to the x defined by the for loop? Yes, if this form is allowed. The other options are either to require: let x in { for (x = 0; x 10; x++) {...x...} } or let x = 0 in { for (; x 10; x++) {...x...} } or to add new syntax: for x = 0 while {x 10} with {x++} do { ...x... } The latter is longer, but it makes more sense to me, because the scoping is absolutely consistent with the braces. I don't know what other C/Java/JS-trained programmers will make of it, though. A remaining interesting question is whether [in the case of for (let or for (const] the for loop reassigns to a single per-for-loop-entry x, or whether it initializes a fresh per-iteration x. If the for loop body has a closure that captures x, it makes a difference. I recommend the per-iteration view. If we can agree quickly on per-iteration, then for (const x = ...) {...x...} should be allowed in ES3.1 (whether or not const hoists to block start). After ES3.1 for (const i :T[i] = ...) {...; a[i] = function(){...i...}; ...} would then mean what it should mean. Cool. Not so fast :-) Consider: for (let i = 0; i 10; i++) { ... } In the i++, which iteration's 'i' is the LeftHandSideExpression referring to? Or does this expand to something like: let ($i = 0) { for (; let (i = $i) {i 10}; let (i = $i) {{i++;} $i = i;}} { let (i = $i) {...}; } } ? That would work as I think you want, but it seems a bit magical to me. I could be convinced, though -- do you want to try and make the case for why there should be a fresh variable per iteration with some concrete examples? -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
David-Sarah Hopwood wrote: Mark S. Miller wrote: But what about let. Do we all agree that in for (let x = ...) {...x...} ... x ... the x after the for loop does not refer to the x defined by the for loop? Yes, if this form is allowed. The other options are either to require: let x in { for (x = 0; x 10; x++) {...x...} } or let x = 0 in { for (; x 10; x++) {...x...} } I meant: let (x) { for (x = 0; x 10; x++) {...x...} } or let (x = 0) { for (; x 10; x++) {...x...} } ('let ... in' is similar to the 'local ... in' syntax that Oz uses, and I think it reads better, but it can't be used in ECMAScript because the part to the right of the identifier is ambiguous with an application of the 'in' operator.) -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 13, 2008, at 8:43 AM, Mark S. Miller wrote: On Mon, Oct 13, 2008 at 8:05 AM, David-Sarah Hopwood [EMAIL PROTECTED] wrote: for (var ...) implicitly introduces a block whether or not it is a substatement. This is a wart of C++/C99/Java syntax that we have to live with, since too much code relies on it. Yes, but how do we live with it? The only currently allowed case, var as above, is no problem, since it hoists to the function body anyway. But what about let. Do we all agree that in for (let x = ...) {...x...} ... x ... the x after the for loop does not refer to the x defined by the for loop? In that case, no problem with for being a SubStatement. No one ever proposed otherwise for for (let...), and it would be nuts to do anything like that. A remaining interesting question is whether the for loop reassigns to a single per-for-loop-entry x, or whether it initializes a fresh per-iteration x. If the for loop body has a closure that captures x, it makes a difference. I recommend the per-iteration view. If we can agree quickly on per-iteration, then for (const x = ...) {...x...} should be allowed in ES3.1 (whether or not const hoists to block start). After ES3.1 for (const i :T[i] = ...) {...; a[i] = function(){...i...}; ...} would then mean what it should mean. Cool. Agreed. See https://bugzilla.mozilla.org/show_bug.cgi?id=449811 -- which is a bug we'd like to fix by following a spec. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Brendan Eich wrote: After some experiments, we decided for ES4 to make let and var the same at top level in a function or global code. This helped avoid implementation pain: very long scripts on the web, consisting of many statements in a row, motivate statement-wise parsing and cumulative code generation, which is straightforward in the absence of goto. Yet scope changes (e.g. due to a tardy rogue let x; after thousands of statements the first of which uses x) require a separate pass to generate the necessary block set-up code before the first use of x. Unifying let and var at top level also reduces the cognitive load (number of scopes in mind) and eliminates useless name shadowing opportunities. But such let/var unification at top level in a function body does leave bad old arguments[0] aliasing x in the above example, allowing mutation of otherwise lexical let x; (change var to let and fill in the ... with arguments[0] = 42; return x). The answer that we chose for ES4, and the one that's already agreeable in committee for Harmony, was deprecate arguments by providing optional and rest parameters. You can't do that. function f() { x = 15; ... let t = some_runtime_expression; ... let x:t = ... } What is the type of x at the beginning of the function? Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Brendan Eich wrote: The agreement from the May TC39 meeting was that the declarations implicit (:*) and explicit annotations must normalize to the same type, or there's an error. That was back when the language had lots of requirements for compile-time expressions, including on all types. We agreed that that's not part of ES-Harmony, and this condition doesn't make sense when type expressions are evaluated at run time. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 13, 2008, at 3:51 PM, Waldemar Horwat wrote: Brendan Eich wrote: After some experiments, we decided for ES4 to make let and var the same at top level in a function or global code. This helped avoid implementation pain: very long scripts on the web, consisting of many statements in a row, motivate statement-wise parsing and cumulative code generation, which is straightforward in the absence of goto. Yet scope changes (e.g. due to a tardy rogue let x; after thousands of statements the first of which uses x) require a separate pass to generate the necessary block set-up code before the first use of x. Unifying let and var at top level also reduces the cognitive load (number of scopes in mind) and eliminates useless name shadowing opportunities. But such let/var unification at top level in a function body does leave bad old arguments[0] aliasing x in the above example, allowing mutation of otherwise lexical let x; (change var to let and fill in the ... with arguments[0] = 42; return x). The answer that we chose for ES4, and the one that's already agreeable in committee for Harmony, was deprecate arguments by providing optional and rest parameters. You can't do that. Which that do you mean? function f() { x = 15; ... let t = some_runtime_expression; ... let x:t = ... } What is the type of x at the beginning of the function? The example had better fail to compile. I hope my point about implementations optimizing for very long AST hedge programs on the web is mixed up here. Such implementations build a full AST for function bodies, and function bodies tend to be reasonably short on the web (Tennent be damned). Long global code consisting of repeated assignment statements, OTOH, can suck up too much time and space if compiled as a whole instead of incrementally. But I still don't know which that you meant. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 13, 2008, at 3:56 PM, Waldemar Horwat wrote: Brendan Eich wrote: The agreement from the May TC39 meeting was that the declarations implicit (:*) and explicit annotations must normalize to the same type, or there's an error. That was back when the language had lots of requirements for compile- time expressions, including on all types. We agreed that that's not part of ES-Harmony, and this condition doesn't make sense when type expressions are evaluated at run time. True enough -- but even without normalization, multiple equivalent (at runtime, depending on flow) annotations could be allowed. Should they be? /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Brendan Eich wrote: If using an uninitialized let binding is an error, then hoisting is pointless except to make the statements between start of block and the let declaration a dead zone for the binding name. This fits the ancient, weak but not entirely worthless post-hoc rationale for var hoisting (to avoid confusion among novice or inexperienced programmers by making many scopes, each implicitly opened by var), but it's not particularly useful. This was our agreement from the ES4 days. It's very useful, in that it allows mutually recursive lambdas. What's more, as discussed here and in TC39, repeated let declarations for the same binding name within the same block should be allowed. You can't do that in ES-Harmony. There is no way to tell if the two let declarations have the same type. For orthogonality you'd also need to allow multiple const declarations within the same scope, and I just don't want to go there. Anything else is user- and refactoring-hostile. So the non-initial let declarations for a given name in the same block would be ignored. This is loaded language, but I can't tell how requiring there to be a unique point of definition for a const or let is user-hostile. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 13, 2008, at 4:01 PM, Waldemar Horwat wrote: Brendan Eich wrote: If using an uninitialized let binding is an error, then hoisting is pointless except to make the statements between start of block and the let declaration a dead zone for the binding name. This fits the ancient, weak but not entirely worthless post-hoc rationale for var hoisting (to avoid confusion among novice or inexperienced programmers by making many scopes, each implicitly opened by var), but it's not particularly useful. This was our agreement from the ES4 days. It's very useful, in that it allows mutually recursive lambdas. For function bindings, yes, of course -- but we were talking about let bindings (I thought). For let (there is no 'let function') bindings, how? Could you give an example? What's more, as discussed here and in TC39, repeated let declarations for the same binding name within the same block should be allowed. You can't do that in ES-Harmony. There is no way to tell if the two let declarations have the same type. There has to be a way to answer the question are these two terms the same type at run-time. For orthogonality you'd also need to allow multiple const declarations within the same scope, and I just don't want to go there. I don't agree -- const and let are different keywords, they obviously differ in read-only vs. writable binding. They could differ otherwise. Anything else is user- and refactoring-hostile. So the non-initial let declarations for a given name in the same block would be ignored. This is loaded language, but I can't tell how requiring there to be a unique point of definition for a const or let is user-hostile. My words were about let, not const. If let is the new var, then you either accept that re-declaration in the same block (usually function body) of the same name via var is common for good reason, and allow let to be used instead; or you raise the migration tax. I do not mean to over-load the language, but we've seen a lot of code that redeclares using var (e.g. for (var i...) in adjacent for loops). We tried making a strict warning (error console spew) for redeclarations of same kind (var vs. var) four years ago (IIRC -- Firefox 1 betas) and felt the heat from developers. We fell back on mixed var vs. function (const vs. anything is an error of course). People on this list have argued similarly based on refactoring and var hoisting. It may be that the migration tax must rise here, but I'd like to know why type annotations can't be equated at runtime. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Mon, Oct 13, 2008 at 6:34 PM, Brendan Eich [EMAIL PROTECTED] wrote: On the contrary, what's magical is when one stores i in elements of an array, or calls setTimeout (example in https://bugzilla.mozilla.org/show_bug.cgi?id=449811) , and the results are all the last value of the loop variable. This has been a frequent source of confusion and complaints -- a bug, in short. Yes, and binding a fresh induction variable on every iteration makes sense for a 'for-each' loop (as in the bug report you cited), where the user is not in charge of updating the induction variable by means of explicit assignment. In a plain 'for' loop, however, it *is* magic if an assignment results in a fresh binding. And it's unexpected magic. Mark said that there was a desugaring for 'for' to 'lambda,' without special cases, where this all works out, but I haven't been able to figure out what rewrite he had in mind. Doing it the way Mark proposes fixes the bug, and has no other bad effects that I can see (but we'll have to implement and user-test to be sure). Turning an assignment into a non-assignment is bad. I could be convinced, though -- do you want to try and make the case for why there should be a fresh variable per iteration with some concrete examples? https://bugzilla.mozilla.org/show_bug.cgi?id=449811 Like I said, it makes sense for 'for-each.' -Jon ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Brendan Eich wrote: On Oct 13, 2008, at 3:56 PM, Waldemar Horwat wrote: Brendan Eich wrote: The agreement from the May TC39 meeting was that the declarations implicit (:*) and explicit annotations must normalize to the same type, or there's an error. That was back when the language had lots of requirements for compile-time expressions, including on all types. We agreed that that's not part of ES-Harmony, and this condition doesn't make sense when type expressions are evaluated at run time. True enough -- but even without normalization, multiple equivalent (at runtime, depending on flow) annotations could be allowed. Should they be? No. Just because the type matched the last five times you evaluated it doesn't mean that it will match the next time. Also, with multiple lets it's too easy to mistake the case of shadowing (when multiple let declarations are in nested blocks) with sharing (when they are far apart in the same block). You might refactor: { let x = outer; ... use x ... if (always_true) { let x = inner; ... use x ... } ... use x ... } into: { let x = outer; ... use x ... let x = inner; ... use x ... ... use x ... } Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 13, 2008, at 4:14 PM, Jon Zeppieri wrote: Yes, and binding a fresh induction variable on every iteration makes sense for a 'for-each' loop (as in the bug report you cited), where the user is not in charge of updating the induction variable by means of explicit assignment. In a plain 'for' loop, however, it *is* magic if an assignment results in a fresh binding. Why is the assignment operator relevant? The question is the binding scope of i in for (let i = 0; i N; i++) ... No curly braces required, we already have this in JS1.7+ and the let is scoped to the for head except for the initializer of i (you can write let i = x, j = y; too -- x and y are evaluated in the outer scope). There's scope magic in this form even though it uses = for assignment and creates only one block scope for the loop, no how many times the loop iterates. And it's unexpected magic. Users differ on this point, but we've had long-standing confusion and complaints about closures capturing the last value of i in let a = [1,2,3,4]; let b = []; for (let i = 0; i a.length; i++) b[i] = function () { return i*i; } and the like. Getting 16 back from b[0]() is unexpected bad magic. Users may be modeling closures as capturing bindings, not scope chains of mutable objects, one per for (let...) statement or explicitly braced block. If so, could we make let declaration capture this way? Again, I'm proceeding from real users' complaints, not idle wishes. Mark said that there was a desugaring for 'for' to 'lambda,' without special cases, where this all works out, but I haven't been able to figure out what rewrite he had in mind. Tail-recursive lambda rewrite of a C-style for loop should be easy for you :-P. Doing it the way Mark proposes fixes the bug, and has no other bad effects that I can see (but we'll have to implement and user-test to be sure). Turning an assignment into a non-assignment is bad. Assignment is not the issue, the binding's scope is. https://bugzilla.mozilla.org/show_bug.cgi?id=449811 Like I said, it makes sense for 'for-each.' Progress! /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
David-Sarah Hopwood wrote: Mark S. Miller wrote: [...] I recommend the per-iteration view. If we can agree quickly on per-iteration, then for (const x = ...) {...x...} should be allowed in ES3.1 (whether or not const hoists to block start). After ES3.1 for (const i :T[i] = ...) {...; a[i] = function(){...i...}; ...} would then mean what it should mean. Cool. Not so fast :-) Consider: for (let i = 0; i 10; i++) { ... } In the i++, which iteration's 'i' is the LeftHandSideExpression referring to? Or does this expand to something like: let ($i = 0) { for (; let (i = $i) {i 10}; let (i = $i) {{i++;} $i = i;}} { let (i = $i) {...}; } } ? This expansion is wrong for the case where the body updates i. I'll have to think about it some more. -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Mark S. Miller wrote: On Sat, Oct 11, 2008 at 9:11 AM, Peter Michaux [EMAIL PROTECTED] wrote: I think it would be ok to have only unnamed lambdas. (It would be ok to have named lambdas too.) I think we should not introduce named lambdas because then we'd need to decide whether the scoping of a lambda name works the same as the scoping of a function name. Do we really want to reproduce the following confusions: * Distinction between named function expressions vs named function declarations? * The name of a name function expression is only in scope within the function, whereas the name of a named function declaration is in (letrec) scope in the containing block. * Because these look the same, a named function expression cannot be used naked as the start of an expression statement. It must wear protective parentheses. * For consistency, an anonymous function expression also cannot be used as a naked as the start of an expression statement. If lambdas had optional names, we would either need to reproduce the above confusions or deviate from them. Either choice is terribly unpleasant. The main benefit of named lambdas is intelligible stack traces in a debugger. What would you replace them with? In a lot of places there is no other good place to stick a name onto; I don't want to assign a lambda to a variable just so I can name it. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Mon, Oct 13, 2008 at 7:39 PM, Brendan Eich [EMAIL PROTECTED] wrote: On Oct 13, 2008, at 4:14 PM, Jon Zeppieri wrote: Yes, and binding a fresh induction variable on every iteration makes sense for a 'for-each' loop (as in the bug report you cited), where the user is not in charge of updating the induction variable by means of explicit assignment. In a plain 'for' loop, however, it *is* magic if an assignment results in a fresh binding. Why is the assignment operator relevant? The question is the binding scope of i in for (let i = 0; i N; i++) ... How is scope the issue? As far as I know, we don't disagree about scope. The assignment I'm referring to, in this example, is the 'i++' part. Mark is proposing that this does not mean increment i by one, but rather rebind i with the value of i+1 -- which is completely different and not what the user wrote. No curly braces required, we already have this in JS1.7+ and the let is scoped to the for head except for the initializer of i (you can write let i = x, j = y; too -- x and y are evaluated in the outer scope). There's scope magic in this form even though it uses = for assignment and creates only one block scope for the loop, no how many times the loop iterates. I think you misread my message. We do not disagree at all about the scope of the initialization clause, but rather the meaning of the update clause. This is why the 'for-each' loop is not problematic; it doesn't have an update clause. And it's unexpected magic. Users differ on this point, but we've had long-standing confusion and complaints about closures capturing the last value of i in let a = [1,2,3,4]; let b = []; for (let i = 0; i a.length; i++) b[i] = function () { return i*i; } and the like. Getting 16 back from b[0]() is unexpected bad magic. I understand this, but I don't see how the answer is to change the meaning of 'i++' when used in the update clause of a 'for' loop. Users may be modeling closures as capturing bindings, not scope chains of mutable objects, one per for (let...) statement or explicitly braced block. If so, could we make let declaration capture this way? Again, I'm proceeding from real users' complaints, not idle wishes. Mark said that there was a desugaring for 'for' to 'lambda,' without special cases, where this all works out, but I haven't been able to figure out what rewrite he had in mind. Tail-recursive lambda rewrite of a C-style for loop should be easy for you :-P. That's not the point. I'm talking about a rewrite from 'for' to 'lambda' that satisfies the following properties: 1) for (var i = 0; i len; i++) ... continues to mean what it means in ES3. 2) for (let i = 0; i len; i++) ... has the proper scope for 'i' (which you reiterated above), *and* 'i' is rebound -- not mutated -- on each iteration. 3) The rewrite rules are the *same,* regardless of whether it's a for (var ...) or a for (let ...) loop. At least, that's what I took Mark to mean. He can correct me if I'm wrong. -Jon ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 13, 2008, at 4:09 PM, Brendan Eich wrote: On Oct 13, 2008, at 4:01 PM, Waldemar Horwat wrote: Brendan Eich wrote: If using an uninitialized let binding is an error, then hoisting is pointless except to make the statements between start of block and the let declaration a dead zone for the binding name. This fits the ancient, weak but not entirely worthless post-hoc rationale for var hoisting (to avoid confusion among novice or inexperienced programmers by making many scopes, each implicitly opened by var), but it's not particularly useful. This was our agreement from the ES4 days. It's very useful, in that it allows mutually recursive lambdas. For function bindings, yes, of course -- but we were talking about let bindings (I thought). For let (there is no 'let function') bindings, how? Could you give an example? Obvious example: { let even = function (n) n == 0 || odd(n - 1); let odd = function (n) n != 0 even(n - 1); print(even(42), odd(42)); print(even(99), odd(99)); } My words about function binding meant to suggest: why not require users to write { function even(n) n == 0 || odd(n - 1); function odd(n) n != 0 even(n - 1); ... } But I'm not arguing that we shouldn't hoist let to top of block, only trying to justify my not particularly useful assertion ;-). /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Brendan Eich wrote: On Oct 13, 2008, at 4:01 PM, Waldemar Horwat wrote: Brendan Eich wrote: If using an uninitialized let binding is an error, then hoisting is pointless except to make the statements between start of block and the let declaration a dead zone for the binding name. This fits the ancient, weak but not entirely worthless post-hoc rationale for var hoisting (to avoid confusion among novice or inexperienced programmers by making many scopes, each implicitly opened by var), but it's not particularly useful. This was our agreement from the ES4 days. It's very useful, in that it allows mutually recursive lambdas. For function bindings, yes, of course -- but we were talking about let bindings (I thought). For let (there is no 'let function') bindings, how? Could you give an example? I am talking about let bindings. Lars brought up at that meeting. I did not find the use cases particularly convincing, but the dead zone is compelling. There are four ways to do this: A1. Lexical dead zone. References textually prior to a definition in the same block are an error. A2. Lexical window. References textually prior to a definition in the same block go to outer scope. B1. Temporal dead zone. References temporally prior to a definition in the same block are an error. B2. Temporal window. References temporally prior to a definition in the same block go to outer scope. Let's take a look at an example: let x = outer; function g() {return outer} { g(); function f() { ... x ... g ... g() ... } f(); var t = some_runtime_type; const x:t = inner; function g() { ... x ... } g(); f(); } B2 is bad because then the x inside g would sometimes refer to outer and sometimes to inner. A1 and A2 introduce extra complexity but doesn't solve the problem. You'd need to come up with a value for x to use in the very first call to g(). Furthermore, for A2 whether the window occurred or not would also depend on whether something was a function or not; users would be surprised that x shows through the window inside f but g doesn't. That leaves B1, which matches the semantic model (we need to avoid referencing variables before we know their types and before we know the values of constants). What's more, as discussed here and in TC39, repeated let declarations for the same binding name within the same block should be allowed. You can't do that in ES-Harmony. There is no way to tell if the two let declarations have the same type. There has to be a way to answer the question are these two terms the same type at run-time. I'm sure there will be, but that's not the issue. What we need to know is that everyone agrees on the type of the variable. You might write code assuming that the type is set by a nearby let statement, when in fact it's some other let statement that set it. For orthogonality you'd also need to allow multiple const declarations within the same scope, and I just don't want to go there. I don't agree -- const and let are different keywords, they obviously differ in read-only vs. writable binding. They could differ otherwise. They shouldn't differ any more than that. If they do, it would just add gratuitous hair to the language. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 13, 2008, at 5:00 PM, Jon Zeppieri wrote: On Mon, Oct 13, 2008 at 7:39 PM, Brendan Eich [EMAIL PROTECTED] wrote: On Oct 13, 2008, at 4:14 PM, Jon Zeppieri wrote: Yes, and binding a fresh induction variable on every iteration makes sense for a 'for-each' loop (as in the bug report you cited), where the user is not in charge of updating the induction variable by means of explicit assignment. In a plain 'for' loop, however, it *is* magic if an assignment results in a fresh binding. Why is the assignment operator relevant? The question is the binding scope of i in for (let i = 0; i N; i++) ... How is scope the issue? As far as I know, we don't disagree about scope. Probably we're at cross purposes (I often am ;-) because of the primal sin in ECMAScript of specifying scope via object, and closure via scope chain capture. The assignment I'm referring to, in this example, is the 'i++' part. Mark is proposing that this does not mean increment i by one, but rather rebind i with the value of i+1 -- which is completely different and not what the user wrote. Gotcha -- I agree, this is a problem for a proposal that tries to desugar for(;;). Curses. The user expectation with a closure in a for(var i = 0; i N; i++) loop, that each closure captures i's current value, remains, completely parallel to the for (i in o) ... case. Any attempt to solve it via let instead of var -- other than by making multiple (blech) scope (barf) objects, one per iteration -- must respecify how closures work. Hmm. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
David Herman wrote: Also, I wonder why lambda in it's block-less form is restricted to expressions. I'm with you... but I'd want to check with the experts on the ES grammar to see whether this introduces any nasty ambiguities. Please specify what you are proposing. The one proposal I've seen is: Expression ::= ... | lambda Formals Statement This is not particularly useful because then even assign a lambda to a variable would be a syntax error, and you'd introduce a bizarre asymmetry into the ExpressionNoIn case. The questions you'd have to answer are: - Where in the expression grammar would you introduce a lambda? - Is lambda a reserved word? - How does it interact with semicolon insertion? As written, if the Statement were an expression statement then the semicolon would be mandatory. - Do you really want to have extra semicolons in the middle of statements? Now you're forced to accept things like the following: for (; lambda() x++;, lambda() y++;,; lambda() z++;) ... Overall, this syntax does not look promising. It's likely to get you into complexity trouble. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 13, 2008, at 4:54 PM, Waldemar Horwat wrote: I am talking about let bindings. Lars brought up at that meeting. I did not find the use cases particularly convincing, but the dead zone is compelling. There are four ways to do this: A1. Lexical dead zone. References textually prior to a definition in the same block are an error. A2. Lexical window. References textually prior to a definition in the same block go to outer scope. B1. Temporal dead zone. References temporally prior to a definition in the same block are an error. B2. Temporal window. References temporally prior to a definition in the same block go to outer scope. Let's take a look at an example: let x = outer; function g() {return outer} { g(); function f() { ... x ... g ... g() ... } f(); var t = some_runtime_type; const x:t = inner; function g() { ... x ... } g(); f(); } B2 is bad because then the x inside g would sometimes refer to outer and sometimes to inner. A1 and A2 introduce extra complexity but doesn't solve the problem. You'd need to come up with a value for x to use in the very first call to g(). Furthermore, for A2 whether the window occurred or not would also depend on whether something was a function or not; users would be surprised that x shows through the window inside f but g doesn't. That leaves B1, which matches the semantic model (we need to avoid referencing variables before we know their types and before we know the values of constants). Agreed, this is compelling, it shows the insufficiency of lexical order. I missed this somehow (where are those meeting minutes? Still being vetted by TC39?). Thanks for restating it. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Brendan Eich wrote: On Oct 13, 2008, at 5:29 PM, Waldemar Horwat wrote: Brendan Eich wrote: On Oct 13, 2008, at 3:51 PM, Waldemar Horwat wrote: Brendan Eich wrote: After some experiments, we decided for ES4 to make let and var the same at top level in a function or global code. ... You can't do that. Which that do you mean? Turn let into var at the top level of a function body. Ok then! function f() { x = 15; ... let t = some_runtime_expression; ... let x:t = ... } What is the type of x at the beginning of the function? The example had better fail to compile. Because presumably the let x:t became var x:t and var can't have types? Why can't var have a type annotation? Because a function can have many var declarations for the same variable and because you can use the variable before any of the var declarations are evaluated. You can work out the implications easily enough. This has been brought up at meetings before. You can't be serious that let would be broken unless enclosed by an independent block! I didn't say that :-P. Actually, you did. You wrote that you'd like let to become var (and unlike let as used within an independent block) if used at the top level of a function: After some experiments, we decided for ES4 to make let and var the same at top level in a function or global code. This helped avoid implementation pain: very long scripts on the web, consisting of many statements in a row, motivate statement-wise parsing and cumulative code generation, which is straightforward in the absence of goto. Yet scope changes (e.g. due to a tardy rogue let x; after thousands of statements the first of which uses x) require a separate pass to generate the necessary block set-up code before the first use of x. What did you mean by had better fail to compile? Other than the type annotation, there is nothing about function f() { x = 15; ... var t = some_runtime_expression; ... var x:t = ... } that ought to fail to compile. Waldemar ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 13, 2008, at 6:08 PM, Waldemar Horwat wrote: Brendan Eich wrote: Because presumably the let x:t became var x:t and var can't have types? Why can't var have a type annotation? Because a function can have many var declarations for the same variable and because you can use the variable before any of the var declarations are evaluated. You can work out the implications easily enough. This has been brought up at meetings before. Of course, ES4 had var x:t all over, but as you noted, different rules for evaluating t. Still, with new syntax comes the opportunity for reformed semantics, including restrictions on any bad var abusages we would like to move away from. So my question remains (amended to be clear about what is the same): why does this mean we cannot equate let and var scope at the top level? I'm not talking about allowing them to mix in bad ways, or mix at all (see below). I'm talking about not having an implicit block around top-level function and global code. You wrote that you'd like let to become var (and unlike let as used within an independent block) if used at the top level of a function: Ok, sorry for being unclear. I do not mean to translate top-level 'let' to 'var' and free let bindings from necessary restrictions. I do mean that let binds in the variable object, and let usage restricts other usage. We can forbid mixed var x and let x at top level. We can require the same single definition for any let x at any level. We can forbid arguments usage if arguments[i] could alias a let binding, since let is new (along with rest and optional parameters to enable arguments deprecation). But must we have an implicit block around programs and function bodies that contain let declarations, distinct from the variable scope (object)? What did you mean by had better fail to compile? Other than the type annotation, there is nothing about function f() { x = 15; ... var t = some_runtime_expression; ... var x:t = ... } that ought to fail to compile. The assignment to x in that temporal dead zone before t's initializer has been evaluated. Why is this different if you s/var x/let x/? /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Waldemar Horwat wrote: I am talking about let bindings. Lars brought up at that meeting. I did not find the use cases particularly convincing, but the dead zone is compelling. There are four ways to do this: A1. Lexical dead zone. References textually prior to a definition in the same block are an error. A2. Lexical window. References textually prior to a definition in the same block go to outer scope. B1. Temporal dead zone. References temporally prior to a definition in the same block are an error. B2. Temporal window. References temporally prior to a definition in the same block go to outer scope. Let's take a look at an example: let x = outer; function g() {return outer} { g(); function f() { ... x ... g ... g() ... } f(); var t = some_runtime_type; const x:t = inner; function g() { ... x ... } g(); f(); } B2 is bad because then the x inside g would sometimes refer to outer and sometimes to inner. A1 and A2 introduce extra complexity but doesn't solve the problem. I already suggested a solution to the problem based on a refinement of A1: http://www.mail-archive.com/es-discuss@mozilla.org/msg00899.html That solution is entirely compatible with runtime types. The same identifier can be declared with different types in different parts of a block, and each reference to it uses the one that is textually in scope, so there is no need to check compatibility of types. -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
David-Sarah Hopwood wrote: Waldemar Horwat wrote: I am talking about let bindings. Lars brought up at that meeting. I did not find the use cases particularly convincing, but the dead zone is compelling. There are four ways to do this: A1. Lexical dead zone. References textually prior to a definition in the same block are an error. A2. Lexical window. References textually prior to a definition in the same block go to outer scope. B1. Temporal dead zone. References temporally prior to a definition in the same block are an error. B2. Temporal window. References temporally prior to a definition in the same block go to outer scope. Let's take a look at an example: let x = outer; function g() {return outer} { g(); function f() { ... x ... g ... g() ... } f(); var t = some_runtime_type; const x:t = inner; function g() { ... x ... } g(); f(); } B2 is bad because then the x inside g would sometimes refer to outer and sometimes to inner. A1 and A2 introduce extra complexity but doesn't solve the problem. I already suggested a solution to the problem based on a refinement of A1: http://www.mail-archive.com/es-discuss@mozilla.org/msg00899.html This post was more directly relevant: http://www.mail-archive.com/es-discuss@mozilla.org/msg00889.html That solution is entirely compatible with runtime types. The same identifier can be declared with different types in different parts of a block, and each reference to it uses the one that is textually in scope, so there is no need to check compatibility of types. -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Yuh-Ruey Chen wrote: Mark S. Miller wrote: On Sat, Oct 11, 2008 at 4:33 PM, Dave Herman [EMAIL PROTECTED] wrote: Expression ::= ... | lambda Formals? Statement Statement or SubStatement? If Statement, what meaning do you propose for { const f = (lambda () const x = 3;); x } Does the const export its binding into the lambda's enclosing block, such that the x on the next line refers to that x? I hope not. If the answer is SubStatement, then changing the answer to Block in order to require the curlies may very well help people understand the scope relationships across the lambda boundary. Now that I think about it, would it truly be necessary for lambda to create an implicit block scope in the first place? It's not strictly necessary, but it's quite ugly not to. We are intending to restrict 'eval' precisely to remove the ability to inject bindings into the surrounding scope (although 'eval' is worse because the scoping it creates isn't statically analysable). |lambda() return 10| would not require such a block scope. Why not have the block scope only created if there are curly brackets? That would follow the precedent set by the rest of the language with regards to block scope No, that would be entirely inconsistent with the rest of the language: { while (...) let x = ...; } is equivalent to { while (...) { let x = ...; } } not { let x; while (...) { x = ...; } } with the exception of |for (let x...) ...|. I don't think that such an unscoped lambda provides enough (if any) value to justify its complexity. -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
David-Sarah Hopwood wrote: Yuh-Ruey Chen wrote: Now that I think about it, would it truly be necessary for lambda to create an implicit block scope in the first place? It's not strictly necessary, but it's quite ugly not to. We are intending to restrict 'eval' precisely to remove the ability to inject bindings into the surrounding scope (although 'eval' is worse because the scoping it creates isn't statically analysable). |lambda() return 10| would not require such a block scope. Why not have the block scope only created if there are curly brackets? That would follow the precedent set by the rest of the language with regards to block scope No, that would be entirely inconsistent with the rest of the language: { while (...) let x = ...; } is equivalent to { while (...) { let x = ...; } } not { let x; while (...) { x = ...; } } I was under the impression that such statements should be disallowed, following the example of JS1.8. In JS1.8 (Fx3), the following are all syntax errors: while (...) let x = ...; do let x = ...; while (...); if (...) let x = ...; for (...) let x = ...; Actually, this might also apply to JS1.7, but I don't have a copy of Fx2 handy. ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sun, Oct 12, 2008 at 2:19 PM, Yuh-Ruey Chen [EMAIL PROTECTED] wrote: David-Sarah Hopwood wrote: { while (...) let x = ...; } I was under the impression that such statements should be disallowed, following the example of JS1.8. In JS1.8 (Fx3), the following are all syntax errors: while (...) let x = ...; do let x = ...; while (...); if (...) let x = ...; for (...) let x = ...; These are unconditionally disallowed starting in ES3.1 because such declarations are Statements but not SubStatements and only SubStatements are allowed by the grammar at the positions above. -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Mark S. Miller wrote: On Sun, Oct 12, 2008 at 2:19 PM, Yuh-Ruey Chen [EMAIL PROTECTED] wrote: David-Sarah Hopwood wrote: { while (...) let x = ...; } I was under the impression that such statements should be disallowed, following the example of JS1.8. In JS1.8 (Fx3), the following are all syntax errors: while (...) let x = ...; do let x = ...; while (...); if (...) let x = ...; for (...) let x = ...; These are unconditionally disallowed starting in ES3.1 because such declarations are Statements but not SubStatements and only SubStatements are allowed by the grammar at the positions above. Excellent. (I had thought these were only disallowed in strict mode, based on MarkM's comment at http://groups.google.com/group/google-caja-discuss/browse_thread/thread/ca99f5af671a7dc1/7121d2e6d732f0fc#7121d2e6d732f0fc, but disallowing them unconditionally is even better IMHO.) So why are we arguing about whether lambdas should be allowed without braces, when the direction being taken for the rest of the language is to make the braces mandatory around all forms that can potentially declare variables? -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Mark S. Miller wrote: On Sun, Oct 12, 2008 at 2:19 PM, Yuh-Ruey Chen [EMAIL PROTECTED] wrote: David-Sarah Hopwood wrote: { while (...) let x = ...; } I was under the impression that such statements should be disallowed, following the example of JS1.8. In JS1.8 (Fx3), the following are all syntax errors: while (...) let x = ...; do let x = ...; while (...); if (...) let x = ...; for (...) let x = ...; These are unconditionally disallowed starting in ES3.1 because such declarations are Statements but not SubStatements and only SubStatements are allowed by the grammar at the positions above. Since LabelledStatement is included in SubStatement, the following is allowed in the current ES3.1 draft: while (...) foo: var x = ...; I would suggest that it shouldn't be, i.e. that LabelledStatement should be removed from SubStatement. [Jacaranda has similar, but stricter restrictions on substatements. See the rationale for group SIMPLE_STATEMENTS in the Jacaranda spec, http://www.jacaranda.org/jacaranda-spec-0.3.txt.] -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sun, Oct 12, 2008 at 7:34 PM, David-Sarah Hopwood [EMAIL PROTECTED] wrote: So why are we arguing about whether lambdas should be allowed without braces, when the direction being taken for the rest of the language is to make the braces mandatory around all forms that can potentially declare variables? What rest of the language are you talking about? |if|, |while|, and |for| do not require braces and thus do not necessarily create block scopes. I can't imagine a situation where |lambda(...) non_let_statement| would require creating a block scope for the lambda, unless that non_let_statement is a block itself. ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sun, Oct 12, 2008 at 5:45 PM, David-Sarah Hopwood [EMAIL PROTECTED] wrote: Since LabelledStatement is included in SubStatement, the following is allowed in the current ES3.1 draft: while (...) foo: var x = ...; This will be corrected in the next version. But the agreed fix is not to make LabelledStatement a Statement. The fix is to make a LabelledStatement contain only a SubStatement. So the above will still be rejected, but while (...) foo: {var x = ...;} will be fine. -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
YR Chen wrote: On Sun, Oct 12, 2008 at 7:34 PM, David-Sarah Hopwood [EMAIL PROTECTED] wrote: So why are we arguing about whether lambdas should be allowed without braces, when the direction being taken for the rest of the language is to make the braces mandatory around all forms that can potentially declare variables? What rest of the language are you talking about? |if|, |while|, and |for| do not require braces and thus do not necessarily create block scopes. |if|, |while|, |do..while|, |for|, and |with| *do* necessarily create block scopes. Their bodies are always scopes regardless of whether they have braces around them, even in ES3. (When the body is a statement that does not introduce any variables, then whether a scope is created isn't observable, and so we might as well consider them as creating a scope, for regularity.) In ES3.1, this will mean that they normally require braces whenever a body can introduce variables. There are two classes of exceptions, shown by these examples: a) while (...) foo: var x = ...; b) while (...) for (var x = ...; ...; ...) {} I think these are bugs. a) can certainly be disallowed. There might possibly be existing code that is relying on b), confusing though it is, but it can be disallowed in strict mode at least. -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sun, Oct 12, 2008 at 7:55 PM, David-Sarah Hopwood [EMAIL PROTECTED] wrote: In ES3.1, this will mean that they normally require braces whenever a body can introduce variables. There are two classes of exceptions, shown by these examples: a) while (...) foo: var x = ...; b) while (...) for (var x = ...; ...; ...) {} I think these are bugs. a) can certainly be disallowed. There might possibly be existing code that is relying on b), confusing though it is, but it can be disallowed in strict mode at least. ES3.1 will disallow #a because a LabelledStatement can only contain a SubStatement. A variable declaration is a Statement but not a SubStatement. Your point about #b is interesting. I do not know if it has previously been raised. -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
if (h == 0) h = function() {break}; Did you mean if (x == 0)? That's been confusing me in trying to read your example. Dave ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sat, Oct 11, 2008 at 10:02 AM, Mark S. Miller [EMAIL PROTECTED] wrote: On Sat, Oct 11, 2008 at 9:11 AM, Peter Michaux [EMAIL PROTECTED] wrote: As it stands, I always write the following in ES3 var f = function() {}; and now that arguments.callee is on the chopping block, I've started writing recursion as the painful contortion below var f = (function() { var callee = function() { callee(); }; return callee; })(); I don't get it. Why not write var f = function() { f(); }; var f = function() {f();}; var g = f; f = function(){alert('broke your recursion :)');}; g(); The call to g will not create an infinite loop which is the intended behavior of the function object defined in the first line. The late binding of the recursive call to whatever f happens to reference is the problem. Peter ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Peter Michaux wrote: On Sat, Oct 11, 2008 at 10:02 AM, Mark S. Miller [EMAIL PROTECTED] wrote: On Sat, Oct 11, 2008 at 9:11 AM, Peter Michaux [EMAIL PROTECTED] wrote: As it stands, I always write the following in ES3 var f = function() {}; and now that arguments.callee is on the chopping block, I've started writing recursion as the painful contortion below var f = (function() { var callee = function() { callee(); }; return callee; })(); I don't get it. Why not write var f = function() { f(); }; var f = function() {f();}; var g = f; f = function(){alert('broke your recursion :)');}; g(); /*const*/ var f = function() { f(); }; Then just treat /*const*/ variables as if they were 'const', until at some point you no longer care about old versions of JScript and can globally replace '/*const*/ var' - 'const'. -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sat, Oct 11, 2008 at 11:59 AM, Brendan Eich [EMAIL PROTECTED] wrote: On Oct 11, 2008, at 9:05 AM, Peter Michaux wrote: How to define a variable that is local to the enclosing lambda? Isn't the ability to do that essential? Use let (the var replacement declaration form). Sounds good to me but it is a little confusing to keep track if let is either in or out of ES-Harmony and if it is partly in then which of the several JavaScript 1.7 uses are in and if there will be let, let*, letrec semantics. Peter ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Also, I wonder why lambda in it's block-less form is restricted to expressions. I'm with you... but I'd want to check with the experts on the ES grammar to see whether this introduces any nasty ambiguities. Dave ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Sounds good to me but it is a little confusing to keep track if let is either in or out of ES-Harmony and if it is partly in then which of the several JavaScript 1.7 uses are in and if there will be let, let*, letrec semantics. I've got no crystal ball, but I'd say it'd be unlikely (and terribly silly) that we'd have `lambda' without having `let'. (The `lambda' form would fail in its requirement as an equivalence-preserving primitive if it became a target of `var'-hoisting.) Dave ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sat, Oct 11, 2008 at 12:52 PM, Peter Michaux [EMAIL PROTECTED] wrote: Use let (the var replacement declaration form). Sounds good to me but it is a little confusing to keep track if let is either in or out of ES-Harmony and if it is partly in then which of the several JavaScript 1.7 uses are in and if there will be let, let*, letrec semantics. The let declaration is in. Like const and function declarations, it has block-level letrec lexical scoping. I continue to oppose let expressions and let statements, as they don't provide enough additional power to justify their cost. For example, if lambda supports optional args, as I think we all agree it should, then, adapting a suggestion of Lars, we should define the scope in which the default value expressions are evaluated so that var x = 2; (lambda (x = 3, y = x) (x+y))() returns 5, not 6. This gives us an easy enough let. The builtin let declarations provide an easy letrec. Given these, no one will miss let*. -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sat, Oct 11, 2008 at 1:21 PM, Mark S. Miller [EMAIL PROTECTED] wrote: The let declaration is in. Like const and function declarations, it has block-level letrec lexical scoping. I continue to oppose let expressions and let statements, as they don't provide enough additional power to justify their cost. For example, if lambda supports optional args, as I think we all agree it should, then, adapting a suggestion of Lars, we should define the scope in which the default value expressions are evaluated so that var x = 2; (lambda (x = 3, y = x) (x+y))() Simplifying (lambda (x = 3, y = x) (x+y))() to just let (x = 3, y = x) (x+y) makes it much more clear when reading code what the intention of the programmer was. The reader doesn't need to interpret the code with as many steps and doesn't need to check at the end of the expression to see if the lambda is immediately called or not. This checking is an issue when an immediately called lambda spans more than a screen. Repeatedly writing the whole immediately-called, anonymous function is boilerplate for such a common pattern. I admit it is not much character boiler plate but it is the fact that the pattern is so common that I believe the concept is worth abstracting. It is the mental boilerplate that is the waste. Peter ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 11, 2008, at 12:55 PM, David Herman wrote: How to define a variable that is local to the enclosing lambda? Isn't the ability to do that essential? No. With all due respect to Brendan, `var' hoisting to the top of a function body is one of the more problematic aspects of ES's semantics. I agree, it's no skin off my nose -- 'var' hoisting was an artifact of function implementation in Netscape 2, and did not apply to global vars then. It was standardized as hoisting in all kinds of code (global, function, and eval). We are stuck with it. However, hoisting still applies to let: If you want a local variable, use `let' -- it'll be local to its containing block. If you want a variable that is local to the entire body of a `lambda', use `let' at the top level of the `lambda' body. While let is local to containing block, the let-as-new-var proposal (implemented in Firefox 2 and up) hoists to top of block. So you cannot initialize the inner x using the outer x's value: { let x = 42; { let x = x; // undefined, not 42. alert(x); } alert(x); // 42, of course } We've discussed making use-before-set a strict error, but we've avoided it. The initialiser is not mandatory, and we do not wish to impose costly analysis on small implementations. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 11, 2008, at 12:52 PM, Peter Michaux wrote: On Sat, Oct 11, 2008 at 11:59 AM, Brendan Eich [EMAIL PROTECTED] wrote: On Oct 11, 2008, at 9:05 AM, Peter Michaux wrote: How to define a variable that is local to the enclosing lambda? Isn't the ability to do that essential? Use let (the var replacement declaration form). Sounds good to me but it is a little confusing to keep track if let is either in or out of ES-Harmony I do not see why you are confused. I wrote, in the original ECMAScript Harmony post: I heard good agreement on low-hanging de-facto standard fruit, particularly let as the new var, to match block-scoped const as still proposed (IIRC) in 3.1. See https://mail.mozilla.org/pipermail/es-discuss/2008-August/006837.html . and if it is partly in then which of the several JavaScript 1.7 uses are in and if there will be let, let*, letrec semantics. It's something else. See my reply about hoisting, just sent. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 11, 2008, at 2:43 PM, Brendan Eich wrote: On Oct 11, 2008, at 12:52 PM, Peter Michaux wrote: and if it is partly in then which of the several JavaScript 1.7 uses are in and if there will be let, let*, letrec semantics. It's something else. See my reply about hoisting, just sent. Mark and others have described the let declaration (let as new var) as like letrec. Close, but since forward references are possible and initiialization in the declaration is not mandatory, it's analogous, not identical. The function definition form in JS is letrec. But let as new var? I still say it's something else; I admit that like letrec or letrec-ilke works. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Hi Dave, first, my compliments on your lambda proposal. This should significantly simplify the core language after expanding sugars -- especially if you succeed at redefining function as desugaring to lambdas. That would be awesome! On Sat, Oct 11, 2008 at 5:34 AM, David Herman [EMAIL PROTECTED] wrote: Here's roughly the semantics of return-to-label: - return-to-label first checks to see if the label is live on the stack - if not, it raises an exception from the point where the return was attempted - but if so, it attempts to unwind the stack to the point where the label was executed, passing through any intervening finally clauses - if any of the finally clauses has its own non-local exit, this interrupts and aborts the unwinding Thank you for pointing out, though, that try/catch isn't so easily defined on top of return-to-label, since it still needs special handling for finally. The options are either to define a lower-level primitive underlying try/finally (akin to Scheme's dynamic-wind), or to leave exceptions -- or at least try/finally -- as primitive. I lean towards the latter; dynamic-wind is a subtle beast. [For those interested in dynamic-wind, Flatt et al's ICFP 07 paper has a nice investigation into its semantics: http://www.cs.utah.edu/plt/publications/icfp07-fyff.pdf] I haven't looked at the Flatt paper yet. But the semantics you explain above corresponds exactly to E's escape expressions. And E also treats try/finally primitive for similar reasons. (Historical note: The E escape construct and the call/ec previously explained both originate in an escape statement by Reynolds from a paper in the late 60s or early 70s. I don't recall whether Reynolds had any mechanism analogous to try/finally.) -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 11, 2008, at 2:55 PM, Mark S. Miller wrote: On Sat, Oct 11, 2008 at 2:42 PM, Brendan Eich [EMAIL PROTECTED] wrote: We've discussed making use-before-set a strict error, but we've avoided it. The initialiser is not mandatory, and we do not wish to impose costly analysis on small implementations. Since const use-before-set is an unconditional error (i.e., not dependent on strictness), implementations already have to pay for a read-barrier mechanism. Since we'd like to support type-annotation-constraints on initialized let variables, I think initialized let variables should have an unconditional read barrier as well. If using an uninitialized let binding is an error, then hoisting is pointless except to make the statements between start of block and the let declaration a dead zone for the binding name. This fits the ancient, weak but not entirely worthless post-hoc rationale for var hoisting (to avoid confusion among novice or inexperienced programmers by making many scopes, each implicitly opened by var), but it's not particularly useful. What's more, as discussed here and in TC39, repeated let declarations for the same binding name within the same block should be allowed. Anything else is user- and refactoring-hostile. So the non-initial let declarations for a given name in the same block would be ignored. This leaves efficiency as a concern. If implementations do not do the analysis required to catch use before set at compile time, then let as well as const pays the read barrier price. It's non-trivial. Today let (and var in the absence of eval) can be optimized to use a read in the VM, possibly even a load instruction -- no getter barrier required. Whatever the GC write barrier cost, reads dominate and this is a significant savings. On the other hand, computing SSA including dominance relations while parsing (in one pass) is possible so long as we do not add goto to the language. So maybe the analysis is acceptable to modern implementations, which are increasingly sophisticated. Still, it is not yet agreed that let use before set shall be an error. It certainly is not the case for var, and working code counts on the undefined default initial value. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sat, Oct 11, 2008 at 3:26 PM, Brendan Eich [EMAIL PROTECTED] wrote: Of course, let expressions would need lambda-coding no matter what names were shadowed. The experience gained in JS1.7+ shows more let block usage than let expression, but expression temporaries (lacking macros and ignoring automatically generated code) are uncommon in today's JS. The function-expression-immediately-applied cliché usually has statements for effect, if not a return -- not a single expression in its body. Here's an odd idea. (I'm not yet advocating this; just brainstorming) What if the body of a lambda were always a block (curlies enclosing a statement list), but, by analogy to eval, if the last statement executed is an expression statement, then invoking the lambda returns the value of that last expression statement. Let's say, further, that a lambda's parameter list is syntactically optional. Together, this would result in lambda{ } being a first-class control-flow block as in Smalltalk. Then, if we did want let-blocks that desugar to lambda, with no additional complexity, a let block could be an expression even though its body had statements. One construct would serve both as let expressions and let blocks, and would for free turn JavaScript into an expression language. 3 + let{ while(...){...}; 4; } would either not terminate or evaluate to 7. None of this would violate TC. -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sat, Oct 11, 2008 at 4:05 PM, Dave Herman [EMAIL PROTECTED] wrote: Read the proposal again: the statement form of lambdas *does* return the value of its last expression; this is what ES3 calls the completion value. Cool! So why are we still discussing proposed let expressions and let blocks as distinct constructs? -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 9, 2008, at 8:57 PM, Peter Michaux wrote: This keyword/scoping problem must already have appeared for functions as function declarations have var scoping and obtaining let scoping requires using something like let a = function(){}. This is pretty ugly for functions to have let scoping An agreement from TC39 this past sprint was that function definitions directly nested in blocks, not specified by ES3, defined block-local (let) bindings. There was general aversion to 'let function f() ...', an earlier ES4 idea already on the ropes. Separating binding forms from function definition and other options (such as const) avoids too many binding forms ('let const', 'let function', etc.). But too many binding forms is just too many, and the committee strongly favored using grammatical placement to avoid adding more syntactic complexity. but the good news is the door has been left open for real lambdas to snatch up the available var a(){} and let a(){} syntaxes. There's no reason to add var a() {} given function a() {} as a direct child of a program or function body. It seems to me let a(){} is Dave's define. So we're back to function vs. define/lambda. The idea of a desugaring let statement and let expression require lambda, the reformed function (whether define wins or not). But let declarations as the new var do not desugar to lambdas. They hoist, even if forward references (use before set) are errors. We haven't found a reformed var; I don't think there is one. This does not mean let declarations are somehow not worth adding. They're a big improvement on var declarations in our experience with let in JS1.7 and up. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Peter Michaux wrote: This keyword/scoping problem must already have appeared for functions as function declarations have var scoping and obtaining let scoping requires using something like let a = function(){}. This is pretty ugly for functions to have let scoping but the good news is the door has been left open for real lambdas to snatch up the available var a(){} and let a(){} syntaxes. ... and const a(){} or let const a(){}, which are what you would want in preference to var or let in the majority of cases for function definitions. (Is it just me, or is let const as proposed in http://wiki.ecmascript.org/doku.php?id=proposals:block_expressions a bit too verbose? E uses def for constant declarations, and IMHO it does matter that this is just as concise as var.) -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 10, 2008, at 1:25 PM, Waldemar Horwat wrote: So what should f(5, 0) do? function f(x, h) { while (true) { try { if (h == 0) h = function() {break}; Just to repeat something Dave wrote, we don't propose to allow break in a function where the break is not in a labeled statement, switch, or loop within that function. Only lambda would support such novelties. if (x != 0) f(x-1, h); else h(); This will break from the while (true) in the outermost (x = 5) activation of f. In Scheme implementations that support it, the analogue is call/ec -- call-with-escape-continuation (weak continuation is another name for escape continuation) -- where the caller's continuation is the argument to the procedure passed to call/ec. Escape continuations are cheaper to implement and simpler to reason about than full call/cc continuations because of the dynamic error (exception) you get if you call the escape continuation outside of the dynamic extent of the current call. Sorry if this is already known; Dave should wipe my chin as needed since he is the adult Schemer here and I'm the toddler. } catch (e) { alert(caught + e + on + x); } finally { alert(f called finally on + x); } alert(f looping on + x); } alert(f exited on + x); } The break itself does not propagate as an exception, just to be clear. If the statement being broken from is inactive, then an exception will be thrown from the break evaluation in the function that was assigned to h. The call to h would have to come after control flow had left the outermost while (true), via a statement after that loop, or some other call made via a returned or heap reference (upward funarg). /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 10, 2008, at 3:31 PM, Brendan Eich wrote: } catch (e) { alert(caught + e + on + x); } finally { alert(f called finally on + x); } Skipping the intervening active finally clauses is bad, though -- a bug in the current wiki rough draft that I should have mentioned. Dave will post a follow-up soon. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
David Herman wrote: My question was whether the semantics of break and continue would support the following: Yes, this is another good case to consider. Thanks for pointing it out; I'll add this to the strawman:lambdas proposal. Essentially this is another aspect of the semantics of 'function' that is implicit -- that it cancels out the scope of break/continue labels -- and it's precisely these implicit elements of a language feature that break expected equivalences. (They are essentially unhygienic -- if you push me, I can explain the connection to macros.) First off, I'm really glad that clean functions are being considered for ES-Harmony - another step toward hygienic macros! I read through the strawman:lambdas proposal and saw that it did not mention anything about |var|, e.g. (function() { lambda { var x = 10; }(); return x; })() Does the |var| within the lambda define a var in the function body, and does that var declaration hoist to the top of the function body? ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sep 2, 2008, at 2:16 PM, Lex Spoon wrote: On Sun, Aug 24, 2008 at 3:17 AM, Brendan Eich [EMAIL PROTECTED] wrote: First, let's settle the hash over whether any desugaring without extensions such as return-to-label, reformed lexical scope, tamed this, banished arguments, etc. etc., trumps adding a new binding form, to wit: let as block scoped var. With no extensions, it is true, return would end up returning from a different method under the proposed rewrite. Likewise, this and arguments would cause trouble. Sorry for the very tardy reply. You make good points in the abstract, and the messy language-specific details of existing semantics for functions not being clean enough deserves a better response than just don't desugar. I stand by don't desugar let to functions as-is. I'm also pretty certain don't add more modes or subsets to try to fix existing forms is sound, since versionitis does not help us either keep the spec simple or specify the backward-compatible semantics in the full language. So, to avoid trouble, we've been thinking of new forms including a better function, call it lambda, that has none of the compatibility baggage. I say we but really Dave Herman deserves credit for championing this. A lambda form has been a topic now and then for a while, on this list and in committee, and sometimes only as syntactic sugar (which would miss the opportunity for semantic reform) -- yet without it getting the breathing room it needs. Dave is working now in the http://wiki.ecmascript.org/doku.php?id=strawman:strawman space on the wiki. Don't throw stones, this is not in the harmony: namespace for good reason. Constructive comments welcome. And I still owe the list a story on wiki access that keeps Ecma happy and doesn't throw open the edit wars doors. Among the new strawman pages, the following are relevant and (I hope) helpful: http://wiki.ecmascript.org/doku.php?id=strawman:lambdas http://wiki.ecmascript.org/doku.php?id=strawman:lexical_scope http://wiki.ecmascript.org/doku.php?id=strawman:return_to_label Possibly break and continue would, depending on what their precise semantics are. JS has break from labeled statement, and continue to labeled loop bottom, a la Java. These look trouble-free to me. Let me know if you see a hard case. Thanks, /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Thu, Oct 9, 2008 at 5:31 PM, Brendan Eich [EMAIL PROTECTED] wrote: JS has break from labeled statement, and continue to labeled loop bottom, a la Java. These look trouble-free to me. Let me know if you see a hard case. Thanks, My question was whether the semantics of break and continue would support the following: while(true) { (function() { if (--x == 0) break; })(); } I honestly don't know, but it shouldn't cause any real trouble to allow it. The implementation would be analogous to that for labeled return. For example, if the appropriate while loop is no longer on the stack, the break would turn into an exception. As my usual disclaimer, I am not closely following the different ES trends including Harmony. I'm only commenting about what could possibly make the language more consistent and orthogonal. -Lex ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 9, 2008, at 3:05 PM, Lex Spoon wrote: On Thu, Oct 9, 2008 at 5:31 PM, Brendan Eich [EMAIL PROTECTED] wrote: JS has break from labeled statement, and continue to labeled loop bottom, a la Java. These look trouble-free to me. Let me know if you see a hard case. Thanks, My question was whether the semantics of break and continue would support the following: while(true) { (function() { if (--x == 0) break; })(); } That's currently a specified error (possibly at runtime; chapter 16 of ES3 allows it to be at compile time). So a future edition could allow it, probably without opt-in versioning. Our compatibility model does not guarantee exceptions, since it allows contemporaneous extensions that remove those exceptions (see ES3 chapter 16 again, second bulleted list, first bullet -- these lists need their own sub-sections and numbers!). I honestly don't know, but it shouldn't cause any real trouble to allow it. The implementation would be analogous to that for labeled return. Right, and break to label outside the function's body, but lexically in scope, would be completely analogous (or just the same ;-)): L: while (true) { (function () { ... // stuff possibly including a loop or switch that brackets the next line if (--x == 0) break L; ... })(); } For example, if the appropriate while loop is no longer on the stack, the break would turn into an exception. Yes, this new runtime exception is the price of admission. The exception seems to a major source of grief in the Java BGGA closures controversy, or at least it did when I last looked. But it comes up with escape continuations in Scheme, and it is inevitable if we want these kinds of program equivalences. I'm interested in the reactions of others on the list to such return/ break/continue from already-deactivated statement/frame exceptions. They could be caught and handled, of course. Feature and bug, dessert topping and floor wax ;-). /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 9, 2008, at 4:28 PM, David Herman wrote: How would people feel about the declaration form being 'define' instead of lambda? As in: define const(x) { lambda(y) x } Maybe I'm just accustomed to Scheme, but it looks awkward to me for the declaration form to be called lambda. Dylan also used 'define'. For named functions, it's less cryptic, it has clearer connotations. For anonymous functions, e.g.: (define (x) {...})(x) or return foo(define (y) {...}, z); your mileage *will* vary, but it seems worse by a hair to me. But I'm used to lambda as a term of art. The obscurity of lambda helps it avoid collisions (we have ways of unreserving keywords in property-name contexts, but these do not work for formal parameters and variables named define, which seem likelier at a guess than lambda -- spidering the web could help confirm this guess). The obscurity also arguably partners lambda better with function. Setting up define as a cleaner function seems to switch domains of discourse. Concretely, we have in ES3.1 Object.defineProperty and similarly named functions. These define APIs were prefigured by Object.prototype._defineGetter__, etc.. This sense of define has meant bind property name to value or getter/setter. On the other side, Python, E, etc. use def. But we would be verbose like Scheme and Dylan. So define vs. lambda. End of my bike-shedding ruminations. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Sorry, I was unclear. I meant 'lambda' for the expression form and 'define' for the definition form. Dave - Original Message - From: Brendan Eich [EMAIL PROTECTED] To: David Herman [EMAIL PROTECTED] Cc: Peter Michaux [EMAIL PROTECTED], es3 x-discuss [EMAIL PROTECTED], es-discuss@mozilla.org Sent: Thursday, October 9, 2008 9:12:26 PM GMT -05:00 US/Canada Eastern Subject: Re: return when desugaring to closures On Oct 9, 2008, at 4:28 PM, David Herman wrote: How would people feel about the declaration form being 'define' instead of lambda? As in: define const(x) { lambda(y) x } Maybe I'm just accustomed to Scheme, but it looks awkward to me for the declaration form to be called lambda. Dylan also used 'define'. For named functions, it's less cryptic, it has clearer connotations. For anonymous functions, e.g.: (define (x) {...})(x) or return foo(define (y) {...}, z); your mileage *will* vary, but it seems worse by a hair to me. But I'm used to lambda as a term of art. The obscurity of lambda helps it avoid collisions (we have ways of unreserving keywords in property-name contexts, but these do not work for formal parameters and variables named define, which seem likelier at a guess than lambda -- spidering the web could help confirm this guess). The obscurity also arguably partners lambda better with function. Setting up define as a cleaner function seems to switch domains of discourse. Concretely, we have in ES3.1 Object.defineProperty and similarly named functions. These define APIs were prefigured by Object.prototype._defineGetter__, etc.. This sense of define has meant bind property name to value or getter/setter. On the other side, Python, E, etc. use def. But we would be verbose like Scheme and Dylan. So define vs. lambda. End of my bike-shedding ruminations. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss