Re: return when desugaring to closures
if (h == 0) h = function() {break}; Did you mean if (x == 0)? That's been confusing me in trying to read your example. Dave ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sat, Oct 11, 2008 at 10:02 AM, Mark S. Miller [EMAIL PROTECTED] wrote: On Sat, Oct 11, 2008 at 9:11 AM, Peter Michaux [EMAIL PROTECTED] wrote: As it stands, I always write the following in ES3 var f = function() {}; and now that arguments.callee is on the chopping block, I've started writing recursion as the painful contortion below var f = (function() { var callee = function() { callee(); }; return callee; })(); I don't get it. Why not write var f = function() { f(); }; var f = function() {f();}; var g = f; f = function(){alert('broke your recursion :)');}; g(); The call to g will not create an infinite loop which is the intended behavior of the function object defined in the first line. The late binding of the recursive call to whatever f happens to reference is the problem. Peter ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Strengthening Function.prototype.toString
Inline. Yuh-Ruey Chen wrote: So as far as I can tell, what we need to discourage usage of func.toString() is: 1) An API for function currying/partial evaluation (specializing/binding certain arguments). 2) A read-only property on functions that contains the list of parameter names. Both points are valid. But #2 is of little utility for variadic functions. Example: function foo(){ return arguments[5]; }; As you can see it is generally impossible to know how many arguments are actually expected, and what are their names. Personally I am not against a read-only property for argument names, just pointing out its limited utility. For any other purpose, I would think that you might as well parse the whole source file rather than just the function, since you would need to keep track of closures. For example, consider this: function foo() { var x = 10; return function() { return x; }; } function bar(f) { var x = 20; print(f()); print(eval(f.toString())()); } bar(foo()); That would print 10, then 20. So as you can see, even in the same scope, eval(f.toString())(...) is not necessarily equal to f(), even if f.toString() is a correct decompilation of f. That's a good point about literal comparison of bodies. The other side of this argument is: it is generally impossible to read the function body, and compile it back with eval() expecting to get the same functional behavior because it this point we have no idea about closures. To accomplish that we need a way to manipulate closures/context: extract it from a function somehow, specify it as a parameter to eval() and so on. I am not sure it is doable/practical. Thanks, Eugene ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Peter Michaux wrote: On Sat, Oct 11, 2008 at 10:02 AM, Mark S. Miller [EMAIL PROTECTED] wrote: On Sat, Oct 11, 2008 at 9:11 AM, Peter Michaux [EMAIL PROTECTED] wrote: As it stands, I always write the following in ES3 var f = function() {}; and now that arguments.callee is on the chopping block, I've started writing recursion as the painful contortion below var f = (function() { var callee = function() { callee(); }; return callee; })(); I don't get it. Why not write var f = function() { f(); }; var f = function() {f();}; var g = f; f = function(){alert('broke your recursion :)');}; g(); /*const*/ var f = function() { f(); }; Then just treat /*const*/ variables as if they were 'const', until at some point you no longer care about old versions of JScript and can globally replace '/*const*/ var' - 'const'. -- David-Sarah Hopwood ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Revenge of the double-curly [Was: return when desugaring to closures]
On Oct 11, 2008, at 7:25 AM, David-Sarah Hopwood wrote: It is correct to say, though, that: function foo() { ... { var bar = baz; } ... } is equivalent to function foo() { let bar = undefined; ... { bar = baz; } ... } That is, 'var' need not be primitive. Only 'let' needs to be primitive. Indeed. Similarly, if we are careful in specifying 'lambda' then 'function' need not be primitive, since it will be expressible as a rewrite to 'lambda'. Yes, this is called out already in http://wiki.ecmascript.org/doku.php?id=strawman:lambdas /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sat, Oct 11, 2008 at 11:59 AM, Brendan Eich [EMAIL PROTECTED] wrote: On Oct 11, 2008, at 9:05 AM, Peter Michaux wrote: How to define a variable that is local to the enclosing lambda? Isn't the ability to do that essential? Use let (the var replacement declaration form). Sounds good to me but it is a little confusing to keep track if let is either in or out of ES-Harmony and if it is partly in then which of the several JavaScript 1.7 uses are in and if there will be let, let*, letrec semantics. Peter ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Also, I wonder why lambda in it's block-less form is restricted to expressions. I'm with you... but I'd want to check with the experts on the ES grammar to see whether this introduces any nasty ambiguities. Dave ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Sounds good to me but it is a little confusing to keep track if let is either in or out of ES-Harmony and if it is partly in then which of the several JavaScript 1.7 uses are in and if there will be let, let*, letrec semantics. I've got no crystal ball, but I'd say it'd be unlikely (and terribly silly) that we'd have `lambda' without having `let'. (The `lambda' form would fail in its requirement as an equivalence-preserving primitive if it became a target of `var'-hoisting.) Dave ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sat, Oct 11, 2008 at 12:52 PM, Peter Michaux [EMAIL PROTECTED] wrote: Use let (the var replacement declaration form). Sounds good to me but it is a little confusing to keep track if let is either in or out of ES-Harmony and if it is partly in then which of the several JavaScript 1.7 uses are in and if there will be let, let*, letrec semantics. The let declaration is in. Like const and function declarations, it has block-level letrec lexical scoping. I continue to oppose let expressions and let statements, as they don't provide enough additional power to justify their cost. For example, if lambda supports optional args, as I think we all agree it should, then, adapting a suggestion of Lars, we should define the scope in which the default value expressions are evaluated so that var x = 2; (lambda (x = 3, y = x) (x+y))() returns 5, not 6. This gives us an easy enough let. The builtin let declarations provide an easy letrec. Given these, no one will miss let*. -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sat, Oct 11, 2008 at 1:21 PM, Mark S. Miller [EMAIL PROTECTED] wrote: The let declaration is in. Like const and function declarations, it has block-level letrec lexical scoping. I continue to oppose let expressions and let statements, as they don't provide enough additional power to justify their cost. For example, if lambda supports optional args, as I think we all agree it should, then, adapting a suggestion of Lars, we should define the scope in which the default value expressions are evaluated so that var x = 2; (lambda (x = 3, y = x) (x+y))() Simplifying (lambda (x = 3, y = x) (x+y))() to just let (x = 3, y = x) (x+y) makes it much more clear when reading code what the intention of the programmer was. The reader doesn't need to interpret the code with as many steps and doesn't need to check at the end of the expression to see if the lambda is immediately called or not. This checking is an issue when an immediately called lambda spans more than a screen. Repeatedly writing the whole immediately-called, anonymous function is boilerplate for such a common pattern. I admit it is not much character boiler plate but it is the fact that the pattern is so common that I believe the concept is worth abstracting. It is the mental boilerplate that is the waste. Peter ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 11, 2008, at 12:55 PM, David Herman wrote: How to define a variable that is local to the enclosing lambda? Isn't the ability to do that essential? No. With all due respect to Brendan, `var' hoisting to the top of a function body is one of the more problematic aspects of ES's semantics. I agree, it's no skin off my nose -- 'var' hoisting was an artifact of function implementation in Netscape 2, and did not apply to global vars then. It was standardized as hoisting in all kinds of code (global, function, and eval). We are stuck with it. However, hoisting still applies to let: If you want a local variable, use `let' -- it'll be local to its containing block. If you want a variable that is local to the entire body of a `lambda', use `let' at the top level of the `lambda' body. While let is local to containing block, the let-as-new-var proposal (implemented in Firefox 2 and up) hoists to top of block. So you cannot initialize the inner x using the outer x's value: { let x = 42; { let x = x; // undefined, not 42. alert(x); } alert(x); // 42, of course } We've discussed making use-before-set a strict error, but we've avoided it. The initialiser is not mandatory, and we do not wish to impose costly analysis on small implementations. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 11, 2008, at 12:52 PM, Peter Michaux wrote: On Sat, Oct 11, 2008 at 11:59 AM, Brendan Eich [EMAIL PROTECTED] wrote: On Oct 11, 2008, at 9:05 AM, Peter Michaux wrote: How to define a variable that is local to the enclosing lambda? Isn't the ability to do that essential? Use let (the var replacement declaration form). Sounds good to me but it is a little confusing to keep track if let is either in or out of ES-Harmony I do not see why you are confused. I wrote, in the original ECMAScript Harmony post: I heard good agreement on low-hanging de-facto standard fruit, particularly let as the new var, to match block-scoped const as still proposed (IIRC) in 3.1. See https://mail.mozilla.org/pipermail/es-discuss/2008-August/006837.html . and if it is partly in then which of the several JavaScript 1.7 uses are in and if there will be let, let*, letrec semantics. It's something else. See my reply about hoisting, just sent. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 11, 2008, at 2:43 PM, Brendan Eich wrote: On Oct 11, 2008, at 12:52 PM, Peter Michaux wrote: and if it is partly in then which of the several JavaScript 1.7 uses are in and if there will be let, let*, letrec semantics. It's something else. See my reply about hoisting, just sent. Mark and others have described the let declaration (let as new var) as like letrec. Close, but since forward references are possible and initiialization in the declaration is not mandatory, it's analogous, not identical. The function definition form in JS is letrec. But let as new var? I still say it's something else; I admit that like letrec or letrec-ilke works. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
Hi Dave, first, my compliments on your lambda proposal. This should significantly simplify the core language after expanding sugars -- especially if you succeed at redefining function as desugaring to lambdas. That would be awesome! On Sat, Oct 11, 2008 at 5:34 AM, David Herman [EMAIL PROTECTED] wrote: Here's roughly the semantics of return-to-label: - return-to-label first checks to see if the label is live on the stack - if not, it raises an exception from the point where the return was attempted - but if so, it attempts to unwind the stack to the point where the label was executed, passing through any intervening finally clauses - if any of the finally clauses has its own non-local exit, this interrupts and aborts the unwinding Thank you for pointing out, though, that try/catch isn't so easily defined on top of return-to-label, since it still needs special handling for finally. The options are either to define a lower-level primitive underlying try/finally (akin to Scheme's dynamic-wind), or to leave exceptions -- or at least try/finally -- as primitive. I lean towards the latter; dynamic-wind is a subtle beast. [For those interested in dynamic-wind, Flatt et al's ICFP 07 paper has a nice investigation into its semantics: http://www.cs.utah.edu/plt/publications/icfp07-fyff.pdf] I haven't looked at the Flatt paper yet. But the semantics you explain above corresponds exactly to E's escape expressions. And E also treats try/finally primitive for similar reasons. (Historical note: The E escape construct and the call/ec previously explained both originate in an escape statement by Reynolds from a paper in the late 60s or early 70s. I don't recall whether Reynolds had any mechanism analogous to try/finally.) -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Oct 11, 2008, at 2:55 PM, Mark S. Miller wrote: On Sat, Oct 11, 2008 at 2:42 PM, Brendan Eich [EMAIL PROTECTED] wrote: We've discussed making use-before-set a strict error, but we've avoided it. The initialiser is not mandatory, and we do not wish to impose costly analysis on small implementations. Since const use-before-set is an unconditional error (i.e., not dependent on strictness), implementations already have to pay for a read-barrier mechanism. Since we'd like to support type-annotation-constraints on initialized let variables, I think initialized let variables should have an unconditional read barrier as well. If using an uninitialized let binding is an error, then hoisting is pointless except to make the statements between start of block and the let declaration a dead zone for the binding name. This fits the ancient, weak but not entirely worthless post-hoc rationale for var hoisting (to avoid confusion among novice or inexperienced programmers by making many scopes, each implicitly opened by var), but it's not particularly useful. What's more, as discussed here and in TC39, repeated let declarations for the same binding name within the same block should be allowed. Anything else is user- and refactoring-hostile. So the non-initial let declarations for a given name in the same block would be ignored. This leaves efficiency as a concern. If implementations do not do the analysis required to catch use before set at compile time, then let as well as const pays the read barrier price. It's non-trivial. Today let (and var in the absence of eval) can be optimized to use a read in the VM, possibly even a load instruction -- no getter barrier required. Whatever the GC write barrier cost, reads dominate and this is a significant savings. On the other hand, computing SSA including dominance relations while parsing (in one pass) is possible so long as we do not add goto to the language. So maybe the analysis is acceptable to modern implementations, which are increasingly sophisticated. Still, it is not yet agreed that let use before set shall be an error. It certainly is not the case for var, and working code counts on the undefined default initial value. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sat, Oct 11, 2008 at 3:26 PM, Brendan Eich [EMAIL PROTECTED] wrote: Of course, let expressions would need lambda-coding no matter what names were shadowed. The experience gained in JS1.7+ shows more let block usage than let expression, but expression temporaries (lacking macros and ignoring automatically generated code) are uncommon in today's JS. The function-expression-immediately-applied cliché usually has statements for effect, if not a return -- not a single expression in its body. Here's an odd idea. (I'm not yet advocating this; just brainstorming) What if the body of a lambda were always a block (curlies enclosing a statement list), but, by analogy to eval, if the last statement executed is an expression statement, then invoking the lambda returns the value of that last expression statement. Let's say, further, that a lambda's parameter list is syntactically optional. Together, this would result in lambda{ } being a first-class control-flow block as in Smalltalk. Then, if we did want let-blocks that desugar to lambda, with no additional complexity, a let block could be an expression even though its body had statements. One construct would serve both as let expressions and let blocks, and would for free turn JavaScript into an expression language. 3 + let{ while(...){...}; 4; } would either not terminate or evaluate to 7. None of this would violate TC. -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: return when desugaring to closures
On Sat, Oct 11, 2008 at 4:05 PM, Dave Herman [EMAIL PROTECTED] wrote: Read the proposal again: the statement form of lambdas *does* return the value of its last expression; this is what ES3 calls the completion value. Cool! So why are we still discussing proposed let expressions and let blocks as distinct constructs? -- Cheers, --MarkM ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss