Re: return when desugaring to closures

2008-10-21 Thread Brendan Eich
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

2008-10-21 Thread Waldemar Horwat
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

2008-10-21 Thread Mark S. Miller
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

2008-10-21 Thread David-Sarah Hopwood
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

2008-10-21 Thread Brendan Eich
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

2008-10-20 Thread Waldemar Horwat
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

2008-10-17 Thread Waldemar Horwat
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

2008-10-17 Thread Waldemar Horwat
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

2008-10-17 Thread Brendan Eich
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

2008-10-17 Thread Brendan Eich
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

2008-10-17 Thread Brendan Eich
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

2008-10-17 Thread Maciej Stachowiak

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

2008-10-17 Thread Eric Suen
 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

2008-10-17 Thread Brendan Eich
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

2008-10-16 Thread Waldemar Horwat
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

2008-10-16 Thread Waldemar Horwat
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

2008-10-16 Thread Mark S. Miller
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

2008-10-16 Thread Brendan Eich

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

2008-10-16 Thread Brendan Eich
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

2008-10-16 Thread Brendan Eich
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

2008-10-16 Thread David-Sarah Hopwood
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

2008-10-16 Thread Waldemar Horwat
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

2008-10-16 Thread Mark S. Miller
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

2008-10-16 Thread Maciej Stachowiak


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

2008-10-15 Thread Dave Herman
 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

2008-10-15 Thread Waldemar Horwat
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

2008-10-15 Thread liorean
 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

2008-10-15 Thread Brendan Eich

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

2008-10-15 Thread Brendan Eich


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

2008-10-14 Thread Neil Mix

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

2008-10-14 Thread David-Sarah Hopwood
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

2008-10-14 Thread Brendan Eich
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

2008-10-14 Thread Neil Mix
 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

2008-10-14 Thread P T Withington
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

2008-10-14 Thread Waldemar Horwat
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

2008-10-14 Thread Waldemar Horwat
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

2008-10-14 Thread David-Sarah Hopwood
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

2008-10-13 Thread David-Sarah Hopwood
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

2008-10-13 Thread Mark S. Miller
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

2008-10-13 Thread Jon Zeppieri
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

2008-10-13 Thread Mark Miller
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

2008-10-13 Thread Jon Zeppieri
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

2008-10-13 Thread David-Sarah Hopwood
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

2008-10-13 Thread David-Sarah Hopwood
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

2008-10-13 Thread Brendan Eich
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

2008-10-13 Thread Waldemar Horwat
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

2008-10-13 Thread Waldemar Horwat
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

2008-10-13 Thread Brendan Eich
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

2008-10-13 Thread Brendan Eich
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

2008-10-13 Thread Waldemar Horwat
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

2008-10-13 Thread Brendan Eich
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

2008-10-13 Thread Jon Zeppieri
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

2008-10-13 Thread Waldemar Horwat
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

2008-10-13 Thread Brendan Eich
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

2008-10-13 Thread David-Sarah Hopwood
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

2008-10-13 Thread Waldemar Horwat
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

2008-10-13 Thread Jon Zeppieri
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

2008-10-13 Thread Brendan Eich
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

2008-10-13 Thread Waldemar Horwat
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

2008-10-13 Thread Brendan Eich
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

2008-10-13 Thread Waldemar Horwat
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

2008-10-13 Thread Brendan Eich
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

2008-10-13 Thread Waldemar Horwat
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

2008-10-13 Thread Brendan Eich
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

2008-10-13 Thread David-Sarah Hopwood
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

2008-10-13 Thread David-Sarah Hopwood
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

2008-10-12 Thread David-Sarah Hopwood
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

2008-10-12 Thread Yuh-Ruey Chen
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

2008-10-12 Thread Mark S. Miller
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

2008-10-12 Thread David-Sarah Hopwood
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

2008-10-12 Thread David-Sarah Hopwood
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

2008-10-12 Thread YR Chen
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

2008-10-12 Thread Mark S. Miller
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

2008-10-12 Thread David-Sarah Hopwood
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

2008-10-12 Thread Mark S. Miller
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

2008-10-11 Thread David Herman
   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

2008-10-11 Thread Peter Michaux
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

2008-10-11 Thread David-Sarah Hopwood
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

2008-10-11 Thread Peter Michaux
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

2008-10-11 Thread David Herman
 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

2008-10-11 Thread David Herman
 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

2008-10-11 Thread Mark S. Miller
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

2008-10-11 Thread Peter Michaux
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

2008-10-11 Thread Brendan Eich
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

2008-10-11 Thread Brendan Eich

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

2008-10-11 Thread Brendan Eich
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

2008-10-11 Thread Mark S. Miller
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

2008-10-11 Thread Brendan Eich
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

2008-10-11 Thread Mark S. Miller
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

2008-10-11 Thread Mark S. Miller
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

2008-10-10 Thread Brendan Eich
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

2008-10-10 Thread David-Sarah Hopwood
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

2008-10-10 Thread Brendan Eich
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

2008-10-10 Thread Brendan Eich
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

2008-10-10 Thread Yuh-Ruey Chen
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

2008-10-09 Thread Brendan Eich
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

2008-10-09 Thread Lex Spoon
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

2008-10-09 Thread Brendan Eich
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

2008-10-09 Thread Brendan Eich
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

2008-10-09 Thread David Herman
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


  1   2   >