Expression closures - use-cases for shortcut lambda syntax (blocks)

# Vassily Gavrilyak (17 years ago)

I like the expression closures were accepted into the spec, they will nicely cover many use-cases for iterating, sorting, etc. using closures. And I was the one who suggested Brendan on his blog to bring the issue of shortcut function syntax. Actually I proposed a more simpler solution, just using lambda-like syntax from Haskell.

However I didn't provide more use-cases where it can be useful and allows to not add support for such use-cases with some special language construct.

In short - look how Ruby uses blocks.

So, just a few from the top

  1. Automatic resource deallocation. using(File.open("test"), function(file){ //do something with file here.. }); //auto-close of file here.

2.Transactions (mixed with resource usage) for frameworks like Phobos (Phobos on Rails? :-) ) connection.transaction( function( tx){ using(connection.prepare("INSERT INTO ..."), function( statement){ using(statement, function(recordset){ processRecordset(recordset); }) // recordset closed }); //statement closed })//commit-rollback depended on whether exception was thrown

  1. CPS-style asynchronous processing 3.1 - AJAX server.getEMails(function(emails){ eMails.forEach(function(email){ if(somethingInEmail(email)){ server.forwardEMail(function(response){ processForwarding(responce); }) } }) }) 3.2 animations (Flash or DHTM) Something like - move ball, when it hits the wall, explode can be clearly expressed with cps-style too.

  2. Some simplest idioms like 10.times do {} from Ruby. (10).times(function(){ print("Hi"); })

Now, all those "function" just doesn't look like functions, bat rather blocks. So word function is meaningless here and unnecessary complicates the code.

Maybe the best solution here is too introduce some special syntax for resource allocation and async programming. That what some languages do and ES is still not here. But I doubt that transaction support will ever be in language. And there probably many more usages for blocks.

In our team we currently using modified SpiderMonkey for JS and MTASC for AS with modified lexer (not parser) for support shortcut syntax. We use this syntax quite heavily and found it very useful and not a false economy. Modification is VERY simple (2 lines in both cases) - when lexer found "(" replace it with "function("

This way ES could almost for free have a feature that many languages (C# 3, Ruby, Smalltalk, Perl) already have and developers found this feature useful. Java seems going there too with closures proposal.

And yes, this is just syntax sugar and nothing new for ES that aren't already present. But developers will fail to recognize that ES actually have blocks just because of syntax. I am the example of such developer :-).

Hope it's probably not to late to address those issues.

, Vassily Gavrilyak

# Brendan Eich (17 years ago)

On Mar 15, 2007, at 5:37 PM, Vassily Gavrilyak wrote:

In our team we currently using modified SpiderMonkey for JS and
MTASC for AS with modified lexer (not parser) for support shortcut syntax. We use this syntax
quite heavily and found it very useful and not a false economy. Modification is VERY simple (2 lines in both cases) - when lexer
found "(" replace it with "function("

Don't you mean ( instead of (\ above?

I am warming up to this use of backslash. What do others think?

This way ES could almost for free have a feature that many
languages (C# 3, Ruby, Smalltalk, Perl) already have and developers found this feature useful. Java seems going there
too with closures proposal.

And yes, this is just syntax sugar and nothing new for ES that
aren't already present. But developers will fail to recognize that ES actually have blocks
just because of syntax. I am the example of such developer :-).

Hope it's probably not to late to address those issues.

Syntax matters, it's the user interface. It is not too late, but it
is late. We should discuss pros and cons here in a thread.

# Jon Zeppieri (17 years ago)

On 3/15/07, Brendan Eich <brendan at mozilla.org> wrote:

I am warming up to this use of backslash. What do others think?

+1

Syntax matters, it's the user interface. It is not too late, but it is late. We should discuss pros and cons here in a thread.

PLT Scheme allows λ for 'lambda'. JS could allow λ for 'function' and backslash as a poor-man's λ. I know it's lame to add two synonyms for function, but it's also lame to use backslash explicitly as a lambda-lookalike in a language that already accepts lambdas in identifier syntax.

# Brendan Eich (17 years ago)

On Mar 15, 2007, at 6:35 PM, Jon Zeppieri wrote:

On 3/15/07, Brendan Eich <brendan at mozilla.org> wrote:

I am warming up to this use of backslash. What do others think?

+1

Syntax matters, it's the user interface. It is not too late, but it is late. We should discuss pros and cons here in a thread.

PLT Scheme allows λ for 'lambda'. JS could allow λ for 'function'
and backslash as a poor-man's λ. I know it's lame to add two synonyms
for function, but it's also lame to use backslash explicitly as a lambda-lookalike in a language that already accepts lambdas in identifier syntax.

My Unicode implant is on back-order -- what code point is that? Any
clue on how to type it on my Macbook Pro? Thanks,

# Peter Hall (17 years ago)

I think, once I've figured out how to actually type "λ" on my UK keyboard (I actually don't know how to enter 0x03BB in my keyboard, I had to paste from charmap), I might as well have typed "function(".

Also, "λ" is a perfectly permissable character for an identifier. Wouldn't it cause problems to give it special meaning? (for Greeks especially)

Peter

# Jon Zeppieri (17 years ago)

On 3/15/07, Peter Hall <peter.hall at memorphic.com> wrote:

I think, once I've figured out how to actually type "λ" on my UK keyboard (I actually don't know how to enter 0x03BB in my keyboard, I had to paste from charmap), I might as well have typed "function(".

Right -- which is why I also like backslash.

Good programming editors, of course, allow you to bind characters to whatever keys you want. In DrScheme, on the Mac, λ is mapped to command-\ by default. Mail clients tend not to be good programming editors...

Also, "λ" is a perfectly permissable character for an identifier. Wouldn't it cause problems to give it special meaning? (for Greeks especially)

Good point. It could break some code.

# Peter Hall (17 years ago)

I am warming up to this use of backslash. What do others think?

+1

It seems to me that ES4 is rapidly using up every combination of easily typable non-identifier characters. Apart from reducing the choice of possible syntax for ES in the future (look at the type parameters discussion), I think a lot of developers who are comfortable with existing JS and AS versions will have a number of wtf moments when they see this stuff used for the first time. Does it really offer enough reward?

Peter

# John Cowan (17 years ago)

Peter Hall scripsit:

I think, once I've figured out how to actually type "?" on my UK keyboard (I actually don't know how to enter 0x03BB in my keyboard, I had to paste from charmap), I might as well have typed "function(".

Hence the desire to allow \ as a synonym.

Also, "?" is a perfectly permissable character for an identifier. Wouldn't it cause problems to give it special meaning? (for Greeks especially)

Not really, no more than it causes problems to give "if" special meaning.

# Dave Herman (17 years ago)

Also, DrScheme has a keybinding that allows you to type Ctrl-\ and it inserts a lambda, which might make a nice tie-in between the two different syntaxes.

# Neil Mix (17 years ago)

I am warming up to this use of backslash. What do others think?

I like it.

Although (prepare for gratuitous land grab), I have to say one of my
top coding typos with lambda-as-argument is failing to remember the
closing paren. It would be really nice if by syntactic sugar a
block following a function call was converted into a lambda parameter
at the end of the arguments list, so that:

ajax(url, \(response) {
	// do something
});

could become:

ajax(url) {
	var [response] = arguments;
	// do something
}

Even though my version has more characters due to the arguments
destructuring, I'd gladly trade that off for the enhanced readability.

ducks

# Brendan Eich (17 years ago)

On Mar 15, 2007, at 6:55 PM, Peter Hall wrote:

I think, once I've figured out how to actually type "λ" on my UK keyboard (I actually don't know how to enter 0x03BB in my keyboard, I had to paste from charmap), I might as well have typed "function(".

Or at least "" ;-).

Also, "λ" is a perfectly permissable character for an identifier. Wouldn't it cause problems to give it special meaning? (for Greeks especially)

Yes, you are right -- ES3 allows Unicode identifiers, so this would
not be a backward compatible change. It seems excessive anyway, if \ (x)x is acceptable for (lambda (x) x).

# liorean (17 years ago)

On 16/03/07, Jon Zeppieri <zeppieri at gmail.com> wrote:

On 3/15/07, Brendan Eich <brendan at mozilla.org> wrote:

My Unicode implant is on back-order -- what code point is that? Any clue on how to type it on my Macbook Pro? Thanks,

Code point: 0x03bb

There are at least three ways to enter the character on the Mac.

In addition to those, editors written specifically for editing ECMAScript 4 will also be able supply an easy method on inserting it via either text replacement (insert ( and it magics that into λ) or via some keypress combination.

One problem here is that text documents default to non-unicode code pages on Windows (no idea about other OSes), and servers don't usually serve javascript as utf-8 or utf-16.

# Brendan Eich (17 years ago)

On Mar 15, 2007, at 7:38 PM, John Cowan wrote:

Also, "?" is a perfectly permissable character for an identifier. Wouldn't it cause problems to give it special meaning? (for Greeks especially)

Not really, no more than it causes problems to give "if" special meaning.

js> o = {if: 42} [object Object] js> o.if

42 js> function if(x){return 1+x}

js> if(42) /* oops */; js> function::if(42)

43

The scenario to consider is: existing ES3 content uses the lambda
unichar as a single-character identifier of a function. Then λ(x) +x
looks like a call to the λ function, then an evaluation of a variable
x, then an add operator evaluation. If λ is short for function, then
the example sentence is ambiguous.

# Daniel C. Wang (17 years ago)

why not {(x,y) \ x + y } then it would be ajax(url, { (response)
// do something });

... you can then consider introducing

{ (x,y) | x % 2 == y}

which just constrains the body to be a predicated!

# Brendan Eich (17 years ago)

Ok, this is getting out of hand ;-).

Seriously, we are not going to inject any alien syntax. Vassily
proposed a shorthand in expression closures: \ for function. Let's
try to get back to that.

On Mar 16, 2007, at 1:49 AM, Daniel C. Wang wrote:

why not {(x,y) \ x + y } then it would be ajax(url, { (response)
// do something });

... you can then consider introducing

{ (x,y) | x % 2 == y}

That's ambiguous: , | % and == are all dyadic operators.

which just constrains the body to be a predicated!

This isn't a comprehension. Not sure why the syntax constrains the
expression to be boolean.

# P T Withington (17 years ago)

On 2007-03-16, at 04:58 EDT, Brendan Eich wrote:

Seriously, we are not going to inject any alien syntax. Vassily proposed a shorthand in expression closures: \ for function. Let's try to get back to that.

I don't see the value, except for the 'obfuscated Javascript
contest'. If you make that many closures in a day, why not bind c-\
to insert function in your editor? I would rather programs were
easy to read than easy to write, since I spend a lot more time doing
the former. [Another pet peeve: I wish ! were spelled not. It
is too easy to overlook.]

# Igor Bukanov (17 years ago)

On 16/03/07, Brendan Eich <brendan at mozilla.org> wrote:

Yes, you are right -- ES3 allows Unicode identifiers, so this would not be a backward compatible change. It seems excessive anyway, if
(x)x is acceptable for (lambda (x) x).

And if farther (args) expression would stand for function(args) { return expression; }

then JS would be close to beating ML and friends in shortness of defining lambdas.

, Igor

# Igor Bukanov (17 years ago)

On 16/03/07, P T Withington <ptw at pobox.com> wrote:

On 2007-03-16, at 04:58 EDT, Brendan Eich wrote:

Seriously, we are not going to inject any alien syntax. Vassily proposed a shorthand in expression closures: \ for function. Let's try to get back to that.

I don't see the value, except for the 'obfuscated Javascript contest'. If you make that many closures in a day, why not bind c-
to insert function in your editor? I would rather programs were easy to read than easy to write, since I spend a lot more time doing the former.

For me function(args) { .... } is just too verbose and makes code harder to read especially in the case of many short lambdas.

, Igor

# Lars T Hansen (17 years ago)

On 3/16/07, Igor Bukanov <igor at mir2.org> wrote:

On 16/03/07, Brendan Eich <brendan at mozilla.org> wrote:

Yes, you are right -- ES3 allows Unicode identifiers, so this would not be a backward compatible change. It seems excessive anyway, if
(x)x is acceptable for (lambda (x) x).

And if farther (args) expression would stand for function(args) { return expression; }

then JS would be close to beating ML and friends in shortness of defining lambdas.

Shortness isn't a reasonable metric by itself. (Beating ML and Haskell even less so.) The language has to balance compactness and clarity. And though ES has aspects of a functional language (and ES4 will be even more so than ES3), it is more fundamentally imperative. No doubt many uses will be found for the expression function syntax, but going to this level of terseness does not seem to be justified. As Tucker said, programs are read much more often than they're written. "function" provides all readers with a reasonable idea about what follows. "" is overly terse, and makes sense only if you think "lambda" would make sense in the same context (I don't, particularly -- it's bad UI, to use Brendan's terminology).

# Jeff Dyer (17 years ago)

Just to recap

Vassily wrote:

  1. Some simplest idioms like 10.times do {} from Ruby. (10).times(function(){ print("Hi"); })

Now, all those "function" just doesn't look like functions, bat rather blocks. So word function is meaningless here and unnecessary complicates the

code.

Brendan wrote:

Syntax matters, it's the user interface. It is not too late, but it is late. We should discuss pros and cons here in a thread.

Graydon wrote:

A little haskell-loving part of me likes this (the backslash, not the lambda), but I also don't think it reads very well. Actually, no: I think "function(x) x" reads poorly, and this makes it either slightly better or slightly worse depending on your skill level. New users will balk, experienced users will like it.

Brendan wrote:

Seriously, we are not going to inject any alien syntax. Vassily proposed a shorthand in expression closures: \ for function. Let's try to get back to that.

Lars wrote:

I think "function" is significantly more self-explanatory to everyone not coming from the functional programming camp, and probably even to some of those (I include myself). Anyone from the C/C++/Java/C#(?) camp will read "" as meaning "the following character has an unusual meaning", which is not right here. So you're overloading "". This is not the end of the world, but a consideration.

The committee has already decided not to use "fun" as a shorter keyword for "function" because the savings were not worth the cognitive load. I think the same argument applies for "".

In fact, the experience I've had with using the expression-function form in the standard libraries suggests that it is really only useful for fairly trivial stuff -- a couple of lines of code at most. Beyond that you'll be much happier going to a multi-statement form, because the expression language is so limited -- it's not like Scheme, say, where everything is an expression. So I would suggest that we not overestimate the value of the expression-function form. (And once you go beyond a couple of lines, the extra "{", "}", and "return" make little difference in the amount you have to type and read, and generally make things more readable.)

P. T. Withington wrote:

I would rather programs were easy to read than easy to write, since I spend a lot more time doing the former.

Igor wrote:

For me function(args) { .... } is just too verbose and makes code harder to read especially in the case of many short lambdas.

Lars wrote:

Shortness isn't a reasonable metric by itself. (Beating ML and Haskell even less so.) The language has to balance compactness and clarity. And though ES has aspects of a functional language (and ES4 will be even more so than ES3), it is more fundamentally imperative. No doubt many uses will be found for the expression function syntax, but going to this level of terseness does not seem to be justified. As Tucker said, programs are read much more often than they're written. "function" provides all readers with a reasonable idea about what follows. "" is overly terse, and makes sense only if you think "lambda" would make sense in the same context (I don't, particularly -- it's bad UI, to use Brendan's terminology).

Dave wrote:

FWIW, I think I agree with Tucker and Lars. I don't see much value in introducing single-token synonyms. If it's a writeability issue,

mumble

mumble keyboard shortcuts and IDE's mumble mumble.

Re: readability, I just don't see the 5 extra characters as that huge

a

hardship. In a perfect world maybe the operator would have been "fun", but I think programs can survive a few gratuitous "-ction"s.

Remember (Igor) that ES4 introduces a shorter form of function expression which avoids the need for '{', 'return', and '}' -- not a small step forward in saving characters, and in expressing more naturally the idea of functions as expressions. As Brendan and Dave remind us, what is being proposed is to have a shorter spelling for 'function' in this case. As Lars and Dave point out, the working group has tried to save shave some of those eight characters to no avail.

So here is where we stand:

The primary arguments for the proposal are based on ease of typing and aesthetics for some (expert?) programmers. The primary arguments against are based on ease of reading and aesthetics for some (non-expert?) programmers. Lars also points out that the use of '' in this way is lexically inconsistent with the rest of the language and other C like languages where it means "escape the next character". This contributes to the readability problem for users with less sophisticated mental grammars.

The way forward is to identify who are the users at the interface we are designing? If they are primarily the sophisticated kind who read and write lambda expressions without missing a character, then I say the proposed feature pays for itself. If not then I say the cost to readability is too high for too many users and the proposal should be rejected.

Does anyone want to argue that most of our users are of the sophisticated kind?

# Steven Johnson (17 years ago)

I don't see the value, except for the 'obfuscated Javascript contest'. If you make that many closures in a day, why not bind c-
to insert function in your editor? I would rather programs were easy to read than easy to write, since I spend a lot more time doing the former. [Another pet peeve: I wish ! were spelled not. It is too easy to overlook.]

+1 on the -considered-harmful position.

If the desire is to save keystrokes, then we're optimizing for the wrong prolem.

To be sure, terseness is a desirable quality in code, but never at the expense of clarity.

From my position (C/C++/Java-ish background), the terseness is far

outweighed by the sheer "wtf?" factor; I look at the code and try to figure out why ( is being escaped by . The sorta-looks-like-a-lambda connection is (IMHO) gonna be very tenuous to the typical JS programmer.

I find this construct to be extraordinarily hard to read, and it was legal, would probably try to institute a coding convention on my projects that its use should be avoided.

# John Cowan (17 years ago)

Steven Johnson scripsit:

To be sure, terseness is a desirable quality in code, but never at the expense of clarity.

Clarity, in this case, is nothing but familiarity.

# Vassily Gavrilyak (17 years ago)

Brendan: No, I didn't meant (, I meant exactly (
It looks like lambda, reads nicely and works good with current lexers (at least for our code base with SpiderMonkey and MTASC) All we did in jsscan.c is case '(': if (MatchChar(ts, '\')) { UngetChar(ts,'('); tt = TOK_FUNCTION; tp->t_op=JSOP_NOP; } else { tt = TOK_LP }

Next about readability. My primary concern IS exactly readability, I never mind typing additional characters. But I want to clearly show the intent in my code. I want to see word 'function' when it means exactly function. And I want to read 'block' when it means block. so function someFunction(){ // this is a FUNCTION using(File("hello". "r"), (\ file){ // this is a block that uses file. process(file); }) // file will be closed here. } We would be probably happier with clearer syntax (without \ and some parenteses) but such "feature" can break old code and introduce corner cases.

For me even using word 'block' will be good, but why we need more keywords if we can do without them.

About C/C++/Java background. Well, everybody in our team has exactly this background. We dumped all those 3 'bigs' in favor of JS and never looked back since.

And everybody accepted the proposal to hack the lexers only to show the clear intent in the code, so everybody will understand what's going on. And we did it cause we have a lot of above provided use-cases in our code (web and game development). That was hard decision, cause nobody want to maintain foreign code base and apply diffs with every version. But we did it and now we are using Spidermonkeys eval/uneval trick just to make legacy browsers be happy with our code. That's the only 'fork' we did, cause JS is just 'right' for us. We do not want operators, overloading, multiply inheritance and any other 'cool' staff (And that's with C++ background :-) ). But we need to write async code, use non-GC resources and do transactions in database all the time. Java-way is too complex and error prone here (from our past experience).

About project policy. Adobe project policy probably should disallow lambda and use OO. Our project policy disallowed AS2 OO-extensions and favored lambda. People are just different and projects are different and that's ok. Language should provide mechanism, not policy. That's why JS won for us.

Now, not everybody has C/C++ background. Ruby and Perl developers will be just HAPPY with block syntax. Ruby is success because of Rails. Please look at Rails examples and count number of usage of blocks here. Lots of Java/C# people escaped to Ruby (sadly, not to JS) and are just happy. There are rumors that all ex-Perl hackers are becoming JS-hackers right now and found a good language. C# coders will follow shortly. They have using(resource) {} now and will have blocks in 3.0. Scala have only one behavior pattern - 'loan', that looks exactly like first thow use-cases for resource and transaction management.

Maybe we can emulate some other language here, e.g Ruby with '|' instead of . For us Haskell's lambda is nice enough.

To conclude: -Proposal is about clarity, not terseness, please do not accept it as 'just to save 6 keystrokes' -Not everybody has C/C++/Java background. Lot of people have Perl, Ruby, PHP background. -Blocks with proposed syntax are simple to implement and do not break current code. -Provided use-cases are widespread in web-programming and are currently hard to write and read clearly in ES3. Blocks can fix this.

Thanks for all interested , Vassily Gavrilyak

# Vassily Gavrilyak (17 years ago)

Jeff, concerning who are 'sophisticated' users.

Ruby and Smalltalk and Perl users found nothing sophisticated with blocks. And most of those users are web-developers, so they will need to deal with ES anyway. They are even considered less sophisticated then 'real' Java/C++ developers, cause of scripting nature.

About current ES user base. Is seams that most JS libs use (function(){}) trick for namespaces and function callbacks for AJAX calls. People are already using the closures and carefully counting the ()s. Well, actually text editors count them :-). But that's about writing. When reading I do not look at the separators, just the words and indentation, and ocasional meaningless 'function' keyword in 'wrong' just breaks the words reading process.

Syntax could be much clearer and I (and most scripters) would be very happy to have clear blocks like in Ruby.

File.open("file.txt") do | file file.process() end

or Groovy's

File.open("file"){ file-> //process } Or C# 3 C# 3.0 Specification File.open("file", (file))=>{ //process });

But first is not-ES and second is broken with auto semicolon insertion. May be C# way will be good, and C# is ECMA too. It has the same superfluous () as ES3, but looks good. It seems harder to implement and probably can break some syntax, but C# user base is huge. Unfortunately I have no better ideas of clear syntax. Seems any nice idea will break something in current syntax. Hope it will be born is this discussion.

Vassily Gavrilyak

# Brendan Eich (17 years ago)

On Mar 16, 2007, at 2:17 PM, Vassily Gavrilyak wrote:

Brendan: No, I didn't meant (, I meant exactly (\

Sorry, I mis-remembered your blog post (http:// weblogs.mozillazine.org/roadmap/archives/2006/05/ javascript_2_ecmascript_editio.html#comments). Indeed you have always
written (\ formals ) expression -- but now I'm cooling, not warming,
to the (\ proposal.

We dumped all those 3 'bigs' in favor of JS and never looked
back since. And everybody accepted the proposal to hack the lexers only to show
the clear intent in the code, so everybody will understand what's
going on.

I agree with an earlier poster that clarity depends on one's
background, therefore familiarity can make idioms clear.

Digression: sometimes "idiom" is abused to mean "cliché", but the
latter is just a common phrase or construct, whose meaning may or may
not be clear from denotation and connotation of component words or
symbols. "Idiom" is something like the English phrase "kick the
bucket" (meaning to die), whose definition cannot be deduced from its
components. With your proposed lambda syntax, (\ formals )
expression, we definitely have another idiom for JS hackers to learn.

This idiom would not re-use brain-print from any other popular
language. So familiarity would have to be acquired at some cost. That
cost may be no lower or higher than the cost of learning expression
closures as proposed (function (formals) expression), but it seems
higher to me given the oddness of (.

To conclude: -Proposal is about clarity, not terseness, please do not accept it
as 'just to save 6 keystrokes'

Seven keystrokes, by my count.

-Not everybody has C/C++/Java background. Lot of people have Perl,
Ruby, PHP background.

But (\ is unclear to users of those languages, it is a new idiom.

-Blocks with proposed syntax are simple to implement and do not
break current code.

True, but same goes for expression closures.

-Provided use-cases are widespread in web-programming and are
currently hard to write and read clearly in ES3. Blocks can fix this.

"Hard to write" must mean those seven extra keystrokes. I'm
sympathetic to a point, but readability trumps writability.

"Hard to read" must mean hard on account of the repeated "function"
word, which becomes noise to signal at scale. I'm sympathetic to
this, I often comment on it (e.g. when I show the Y combinator in JS
in presentations such as the one I gave at ICFP 2005).

However, (\ is really an abuse of backslash, as others have noted.

It looks less like a lambda every time I see it (subjective, but I'm
just giving my opinion).

I agree that there's no point in adding a "block" keyword.

So without an idiom from another language, I'm cool again. Sorry, but
feel free to propose a better syntax. We are not out of room in the
grammar yet!

# Andrew Dupont (17 years ago)

So without an idiom from another language, I'm cool again. Sorry, but
feel free to propose a better syntax. We are not out of room in the
grammar yet!

How about a slightly-modified version of Ruby's block syntax?

(10).times( |number| { console.log(number); });

The pipe character delimits the arguments, with the only difference being that the arguments occur outside of the braces. In Ruby this would be

10.times { |number| puts number }

# Igor Bukanov (17 years ago)

On 16/03/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote:

Next about readability. My primary concern IS exactly readability, I never mind typing additional characters. But I want to clearly show the intent in my code. I want to see word 'function' when it means exactly function. And I want to read 'block' when it means block. so function someFunction(){ // this is a FUNCTION using(File("hello". "r"), (\ file){ // this is a block that uses file. process(file); }) // file will be closed here. }

You do not need function closure here in ES4, use generators instead (that already works in SpiderMonkey since Firefox 2.0):

for (file in autoFile("hello", "r")) { process(file); }

where autoFile is a generator:

function autoFile(name, mode) { File f = new File(name, mode) try { yield f; } finally { f.close(); } }

That is, using idiom from C# is already available in JS with generators so it can not be used as an argument for () expression.

, Igor

# Vassily Gavrilyak (17 years ago)

I agree that (\ is not familiar for Perl or Ruby developers. It's Haskell's idiom and the combination (not a slash) just looks like lambda, that's why we choose it. Maybe it looks for good me because cyrillic analog of letter lambda looks exactly this way :-) (\ x -> 3*x + x/4) [1,2,3,4].

Parenthesis are not mandatory in Haskell and I think that's why you thought about slash before and not after. But I agree that it is new for JS users. What's left from other languages | | from Ruby -> or => from ML, Haskell again, haXe, Groovy and C#.

| | loolks better for me and I like Andrew's proposal. It clears the parenthesis and seems to work nicely with current syntax and functions expressions () too. The only thing worried me - without arguments it will be exactly the || operator.

-Blocks with proposed syntax are simple to implement and do not break current code. True, but same goes for expression closures.

Expressions closures are genius idea and I like it very much. It just covers different use cases.

Vassily Gavrilyak

# Vassily Gavrilyak (17 years ago)

Well, I agree, it is available such way too. And it is avalivaible with try {} finally{} from long time ago. It's just the same issue of readability. The question - what is for doing here, where is the loop immediately come to mind when reading such construct. The same for try - what this code is supposed to catch here? 20 lines below

  • ah, nothing, that was for closing resource. We can even do this with if, goto, while, switch anything. But there are better ways :-)

Vassily Gavrilyak

# Igor Bukanov (17 years ago)

On 17/03/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote:

File.open("file.txt") do | file file.process() end

and

Well, I agree, it is available such way too. And it is avalivaible with try {} finally{} from long time ago. It's just the same issue of readability. The question - what is for doing here, where is the loop immediately come to mind when reading such construct.

Ruby's do construction is also a loop, not a lambda, almost exactly corresponding to the generator example in JS, yet it was given as an example to support the need for short lambdas in JS.

I think the issue here is that the generators are not yet widely used in JS so the code like for (var f in autoFile("file.txt")) processFile(f); still looks strange.

But, compared with lambda proposal, that code is in fact shorter, much more clear IMO and can be optimized better.

For that reason I think examples like this can not be used to support the lambda shorthand. What can be used is a case like:

var filtered = array.filter((\ elem) elem > 0)

versus

var filtered = array.filter(function(elem) { return elem > 0; })

, Igor

# Brendan Eich (17 years ago)

On Mar 16, 2007, at 5:34 PM, Vassily Gavrilyak wrote:

Well, I agree, it is available such way too. And it is avalivaible with try {} finally{} from long time ago. It's just the same issue of readability. The question - what is for doing here, where is the loop
immediately come to mind when reading such construct. The same for try - what this code is supposed to catch here? 20
lines below - ah, nothing, that was for closing resource. We can even do this with if, goto, while, switch anything. But there are better ways :-)

There are duals competing in language design. You see this in Python,
where comprehensions and generators are favored over more functional
programming idioms (hence the crippled one-line lambda). Some
languages start with lambdas and grow sugar (comprehensions) and
iterable coroutines (generators). I'm hesitant to pick a winner, and
we have already said that we don't believe in minimizing JS/ES into a
shiny, Scheme-like jewel that requires users from beginner to
intermediate skill level to become too expert too soon.

We want conveniences, especially where they reflect existing practice
in JS and "nearby" languages (I argue Python is one such language).
We will always have first-class functions.

Given all this generalization, I would like to make a particular
suggestion: keep focusing on expression closure use-cases, and show
"A" vs. "B" examples with the different suggested syntaxes (all
suggestions). Pick use-cases that come from real code, Ruby or
whatever. Don't use miniature fakes. Then perhaps we can come to
agreement quickly.

# Yuh-Ruey Chen (17 years ago)

Igor Bukanov wrote:

You do not need function closure here in ES4, use generators instead (that already works in SpiderMonkey since Firefox 2.0):

for (file in autoFile("hello", "r")) { process(file); }

This is a bit of a side note, but this type of usage of generators can be misleading to novice programmers, so I'm not sure if it's any better. If I were new to the language, I would interpret this as some type of for loop that can process multiple files. The intent that you'd like the code to show is that process(file) is called only once and is cleaned up afterwards.

The Python guys have in fact talked a lot about this specific construct:

www.python.org/dev/peps/pep-0343, www.python.org/dev/peps/pep-0340 (rejected in favor of PEP 343) www.python.org/dev/peps/pep-0310 (rejected in favor of PEP 343)

  • Yuh-Ruey Chen
# Robert Sayre (17 years ago)

On 3/16/07, Brendan Eich <brendan at mozilla.org> wrote:

Pick use-cases that come from real code, Ruby or whatever. Don't use miniature fakes. Then perhaps we can come to agreement quickly.

Here are some examples from culled from the web for MochiKit's addLoadEvent function:

addLoadEvent(function () { var elems = getElementsByTagAndClassName("A", "view-source"); var page = "rounded_corners/"; for (var i = 0; i < elems.length; i++) { var elem = elems[i]; var href = elem.href.split(///).pop(); elem.target = "_blank"; elem.href = "../view-source/view-source.html#" + page + href; } });

addLoadEvent(function() { $("debug").innerHTML = repr(getElementPosition($("street"))); $("debug").innerHTML += "<br>"; $("debug").innerHTML += repr(quirksmode.findPos($("street"))); });

addLoadEvent(function(){ for(var _70 in DomDeco.registry){ for(var i=0;i<DomDeco.registry[_70].length;i++){ DomDeco.apply(_70,DomDeco.registry[_70][i]); } } });

addLoadEvent(function () { var d = wait(0.5, {data: [["getUsers", "getUsers"], ["foo", "bar"]]}); d.addCallback(showSelectData); d.addErrback(showError); });

Nested anonymous functions:

addLoadEvent(function(){ connect('pagelist','onclick', function (e) { e.preventDefault(); var d = loadJSONDoc("${std.url('/pagelist', tg_format='json')}"); d.addCallback(showPageList); }); });

addLoadEvent(function(){ connect('bad_example','onclick', function(e){signal('bad_example','showvalue',"explicit_value")}); connect('bad_example','showvalue',window,'alert');

connect('good_example','onclick',
    function(e){signal(randomObj,'showvalue',"explicit_value")});
connect(randomObj,'showvalue',window,'alert');

});

Seems to me that "function(){" is a little more painful when nested.

# Brad Fults (17 years ago)

I agree that the nested "function () { ... }" syntax gets messy (and this can be seen especially within complex event-driven scripts), but trying to define blocks as fundamentally different constructs is, in my opinion, only more confusing for the ES novice.

I am not inclined to support any of the alternate proposals so far; "(" because it is a radical departure from the meaning of '' in ES and is the first step on the path to Perl's magic $... madness, "File.open("file", (file))=>{ ... });" because it is terribly dense

and confusing syntax, and the Ruby-esque "(10).times( |number| { ... });" because it's yet another nonsensical (and unnecessary) idiom for the ES coder to learn and remember (though the least confusing of these three).

for (file in autoFile("hello", "r")) { process(file); }

This is a bit of a side note, but this type of usage of generators can be misleading to novice programmers, so I'm not sure if it's any better.

I completely agree -- this is confusing and not explicit in the least as to what it is actually doing.

I think it's also worth considering that the current (ES3) syntax allows the novice to mentally step through the process in a logical manner; e.g. "this is a lambda function that returns a reference to another lambda function which will inherit scope from the outer lambda and form a closure". Certainly in the use case of event-driven web scripts when preparing an event handler, it makes perfect sense that one would need to call a lambda to return a reference to a new lambda with a closure to be passed to the browser's event handling system. It is indirect and complex, but it is not obscure or magical.

I think the only sensible alternative if "typing seven characters" is a serious issue for the spec to handle is to offer a shortened function identifier that doesn't change terminology to "blocks" or the basic ES function syntax. Changing "function" to "fn" comes to mind. Of course this will be another trade-off with backwards compatibility: there will always be breakage of older scripts if a new identifier is introduced.

This would make one of the above examples:

(10).times( fn(number) { ... });

It's shorter, but I'm not convinced it's worth the negatives. You can bind "function(...) {...}" to a single key in any decent coding-centric text editor and mitigate the density of syntax with liberal use of newlines and indentation.

Thanks.

# Neil Mix (17 years ago)

On Mar 16, 2007, at 8:57 PM, Robert Sayre wrote:

Seems to me that "function(){" is a little more painful when nested.

Great examples -- you've changed my mind about the ( syntax. I
would argue that the painfulness is a result of densely packed
alternating/nested curlies and parens and has little to do with the
function keyword.

# Brendan Eich (17 years ago)

On Mar 16, 2007, at 10:38 PM, Neil Mix wrote:

On Mar 16, 2007, at 8:57 PM, Robert Sayre wrote:

Seems to me that "function(){" is a little more painful when nested.

Great examples -- you've changed my mind about the ( syntax. I would argue that the painfulness is a result of densely packed alternating/nested curlies and parens and has little to do with the function keyword.

Test that claim: show those Mochikit examples rewritten to use
expression closures as proposed for ES4.

# Neil Mix (17 years ago)

On Mar 17, 2007, at 12:44 AM, Brendan Eich wrote:

Test that claim: show those Mochikit examples rewritten to use expression closures as proposed for ES4.

Yes, of course. Here are Robert's examples using the ( syntax:

addLoadEvent(() { var elems = getElementsByTagAndClassName("A", "view-source"); var page = "rounded_corners/"; for (var i = 0; i < elems.length; i++) { var elem = elems[i]; var href = elem.href.split(///).pop(); elem.target = "_blank"; elem.href = "../view-source/view-source.html#" + page + href; } });

addLoadEvent(() { $("debug").innerHTML = repr(getElementPosition($("street"))); $("debug").innerHTML += "<br>"; $("debug").innerHTML += repr(quirksmode.findPos($("street"))); });

addLoadEvent((){ for(var _70 in DomDeco.registry){ for(var i=0;i<DomDeco.registry[_70].length;i++){ DomDeco.apply(_70,DomDeco.registry[_70][i]); } } });

addLoadEvent(() { var d = wait(0.5, {data: [["getUsers", "getUsers"], ["foo",
"bar"]]}); d.addCallback(showSelectData); d.addErrback(showError); });

Nested anonymous functions:

addLoadEvent((){ connect('pagelist','onclick', (e) { e.preventDefault(); var d = loadJSONDoc("${std.url('/pagelist', tg_format='json')}"); d.addCallback(showPageList); }); });

addLoadEvent((){ connect('bad_example','onclick', (e){signal('bad_example','showvalue',"explicit_value")}); connect('bad_example','showvalue',window,'alert');

 connect('good_example','onclick',
     \(e){signal(randomObj,'showvalue',"explicit_value")});
 connect(randomObj,'showvalue',window,'alert');

});

Now, to back up my assertion that the curlies and parens are
cluttering things up (and NOT to re-open my earlier proposal because
that door has already been shut, right?) here's the same code using
the block-following-call-becomes-lambda-arg syntax:

addLoadEvent() { var elems = getElementsByTagAndClassName("A", "view-source"); var page = "rounded_corners/"; for (var i = 0; i < elems.length; i++) { var elem = elems[i]; var href = elem.href.split(///).pop(); elem.target = "_blank"; elem.href = "../view-source/view-source.html#" + page + href; } }

addLoadEvent() { $("debug").innerHTML = repr(getElementPosition($("street"))); $("debug").innerHTML += "<br>"; $("debug").innerHTML += repr(quirksmode.findPos($("street"))); }

addLoadEvent() { for(var _70 in DomDeco.registry){ for(var i=0;i<DomDeco.registry[_70].length;i++){ DomDeco.apply(_70,DomDeco.registry[_70][i]); } } }

addLoadEvent() { var d = wait(0.5, {data: [["getUsers", "getUsers"], ["foo",
"bar"]]}); d.addCallback(showSelectData); d.addErrback(showError); }

Nested anonymous functions:

addLoadEvent() { connect('pagelist','onclick') { e.preventDefault(); var d = loadJSONDoc("${std.url('/pagelist', tg_format='json')}"); d.addCallback(showPageList); } }

addLoadEvent() { connect('bad_example','onclick') { var [e] = arguments; signal('bad_example','showvalue',"explicit_value") } connect('bad_example','showvalue',window,'alert');

 connect('good_example','onclick') {
     var [e] = arguments;
     signal(randomObj,'showvalue',"explicit_value")
 }
 connect(randomObj,'showvalue',window,'alert');

}

I argue that both are confusing at first glance, but the latter is
more legible. (Again, not as a suggestion that it be adopted, but as
proof of my assertion that many dense curlies/parens are the
legibility problem rather than the function keyword.)

# Robert Sayre (17 years ago)

On 3/17/07, Neil Mix <nmix at pandora.com> wrote:

Again, not as a suggestion that it be adopted, but as proof of my assertion that many dense curlies/parens are the legibility problem rather than the function keyword.

Yes, I agree. Your examples actually drive me away from ''.

addLoadEvent((){

It looks like the sound of a skipping CD. :/

# Jeff Walden (17 years ago)

Neil Mix wrote:

addLoadEvent() { var elems = getElementsByTagAndClassName("A", "view-source"); var page = "rounded_corners/"; for (var i = 0; i < elems.length; i++) { var elem = elems[i]; var href = elem.href.split(///).pop(); elem.target = "_blank"; elem.href = "../view-source/view-source.html#" + page + href; } }

This syntax is whitespace-significant; the presence or absence of a line terminator between the closing parenthesis after the function name and the opening brace affects behavior. If a line terminator occurs, semicolon insertion transforms it into a method call of no arguments and a block statement. If a line terminator doesn't occur, your behavior works (in an ES4 implementation, that is). The existing no-line-terminator uses are annoying enough, particularly in the case when I want to return an object from a method, and I'd rather see as few new instances (which don't mimic the existing ones) as possible, personally.

# Jeff Walden (17 years ago)

I skimmed the thread too quickly to notice the note before the example I quoted in the last email saying it was dead syntax -- apologies for the extra email.

# Jeff Walden (17 years ago)

I skimmed the thread too quickly to notice the note before the example I quoted in the last email saying it was dead syntax -- apologies for the extra email.

# Yuh-Ruey Chen (17 years ago)

Jeff Walden wrote:

Neil Mix wrote:

addLoadEvent() { var elems = getElementsByTagAndClassName("A", "view-source"); var page = "rounded_corners/"; for (var i = 0; i < elems.length; i++) { var elem = elems[i]; var href = elem.href.split(///).pop(); elem.target = "_blank"; elem.href = "../view-source/view-source.html#" + page + href; } }

This syntax is whitespace-significant; the presence or absence of a line terminator between the closing parenthesis after the function name and the opening brace affects behavior. If a line terminator occurs, semicolon insertion transforms it into a method call of no arguments and a block statement. If a line terminator doesn't occur, your behavior works (in an ES4 implementation, that is). The existing no-line-terminator uses are annoying enough, particularly in the case when I want to return an object from a method, and I'd rather see as few new instances (which don't mimic the existing ones) as possible, personally.

Jeff

Some musings...

We can avoid the auto-semicolon problem be requiring some sort of token right after the function call, e.g. "do" or "=>" or ":". Then the

brackets can go on the next line.

foo() do { ... }

foo() => // this isn't intuitive at all { ... }

However, only one lambda can be used with this syntax. To use more lambdas, we'd have to go back to the normal syntax for all lambdas except the last:

bar(function(...) ...) do { ... }

which IMO is a bit inelegant. Or maybe we could chain the blocks:

bar() do { } do // notice that this has to be on the same line as the closing "}" of the last block to avoid semicolon insertion { }

which still sucks and only works if there are no non-function arguments between the function arguments (e.g. how you would you convert bar(function() 1, "hi", function() 4) to the above chained block form?).

This goes for any other solution that resorts to putting the lambda body after the ")" in function call. While it does get rid of the closing ")" after the function arguments, it brings up way too many other problem IMO.

-Yuh-Ruey Chen

# Dave Herman (17 years ago)

Let me see if I understand your proposal. I will try to make it concrete and semi-precise, but correct my misunderstandings:

  1. add a production to the grammar allowing block statements to be used as expressions, roughly Expression ::= ... | Block

  2. treat such a block <b> as syntactic sugar for function()<b>

If a block statement can be used as an expression, how do we disambiguate from object literals?

 // empty object or empty block?
 foo(1, 2, {}, 3, 4)

 // block with stmt labelled "lab"? object with property named "lab"?
 foo(1, 2, {lab: bar()}, 3, 4)

I'm sort of sympathetic to your idea as analogous to Smalltalk and Ruby blocks. But I'm not sure it's even workable syntactically. Also, the curly brace is very overloaded in this language, so there's a serious cost involved in overloading it yet again.

If, OTOH, you're only proposing such expressions be legal in more restricted syntactic contexts (you said something like "following a function call expression") then I think your case is even weaker. It doesn't make sense to have a syntactic sugar for function expressions that can only be used in a fraction of the contexts where the real syntax can be used. I suspect it'll just confuse matters and people will adopt a "don't use block expressions 'cause they don't always work and function expressions always do" policy.

I also don't like the asymmetry with lack of named arguments. This will prevent people from ever putting type annotations on these expressions.

# Igor Bukanov (17 years ago)

On 17/03/07, Dave Herman <dherman at ccs.neu.edu> wrote:

  1. treat such a block <b> as syntactic sugar for function()<b> ... I'm sort of sympathetic to your idea as analogous to Smalltalk and Ruby blocks.

Note that Ruby's do-block is not a function but just a body of a loop and a roughly corresponds to for-in loop in JS executed over a generator.

, Igor

# Vassily Gavrilyak (17 years ago)

On 3/17/07, Igor Bukanov <igor at mir2.org> wrote:

Note that Ruby's do-block is not a function but just a body of a loop and a roughly corresponds to for-in loop in JS executed over a generator.

Note that Ruby's do-block is not a function but just a body of a loop and a roughly corresponds to for-in loop in JS executed over a generator.

Well, Ruby blocks are just anonymous functions. They are often used with loops, but that because they are good here. From Ruby language author www.artima.com/intv/closures.html

Yukihiro Matsumoto: Blocks are basically nameless functions. You may be familiar with the lambda from other languages like Lisp or Python. Basically, you can pass a nameless function to another function, and then that function can invoke the passed-in nameless function. For example, a function could perform iteration by passing one item at a time to the

nameless function.

This is a common style, called higher order function style, among

languages that

can handle functions as first class objects. Lisp does it. Python does it . Even C does it with function pointers.

ES has anonymous functions too and their semantics is the same as in Ruby. Only syntax is different. In Ruby do ... end is the same as {}. By convention, second is used for one-liners only. In while loops do can be omitted (just a sugar).

Another example of small DSL in Ruby with blocks from Rails

create_table :products do |t| t.column :title, :string t.column :description, :text t.column :image_url, :string

you can add your own code (not data) here, if needed

end

in ES it would be

createTable("products", function(t){ t.column("title", "string") t.column("description", "text") t.column("image_url", "string") // you can add your own code (not data) here, if needed })

I understand that this partial use-case is better to describe with plain data (JSON-like) but Rails gives you a possibility to define some table data with code too.

The same can be done about GUI or HTML forms, etc. So, just one more example - DSLs.

BTW, Igor, it seems that you are the only one who use the syntax exactly as I proposed. Just curios, is it because you know cyrillic (guessing from your name) and it looks to you like L or lambda letter?

, Vassily

# Vassily Gavrilyak (17 years ago)

Nice example of using some lib, that I do not know and can't see what code does. Let's take a particular function.

addLoadEvent(function() { var d = wait(0.5, {data: [["getUsers", "getUsers"], ["foo", "bar"]]}); d.addCallback(showSelectData); d.addErrback(showError); });

Question - "getUsers", "getUsers" - looks like calling 2 server methods. Are they executed sequantially or at in parallel, so second call will be after first is finished? Looks like at the same time, only code doesn't show this I would rather like to write such code directly to show my intent clearly.

Now about this addLoadEvent() { connect('bad_example','onclick') { var [e] = arguments; signal('bad_example','showvalue',"explicit_value") } connect('bad_example','showvalue',window,'alert');

connect('good_example','onclick') {
    var [e] = arguments;
    signal(randomObj,'showvalue',"explicit_value")
}
connect(randomObj,'showvalue',window,'alert');

}

It it absolutely untyped and use strings for objects and methods. Also it is uses some signal-slot framework that is not needed in ES at all. Direct and typed way will look like this

addLoadEvent((){ var badExample = $("bad_example"). badExample.onClick = (\ e:Event){ badExample.showValue("explicit_value"); }

bad_example.showvalue = (\ value:String) { window.alert("badValue:"

  • value) };

    good_example.onclick = (\ e:Event){ randomObj.showvalue(explicit_value); }

    randomObj.showvalue = (\ value){ window.alert("goodValue:" + value) }; })

So, we typed the example and removed the need for framework. A bonus - showvalue for random obj and bad_example are now showing DIFFERENT values, as it will happen often in real code.

Such examples, BTW is clear indication that developers would rather use some framwork and pass their functions as strings then go with right and typed way.

Vassily Gavrilyak

# Neil Mix (17 years ago)

Let me see if I understand your proposal.

Bleh. I should have known better.

Let me re-iterate: I'm not proposing that syntax. I was (poorly)
illustrating my assertion about the true cause of illegibility in the
use of anonymous functions as parameters.

If someone smarter than me can work up a non-alien syntax for
anonymous functions that reduces the number of parens and curlies,
more power to them. As for me, I'm now regretting that I've confused
this thread so thoroughly, and am distancing myself from the idea
altogether.

Back to the original topic, and speaking of reducing the number of
parens and curlies, one thing I forgot to do in my rewrite of
Robert's mochikit examples was to make use of the new expression
closure capabilities in es4. With those, we can change one of the
more "painful" examples from this:

addLoadEvent(function(){ connect('bad_example','onclick', function(e){signal ('bad_example','showvalue',"explicit_value")}); connect('bad_example','showvalue',window,'alert');

 connect('good_example','onclick',
     function(e){signal(randomObj,'showvalue',"explicit_value")});
 connect(randomObj,'showvalue',window,'alert');

});

to this:

addLoadEvent(function(){ connect('bad_example','onclick', function(e) signal ('bad_example','showvalue',"explicit_value")); connect('bad_example','showvalue',window,'alert');

 connect('good_example','onclick',
     function(e) signal(randomObj,'showvalue',"explicit_value"));
 connect(randomObj,'showvalue',window,'alert');

});

Now, adding in the ( lambda syntax under consideration, we get this:

addLoadEvent(function(){ connect('bad_example','onclick', (e) signal('bad_example','showvalue',"explicit_value")); connect('bad_example','showvalue',window,'alert');

 connect('good_example','onclick',
     \(e) signal(randomObj,'showvalue',"explicit_value"));
 connect(randomObj,'showvalue',window,'alert');

});

I believe there's a correlation between the size of the lambda and
the improved legibility with this shortcut -- the smaller the lambda,
the more helpful the shortcut is. For example, change something like
this:

addLoadEvent(function(){ connect('bad_example','onclick', function(e){signal('bad_example)}); connect('bad_example','showvalue',window,'alert');

 connect('good_example','onclick',
     function(e){signal(randomObj)});
 connect(randomObj,'showvalue',window,'alert');

});

to this:

addLoadEvent(function(){ connect('bad_example','onclick', (e) signal('bad_example')); connect('bad_example','showvalue',window,'alert');

 connect('good_example','onclick', \(e) signal(randomObj));
 connect(randomObj,'showvalue',window,'alert');

});

I defer to others for thoughts and opinions.

# Vassily Gavrilyak (17 years ago)

addLoadEvent(function(){ connect('bad_example','onclick', (e) signal('bad_example')); connect('bad_example','showvalue',window,'alert');

 connect('good_example','onclick', \(e) signal(randomObj));
 connect(randomObj,'showvalue',window,'alert');

});

Actually it should be this, with operator, functions expressions, types and lambda

addLoadEvent(function(){ bad_example.onclick += (\ _) bad_example.showvalue("explicit_value"); bad_example.showvalue += (\ s:String) window.alert(s); good_example.onclick += (\ _) randomObj.showvalue("explicit_value"); randomObj.showvalue += (\ s:String) window.alert(s); });

Now it's cleared from all the syntactic noise. Signal-slot framework in ES is something strange that should not be here at all. ES object properties are slots and functions calls are signals. Connection should be an assignment. ES is not C++, there is no needs for workarounds Thats the first thing bad with this example. Second thing is that is uses method names in order to actually shorten up code (avoid writing 'function' directly). With operators, expression closures and lambdas there will be no place left for such framework. Everything would be possible to write directly and use typing and compilation niceties.

Vassily.

# Bob Ippolito (17 years ago)

On 3/17/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote:

addLoadEvent(function(){ connect('bad_example','onclick', (e) signal('bad_example')); connect('bad_example','showvalue',window,'alert');

 connect('good_example','onclick', \(e) signal(randomObj));
 connect(randomObj,'showvalue',window,'alert');

});

Actually it should be this, with operator, functions expressions, types and lambda

addLoadEvent(function(){ bad_example.onclick += (\ _) bad_example.showvalue("explicit_value"); bad_example.showvalue += (\ s:String) window.alert(s); good_example.onclick += (\ _) randomObj.showvalue("explicit_value"); randomObj.showvalue += (\ s:String) window.alert(s); });

Now it's cleared from all the syntactic noise. Signal-slot framework in ES is something strange that should not be here at all. ES object properties are slots and functions calls are signals. Connection should be an assignment. ES is not C++, there is no needs for workarounds Thats the first thing bad with this example. Second thing is that is uses method names in order to actually shorten up code (avoid writing 'function' directly). With operators, expression closures and lambdas there will be no place left for such framework. Everything would be possible to write directly and use typing and compilation niceties.

The signal/slot implementation in MochiKit is not at all superfluous. More than one callback can be connected to a slot, and when connected to DOM objects it presents a uniform event object regardless of the underlying implementation.

No needs for workarounds? Are you kidding? We are talking about web browsers.

# Bob Ippolito (17 years ago)

On 3/17/07, Bob Ippolito <bob at redivi.com> wrote:

On 3/17/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote:

addLoadEvent(function(){ connect('bad_example','onclick', (e) signal('bad_example')); connect('bad_example','showvalue',window,'alert');

 connect('good_example','onclick', \(e) signal(randomObj));
 connect(randomObj,'showvalue',window,'alert');

});

Actually it should be this, with operator, functions expressions, types and lambda

addLoadEvent(function(){ bad_example.onclick += (\ _) bad_example.showvalue("explicit_value"); bad_example.showvalue += (\ s:String) window.alert(s); good_example.onclick += (\ _) randomObj.showvalue("explicit_value"); randomObj.showvalue += (\ s:String) window.alert(s); });

Now it's cleared from all the syntactic noise. Signal-slot framework in ES is something strange that should not be here at all. ES object properties are slots and functions calls are signals. Connection should be an assignment. ES is not C++, there is no needs for workarounds Thats the first thing bad with this example. Second thing is that is uses method names in order to actually shorten up code (avoid writing 'function' directly). With operators, expression closures and lambdas there will be no place left for such framework. Everything would be possible to write directly and use typing and compilation niceties.

The signal/slot implementation in MochiKit is not at all superfluous. More than one callback can be connected to a slot, and when connected to DOM objects it presents a uniform event object regardless of the underlying implementation.

No needs for workarounds? Are you kidding? We are talking about web browsers.

Additionally the method names are used because it's shorthand for:

function (object, method) { return function () { return object[method].apply(object, arguments); }; };

Really...

# Vassily Gavrilyak (17 years ago)

On 3/17/07, Bob Ippolito <bob at redivi.com> wrote:

The signal/slot implementation in MochiKit is not at all superfluous. More than one callback can be connected to a slot, and when connected to DOM objects it presents a uniform event object regardless of the underlying implementation.

No needs for workarounds? Are you kidding? We are talking about web browsers.

I am not criticizing Mochikit, I like it. It covers many workarounds for browsers. What I am telling that it shouldn't be here, such framework is possible to have in language directly and C# proves this. About more then one callback - that's exactly why I used += and not just =. Again, that is already solved in C#. And it should be as much simpler in ES, otherwise people will just move to C# and use compilers to JS.

Additionally the method names are used because it's shorthand for:

function (object, method) { return function () { return object[method].apply(object, arguments); }; };

Really...

That's I understand too, and used same approach in my code 2 years ago. Then I moved to functions, because methods are not that really needed. When I need to pass method to callback expecting function I do just setCallback(obj.method.delegate(obj)). This way only one argument (function) is needed in API, not two (object, method). Having 2 arguments complicates API, and built-in callbacks still provides one argument only (like setTimeout or Array.sort), so for me looks better to stay coherent with such callbacks.

Also there is no strings usage here, everything looks like it is "typed".

And direct call is still the best, especially with typed ES4.

-bob

, Vassily

# Bob Ippolito (17 years ago)

On 3/17/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote:

On 3/17/07, Bob Ippolito <bob at redivi.com> wrote:

The signal/slot implementation in MochiKit is not at all superfluous. More than one callback can be connected to a slot, and when connected to DOM objects it presents a uniform event object regardless of the underlying implementation.

No needs for workarounds? Are you kidding? We are talking about web browsers. I am not criticizing Mochikit, I like it. It covers many workarounds for browsers. What I am telling that it shouldn't be here, such framework is possible to have in language directly and C# proves this. About more then one callback - that's exactly why I used += and not just =. Again, that is already solved in C#. And it should be as much simpler in ES, otherwise people will just move to C# and use compilers to JS.

Additionally the method names are used because it's shorthand for:

function (object, method) { return function () { return object[method].apply(object, arguments); }; };

Really...

That's I understand too, and used same approach in my code 2 years ago. Then I moved to functions, because methods are not that really needed. When I need to pass method to callback expecting function I do just setCallback(obj.method.delegate(obj)). This way only one argument (function) is needed in API, not two (object, method). Having 2 arguments complicates API, and built-in callbacks still provides one argument only (like setTimeout or Array.sort), so for me looks better to stay coherent with such callbacks.

Also there is no strings usage here, everything looks like it is "typed".

And direct call is still the best, especially with typed ES4.

The API in question supports either one argument or two, actually. We ended up supporting both because of syntax economy.

Even if MochiKit did add delegate to Function.prototype you still have to repeat the object twice. That's no fun. MochiKit.Base.bind(fn, object) is the same as your Function.prototype.delegate, but people prefer to use MochiKit.Base.method(obj, methodName) because they don't have to type as much.

The MochiKit.Signal.connect call in this case allows one or two arguments to specify the callback directly or object+method as an implicit call to method(obj, methodName).

# Vassily Gavrilyak (17 years ago)

The API in question supports either one argument or two, actually. We ended up supporting both because of syntax economy. ......... but people prefer to use MochiKit.Base.method(obj, methodName)

because they don't

have to type as much.

That's exactly what we are discussing here, just a syntax sugar for less typing and reading. In this particular case - (obj,method) - people will use it even in typed language (ES4). I saw also people providing sortBy(nameOfProperty) method, untyped, just to save keystrokes in C# and write people.SortBy("name", "desc"); instead of right way people.Sort(delegate(Person a, Person b){ return a.name > b.name ;})

The same method is present in ActionScript actually. Now this is easy to fix and C# 3 fixed, providing inference and anonymouse functions shortcuts

Now people can write shortly and typed people.OrderBy( it => it.name)

ES4 is almost as good, but still longer people.orderBy(function(it) it.name) So people would still use shorter untyped version.

Vassily

# Igor Bukanov (17 years ago)

On 17/03/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote: ...

Now this is easy to fix and C# 3 fixed, providing inference and anonymouse functions shortcuts

Now people can write shortly and typed people.OrderBy( it => it.name) ES4 is almost as good, but still longer people.orderBy(function(it) it.name) So people would still use shorter untyped version.

So why not to propose C# syntax? IMO array.filter(elem => elem > 0)

looks better than array.filter((\elem) elem > 0)

, Igor

# Vassily Gavrilyak (17 years ago)

On 3/18/07, Igor Bukanov <igor at mir2.org> wrote:

On 17/03/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote: ...

Now this is easy to fix and C# 3 fixed, providing inference and anonymouse functions shortcuts

Now people can write shortly and typed people.OrderBy( it => it.name) ES4 is almost as good, but still longer people.orderBy(function(it) it.name) So people would still use shorter untyped version.

So why not to propose C# syntax? IMO array.filter(elem => elem > 0) looks better than array.filter((\elem) elem > 0)

, Igor

---------- Forwarded message ---------- From: Vassily Gavrilyak <gavrilyak at gmail.com>

Date: Mar 18, 2007 12:20 AM Subject: Re: Expression closures - use-cases for shortcut lambda syntax (blocks) To: Igor Bukanov <igor at mir2.org>

I already proposed it.

File.open("file", (file))=>{ //process }); May be C# way will be good, and C# is ECMA too. It has the same

superfluous () as ES3, >>but looks good.

More examples from C# spec x => x + 1 //

Implicitly typed, expression body x => { return x + 1; } // Implicitly typed, statement body (int x) => x + 1 // Explicitly

typed, expression body (int x) => { return x + 1; } // Explicitly typed, statement body (x, y) => x * y // Multiple parameters () => Console.WriteLine() // No parameters

, Vassily

# Yuh-Ruey Chen (17 years ago)

Vassily Gavrilyak wrote:

I already proposed it.

File.open("file", (file))=>{ //process }); May be C# way will be good, and C# is ECMA too. It has the same superfluous () as ES3, >>but looks good.

More examples from C# spec x => x + 1 // Implicitly typed, expression body x => { return x + 1; } // Implicitly typed, statement body (int x) => x + 1 // Explicitly typed, expression body (int x) => { return x + 1; } // Explicitly typed, statement body (x, y) => x * y // Multiple parameters () => Console.WriteLine() // No parameters

I like it. As with any other syntax addition, I'd like the syntax to be as universal as possible so as to make it intuitive. Since it's equivalent to any other lambda syntax, it can be used within object literals as well. Using all 3 lambda syntaxes:

obj = { x: 0, add: y => this.x + y, increment: y => void (this.x += y) };

obj = { x: 0, add: function(y) { return this.x + y; }, increment: function(y) { this.x += y; } };

obj = { x: 0, add: function(y) this.x + y, increment: function(y) void (this.x += y) };

For non-void functions, the a => b syntax looks really nice. Since it is

equivalent to function(a) b, I expect it to the have the same operator precedence (higher than comma I think).

However, it can't be used for class methods, which I think is fine. But if you really want the syntax to be as universal as possible, I can imagine something like this:

class Klass { foo(x) => print(x) }

or if parenthesis aren't required:

class Klass { foo x => print(x) }

But that looks a bit to alien to me, since it's asymmetric with the call syntax: we do o.foo(x), not o.foo x.

-Yuh-Ruey Chen

# Vassily Gavrilyak (17 years ago)

On 3/18/07, Yuh-Ruey Chen <maian330 at gmail.com> wrote:

Vassily Gavrilyak wrote:

I already proposed it.

File.open("file", (file))=>{ //process }); May be C# way will be good, and C# is ECMA too. It has the same superfluous () as ES3, >>but looks good.

More examples from C# spec x => x + 1 // Implicitly typed, expression body x => { return x + 1; } // Implicitly typed, statement body (int x) => x + 1 // Explicitly typed, expression body (int x) => { return x + 1; } // Explicitly typed, statement body (x, y) => x * y // Multiple parameters () => Console.WriteLine() // No parameters

I like it. As with any other syntax addition, I'd like the syntax to be as universal as possible so as to make it intuitive. Since it's equivalent to any other lambda syntax, it can be used within object literals as well. Using all 3 lambda syntaxes:

obj = { x: 0, add: y => this.x + y, increment: y => void (this.x += y) };

obj = { x: 0, add: function(y) { return this.x + y; }, increment: function(y) { this.x += y; } };

obj = { x: 0, add: function(y) this.x + y, increment: function(y) void (this.x += y) };

For non-void functions, the a => b syntax looks really nice. Since it is equivalent to function(a) b, I expect it to the have the same operator precedence (higher than comma I think).

However, it can't be used for class methods, which I think is fine. But if you really want the syntax to be as universal as possible, I can imagine something like this:

class Klass { foo(x) => print(x) }

or if parenthesis aren't required:

class Klass { foo x => print(x) }

But that looks a bit to alien to me, since it's asymmetric with the call syntax: we do o.foo(x), not o.foo x.

-Yuh-Ruey Chen

Never thought about using shortcut syntax for named functions, but it's good here too. Parenthesis can be made mandatory, not a big deal, however we tried to avoid some parenthesis earlier in this list. They are optional only with one argument in C#, but that's the most common case. This syntax seed to work nice with getters and setters too. The only place where it doesn't work is type declaration. So something like this function map(mapper: (function (:, _:uint, _:Object):), thisObj:Object):Array will become this function map(mapper: (:*, _:uint, _:Object) => *, thisObj:Object):Array

, Vassily

# Vassily Gavrilyak (17 years ago)

Correction :-)

The only place where it doesn't work is type declaration.

should be read as: It also works nicely with type declarations

So something like this function map(mapper: (function (:, _:uint, _:Object):), thisObj:Object):Array will become this function map(mapper: (:*, _:uint, _:Object) => *, thisObj:Object):Array

, Vassily

# Vassily Gavrilyak (17 years ago)

On 3/17/07, Brendan Eich <brendan at mozilla.org> wrote:

On Mar 16, 2007, at 10:38 PM, Neil Mix wrote:

On Mar 16, 2007, at 8:57 PM, Robert Sayre wrote:

Seems to me that "function(){" is a little more painful when nested.

Great examples -- you've changed my mind about the ( syntax. I would argue that the painfulness is a result of densely packed alternating/nested curlies and parens and has little to do with the function keyword.

Test that claim: show those Mochikit examples rewritten to use expression closures as proposed for ES4.

/be

Here the examples rewritten uses functions and => syntax

// forEach used instead of for addLoadEvent(() => { var page = "rounded_corners/"; var elems = getElementsByTagAndClassName("A", "view-source"); elems.forEach(elem => { var href = elem.href.split(///).pop(); elem.target = "_blank"; elem.href = "../view-source/view-source.html#" + page + href; }) });

//usage of 2 different for's isn't very nice here //there should be some way to do it with comrehensions //, but forEach will do it too addLoadEvent(() => { DomDeco.registry.forEach((registry,key) => { registry.forEach(value => DomDeco.apply(key, it)) }) }

//here add*Callback API is not neccessary, CPS will do it nicely too. //bonus - no framework knowledge needed, just a convention addLoadEvent(() => { wait(0.5, {data: [["getUsers", "getUsers"], ["foo", "bar"]]}, res=>showSelectData(res), err=>showError(res)); })

//CPS again, example is actually not complete, //showPageList is already refactored out, that is not always so in real code addLoadEvent(()=>{ pagelist.onclick = e => { e.preventDefault(); loadJSONDoc("${std.url('/pagelist', tg_format='json')}", list=>showPageList(list)); } })

//here the whole slot-signal framework (untyped) //can be replaced with redable and typed version addLoadEvent(()=>{ bad_example.onclick = _:Event => bad_example.showvalue("explicit_value"); bad_example.showvalue = s:String => window.alert(s); good_example.onclick = _:Event => randomObj.showvalue("explicit_value"); randomObj.showvalue = s:String => window.alert(s); });

Regard Vassily Gavrilyak

# Brendan Eich (17 years ago)

We've been writing int -> int when discussing the type system,

because it's too painful (especially on a whiteboard) to write
function(int):int.

When Dave suggested function (formals) expression, Jeff Dyer wanted
something like => after the (formals) -- he suggested = at the time.

I think this clinches it. We have been circling around the design
point of (formals) => assign-expr (you're right about not wanting

comma to bind more tightly than =>) for a while. Using -> when C#

uses => would just be gratuitous.

I would require parentheses around the formals, even when there is
only one.

In a class, I'm not so keen on name(formals) => expression defining a

method, given the lack of a binding form elsewhere.

# Vassily Gavrilyak (17 years ago)

On 3/18/07, Brendan Eich <brendan at mozilla.org> wrote:

We've been writing int -> int when discussing the type system, because it's too painful (especially on a whiteboard) to write function(int):int.

Yes, I agree. I have the same issue when reading the specs. There is also one more issue. When ES4 will get IDE's with tooltips for methods declarations, it would be good to have nice way to show those tooltips.

When Dave suggested function (formals) expression, Jeff Dyer wanted something like => after the (formals) -- he suggested = at the time.

I think this clinches it. We have been circling around the design point of (formals) => assign-expr (you're right about not wanting comma to bind more tightly than =>) for a while. Using -> when C# uses => would just be gratuitous.

I would require parentheses around the formals, even when there is only one.

Probably I would too. That will be confusion for C# people. Not a big issue.

In a class, I'm not so keen on name(formals) => expression defining a method, given the lack of a binding form elsewhere.

Me too, better to write function name(){ } because it's exactly FUNCTION declaration. Shortcuts can be disallowed here at all. By language or by company policy.

So, does such syntax have a chance to be in ES?

# Brendan Eich (17 years ago)

On Mar 18, 2007, at 9:30 AM, Vassily Gavrilyak wrote:

So, does such syntax have a chance to be in ES?

I think so.

# Jeff Dyer (17 years ago)

We've been writing int -> int when discussing the type system, because it's too painful (especially on a whiteboard) to write function(int):int.

When Dave suggested function (formals) expression, Jeff Dyer wanted something like => after the (formals) -- he suggested = at the time.

I think this clinches it. We have been circling around the design point of (formals) => assign-expr (you're right about not wanting comma to bind more tightly than =>) for a while. Using -> when C# uses => would just be gratuitous.

I would require parentheses around the formals, even when there is only one.

In a class, I'm not so keen on name(formals) => expression defining a method, given the lack of a binding form elsewhere.

I like it, and agree with the details as captured here. I could go without the parens around a single formal, but I've been writing a lot of ML code lately :) Maybe requiring parens is better; why have two ways to write something when one will do! Also, for now let's not extend this syntax to binding forms, inside or outside of a class. I have concerns about readability.

I'll update the parser in the next day or so to see if we've overlooked any issues. I don't expect we have.

So, does such syntax have a chance to be in ES?

I think so.

Me too.

Thanks to Vassily and others on es4-discuss for picking up the ball when we drop it.

# Vassily Gavrilyak (17 years ago)

On 3/18/07, Jeff Dyer <jodyer at adobe.com> wrote:

We've been writing int -> int when discussing the type system, because it's too painful (especially on a whiteboard) to write function(int):int.

When Dave suggested function (formals) expression, Jeff Dyer wanted something like => after the (formals) -- he suggested = at the time.

I think this clinches it. We have been circling around the design point of (formals) => assign-expr (you're right about not wanting comma to bind more tightly than =>) for a while. Using -> when C# uses => would just be gratuitous.

I would require parentheses around the formals, even when there is only one.

In a class, I'm not so keen on name(formals) => expression defining a method, given the lack of a binding form elsewhere.

I like it, and agree with the details as captured here. I could go without the parens around a single formal, but I've been writing a lot of ML code lately :)

Now you can rewrite this code directly in ES, it will look better ;-)

Maybe requiring parens is better; why have two ways to write something when one will do!

Maybe, maybe not, no clear winner here for me. Omitted parens looks better, but special case is worse.

Also, for now let's not extend this syntax to binding forms, inside or outside of a class. I have concerns about readability.

It seems everybody agreed on this.Probably getters-setters will look better with shortcut, not sure.

I'll update the parser in the next day or so to see if we've overlooked any issues. I don't expect we have.

So, does such syntax have a chance to be in ES?

I think so.

Me too.

Thanks to Vassily and others on es4-discuss for picking up the ball when we drop it.

Jd

My big thanks to you Jeff and everybody participated in discussion.

Vassily Gavrilyak

# Dave Herman (17 years ago)

I like it, and agree with the details as captured here. I could go

I agree.

without the parens around a single formal, but I've been writing a lot of ML code lately :) Maybe requiring parens is better; why have two ways to write something when one will do! Also, for now let's not extend this syntax to binding forms, inside or outside of a class. I have concerns about readability.

In your opinion, should we allow both

 function(formals) expression
 (formals) => expression

with the former allowed as both a declaration and expression form and the latter allowed as only an expression form? Or do we only allow

 (formals) => expression

and only as an expression form?

If we choose the latter, then there's no way to write function declarations with expression bodies, which seems like an unnecessary restriction.

# Jeff Dyer (17 years ago)

From: Dave Herman [mailto:dherman at ccs.neu.edu]

without the parens around a single formal, but I've been writing a

lot

of ML code lately :) Maybe requiring parens is better; why have two

ways

to write something when one will do! Also, for now let's not extend

this

syntax to binding forms, inside or outside of a class. I have

concerns

about readability.

In your opinion, should we allow both

 function(formals) expression
 (formals) => expression

with the former allowed as both a declaration and expression form and the latter allowed as only an expression form? Or do we only allow

 (formals) => expression

and only as an expression form?

If we choose the latter, then there's no way to write function declarations with expression bodies, which seems like an unnecessary restriction.

This is a trick question! We already have function expressions with a 'function' head and a '{...}' body. Dropping the 'return' and braces as a convenience.

The cool new form being proposed here doesn't replace these functions with statement bodies, but does replace their expression bodied counterparts, except when they are used as declarations.

So, we either:

  • drop the convenience altogether (sorry Brendan & Lars)
  • keep the convenience in declaration contexts only
  • keep the convenience in both contexts and have more than two ways to write function expressions

I'm less of a fan of function declarations with expression bodies than some other TG1 members, so I could live with the first option.

But given that we want to keep this convenience for function declarations, I actually think that the third option is the best one. It avoids the usability issue of special casing. I also think that some users will prefer the keyword form of function expressions over the punctuated form.

I say we do it all.

# P T Withington (17 years ago)

On 2007-03-19, at 15:01 EDT, Jeff Dyer wrote:

  • drop the convenience altogether (sorry Brendan & Lars)
  • keep the convenience in declaration contexts only
  • keep the convenience in both contexts and have more than two ways to write function expressions
  • make 'function' required for declarations, optional for
    expressions, require '=>' for both

('function' identifier?)? '(' formals? ')' '=>' expression

# Dave Herman (17 years ago)

This is a trick question! We already have function expressions with a 'function' head and a '{...}' body. Dropping the 'return' and braces as a convenience.

Not a trick question -- of course we already have functions that are expressions; the question is whether all the syntactic forms of functions (i.e., both function declarations and function expressions) have a variant where the body is an expression.

It's precisely this convenience I'm talking about. In fact, when writing in a functional style, this convenience adds up pretty quickly. Try writing a curried function with "return" and curlies -- it's painful, especially because of the sneaky requirement that return can't be followed by a newline!

 function f(x) {
     return function (y) {
         return function (z) {
             return x + y + z;
         }
     }
 }

vs.

 f(x) => (y) => (z) => (x + y + z)

or

 function f(x) (y) => (z) => (x + y + z)

I didn't quite follow your summary. Indulge my OCD and let me be painfully explicit. :) Here are all the options I think we're talking about, individually labelled (a) through (f). (Ignoring some irrelevant details like named function expressions.)

function-expression
    ::= "function" id? "(" formals ")" block           (a)
     |  "function" id? "(" formals ")" expression      (b)
     |  "function" id? "(" formals ")" "=>" expression (c)
     |  "(" formals ")" "=>" expression                (d)

function-declaration
    ::= "function" id "(" formals ")" block            (e)
     |  "function" id "(" formals" ")" expression      (f)
     |  "function" id "(" formals" ")" "=>" expression (g)
     |  id "(" formals ")" "=>" expression             (h)

expression ::= ... | function-expression
declaration ::= ... | function-declaration

I'm in favor of allowing (a), (c), (d), (e), and (g). I think that was Tucker's proposal just now.

# Jeff Dyer (17 years ago)
  • make 'function' required for declarations, optional for expressions, require '=>' for both

('function' identifier?)? '(' formals? ')' '=>' expression

The '=>' is unnecessary and breaks symmetry with function expressions

with statement bodies. So I don't like it.

# Brendan Eich (17 years ago)

On Mar 19, 2007, at 1:43 PM, Dave Herman wrote:

function-expression
    ::= "function" id? "(" formals ")" block           (a)
     |  "function" id? "(" formals ")" expression      (b)
     |  "function" id? "(" formals ")" "=>" expression (c)
     |  "(" formals ")" "=>" expression                (d)

function-declaration
    ::= "function" id "(" formals ")" block            (e)
     |  "function" id "(" formals" ")" expression      (f)
     |  "function" id "(" formals" ")" "=>" expression (g)
     |  id "(" formals ")" "=>" expression             (h)

expression ::= ... | function-expression
declaration ::= ... | function-declaration

I'm in favor of allowing (a), (c), (d), (e), and (g).

+1

# Jeff Dyer (17 years ago)

This is a trick question! We already have function expressions with

a

'function' head and a '{...}' body. Dropping the 'return' and braces

as

a convenience.

Not a trick question -- of course we already have functions that are expressions; the question is whether all the syntactic forms of functions (i.e., both function declarations and function expressions) have a variant where the body is an expression.

Okay a tricky question then :)

It's precisely this convenience I'm talking about. In fact, when

writing

in a functional style, this convenience adds up pretty quickly. Try writing a curried function with "return" and curlies -- it's painful, especially because of the sneaky requirement that return can't be followed by a newline!

 function f(x) {
     return function (y) {
         return function (z) {
             return x + y + z;
         }
     }
 }

vs.

 f(x) => (y) => (z) => (x + y + z)

or

 function f(x) (y) => (z) => (x + y + z)

I didn't quite follow your summary. Indulge my OCD and let me be painfully explicit. :) Here are all the options I think we're talking about, individually labelled (a) through (f). (Ignoring some

irrelevant

details like named function expressions.)

function-expression
    ::= "function" id? "(" formals ")" block           (a)
     |  "function" id? "(" formals ")" expression      (b)
     |  "function" id? "(" formals ")" "=>" expression (c)
     |  "(" formals ")" "=>" expression                (d)

function-declaration
    ::= "function" id "(" formals ")" block            (e)
     |  "function" id "(" formals" ")" expression      (f)
     |  "function" id "(" formals" ")" "=>" expression (g)
     |  id "(" formals ")" "=>" expression             (h)

expression ::= ... | function-expression
declaration ::= ... | function-declaration

I'm in favor of allowing (a), (c), (d), (e), and (g). I think that was Tucker's proposal just now.

I'm in favor of (a), (b), (d), (e), (f). As I've already said, (c) and (g) are not as nice as (b) and (f) because the latter erase characters from the current function expression form, while the former adds "=>"

To be consistent with the current expression form

function id (x) { return x }

should be shortened to

function id(x) x

not

function id(x) => x
# P T Withington (17 years ago)

On 2007-03-19, at 16:43 EDT, Dave Herman wrote:

I'm in favor of allowing (a), (c), (d), (e), and (g). I think that was Tucker's proposal just now.

Yes. +1

# Brendan Eich (17 years ago)

On Mar 19, 2007, at 1:59 PM, Jeff Dyer wrote:

To be consistent with the current expression form

function id (x) { return x }

should be shortened to

function id(x) x

not

function id(x) => x

Your argument is not consistency, but brevity: "because the latter
erase characters from the current function expression form, while the
former adds '=>'". A different consistency argument, with the new

lambda syntax, favors =>.

How to decide between conflicting consistencies and brevity? One role
of syntax is to catch the reader's eye and make plain what may be
hard to see or visually acquire quickly. In this light, => is good

and we would not do without it, or shorten it, for the new lambda
syntax.

Lars and I have written a fair amount (Lars did most ;-) of the
builtins/*.es self-hosted standard library (ECMA-262 Edition 3
Chapter 15) may agree (I hope) that just

function (formals) expression

has been less readable than I think they should be. Stylistically, we
have always written

function (formals) expression;

in classes. Note the semicolon, which was not originally required by
the grammar. We've been loath to put both head and body on one line
because the closing formals) may be hard to see as distinct from the
expression. Anyway, I'd like to hear Lars's preference, but I don't
think consistency favors one outcome, and brevity can't always win
(we're talking JS here, not C/C++/Java/C#).

# Jeff Dyer (17 years ago)

Your argument is not consistency, but brevity: "because the latter erase characters from the current function expression form, while the former adds '=>'". A different consistency argument, with the new lambda syntax, favors =>

I'm arguing for consistence too. The keyword forms of function are visually more closely related than the two expression forms. Let statements and expressions support this relationship. What we do for function expressions we should do for let expressions.

 let (x=y) x

become

 let (x=y) => x

But => has limited appeal to me. I'd like to use it only as we agreed

yesterday, in function expressions without a 'function' head. In all other contexts it just adds clutter.

# P T Withington (17 years ago)

On 2007-03-19, at 17:35 EDT, Jeff Dyer wrote:

 let (x=y) x

become

 let (x=y) => x

I thought it became:

(x) => x (y)

:P

# Jon Zeppieri (17 years ago)

On 3/19/07, P T Withington <ptw at pobox.com> wrote:

On 2007-03-19, at 17:35 EDT, Jeff Dyer wrote:

 let (x=y) x

become

 let (x=y) => x

I thought it became:

(x) => x (y)

...which brings up the issue of associativity. When I first looked at your example, I thought x was being applied to y, rather than (x) => x

being applied to y.

# Brendan Eich (17 years ago)

On Mar 19, 2007, at 2:35 PM, Jeff Dyer wrote:

Your argument is not consistency, but brevity: "because the latter erase characters from the current function expression form, while the former adds '=>'". A different consistency argument, with the new lambda syntax, favors =>

I'm arguing for consistence too. The keyword forms of function are visually more closely related than the two expression forms. Let statements and expressions support this relationship.

Oh, right -- sorry I didn't remember the let expression form's
influence here.

What we do for function expressions we should do for let expressions.

That's one way to be consistent with another form, yeah. It's not the
only way, in light of the new => based expression closure syntax.

 let (x=y) x

become

 let (x=y) => x

But => has limited appeal to me. I'd like to use it only as we agreed yesterday, in function expressions without a 'function' head. In all other contexts it just adds clutter.

I'm not so sure, having been through an implementation cycle (JS1.7
in Firefox 2) of let blocks and expressions. Users have not cried out
for => or = clutter, but there has been some confusion about lack of

braces meaning let expression.

It's true you did not feel the urge to use = between let head and
expression body, as you did when Dave proposed expression closures
via the 'function (formals) expr' syntax. But the same consistency
argument you make now might have applied to that desire for 'function
(formal) = expr'.

There's no hard or fast rule here. Aesthetics matter, but usability
should trump if in conflict. Alas we don't have much usability data.
I'll try to get some based on JS1.7.

# Brendan Eich (17 years ago)

On Mar 19, 2007, at 2:55 PM, Jon Zeppieri wrote:

On 3/19/07, P T Withington <ptw at pobox.com> wrote:

On 2007-03-19, at 17:35 EDT, Jeff Dyer wrote:

 let (x=y) x

become

 let (x=y) => x

I thought it became:

(x) => x (y)

...which brings up the issue of associativity. When I first looked at your example, I thought x was being applied to y, rather than (x) => x being applied to y.

Don't be tricked, Tucker was joking (note the :-P you deleted when
citing his post). He's an old Lisp and Dylan hacker.

# Brendan Eich (17 years ago)

On Mar 19, 2007, at 2:55 PM, Jon Zeppieri wrote:

On 3/19/07, P T Withington <ptw at pobox.com> wrote:

On 2007-03-19, at 17:35 EDT, Jeff Dyer wrote:

 let (x=y) x

become

 let (x=y) => x

I thought it became:

(x) => x (y)

...which brings up the issue of associativity. When I first looked at your example, I thought x was being applied to y, rather than (x) => x being applied to y.

Forgot to answer the associativity question. With ES3 function
expressions, which are primary expressions, you need not parenthesize
the function expression to call it. Just append (actuals) to invoke:

js> 1 + function(){return 2}()

3

Note the 1 + or some other left-expression-context is necessary.
Oherwise, with just:

js> function(){return 2}

function () { return 2; }

you have written a useless unnamed function expression statement that
ends at the } that closes the body, and any () after will provoke a
syntax error:

js> function(){return 2}()

typein:4: SyntaxError: syntax error: typein:4: function(){return 2}() typein:4: .....................^

This is due to

12.4 Expression Statement Syntax ExpressionStatement : [lookahead ∉ {{,function}] Expression ;

in ECMA-262 Edition 3.

Without the "clutter" => connecting the (formals) and assign-expr

body, we are free to match the ES3 behavior and not require
parentheses around the function expression in order to apply it.

So => could be considered an operator with high precedence, but then

almost all expression bodies would have to be parenthesized. If it is
low precedence, then you will need to parenthesize ((x) => x)(y). I

think Yuh-Ruey Chen already proposed that => be low precedence, but

higher than the comma operator, in an earlier post.

This seems to me another advantage of => -- that it facilitates

operator precedence parsing of expression closures.

# Jeff Dyer (17 years ago)

There's no hard or fast rule here. Aesthetics matter, but usability should trump if in conflict. Alas we don't have much usability data. I'll try to get some based on JS1.7.

Great! And my sense of aesthetics might change in the mean time :)

While we are talking about function expressions. I assume that type parameters are allowed. For example,

var id = .<t>(x:t):t=>x

should work. It falls out naturally as the syntax on the left of => is a

FunctionSignature. Can't remember, maybe we already talked about this.

# Brendan Eich (17 years ago)

On Mar 19, 2007, at 3:36 PM, Brendan Eich wrote:

you have written a useless unnamed function expression statement
that ends at the } that closes the body, and any () after will
provoke a syntax error:

js> function(){return 2}() typein:4: SyntaxError: syntax error: typein:4: function(){return 2}() typein:4: .....................^

This is due to

12.4 Expression Statement Syntax ExpressionStatement : [lookahead ∉ {{,function}] Expression ;

in ECMA-262 Edition 3.

But of course, the chapter and verse I cite, in conjunction with the
rest of the grammar, makes even

js> function () {}

a SyntaxError. But it's not in SpiderMonkey, due to an old
compatibility promise (see bugzilla.mozilla.org/show_bug.cgi? id=303723). Probably it could be made a SyntaxError with more work
under the SpiderMonkey API.

Just wanted to avoid sowing more confusion. ES3's use of function name (formals) block in several contexts, including in expressions where
name is optional, may be the root confusion causer here. If back in
1998 we had thought of the => alterative syntax for function

expressions (unnamed ones -- named ones could use function name (formals) => assign-expr), we should have introduced that form then.

It would have saved us the current trouble ;-).

# Chris Pine (17 years ago)

On 17/03/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote:

File.open("file.txt") do | file file.process() end

Should be: File.open("file.txt") do |file| ... end

On Sat, 17 Mar 2007 02:37:13 +0100, Igor Bukanov <igor at mir2.org> wrote:

Ruby's do construction is also a loop, not a lambda

Not true at all; it's every bit a lambda and not at all a loop. The above
is the same as:

file_func = lambda {file.process()}

File.open("file.txt", &file_func)

Not really all that relevant, but just couldn't let it pass. :)

Chris

# Lars T Hansen (17 years ago)

On 3/19/07, Brendan Eich <brendan at mozilla.org> wrote:

Anyway, I'd like to hear Lars's preference, but I don't think consistency favors one outcome, and brevity can't always win (we're talking JS here, not C/C++/Java/C#).

gmail threading is pretty much broken so I don't quite know where all of this stands as of now, but for the record I pretty much loathe these proposals :-)

As far as I'm concerned if it doesn't start with "function" it's a non-starter, because the terse syntax isn't worth the cost. The brevity of anything else doesn't actually pay off (none of the examples posted convince me).

I'm not even sure that expression functions of the form "function (a) a*2" are really worth it, but at least I'll concede that we agreed to put those in. I doubt it makes any difference at all whether one writes "function (x) 37" or "function (x) => 37"; I think they're both

slightly inferior to "function (x) { return 37 }".

Note I'm not opposed to punctuation / special syntax in general; the object/array initializers are obviously better than their more "general" counterparts. I'm just not convinced that that's the case for function expressions, which are already quite succinct in ES3.

(Also, unless I'm mistaken something like the proposed "(a,b,c,d,e,f,g,h,i,j) => 37" requires arbitrary lookahead in a

top-down parser, and I thought we were trying to avoid that. I understand it can be fixed in the same way we fix destructuring assignments, but it makes me uneasy, as the arbitrary lookahead is also required by the human reader. At least for destructuring the major use cases are in binding forms, not really in plain assignments.)

# Jeff Dyer (17 years ago)

From: lars.t.hansen at gmail.com [mailto:lars.t.hansen at gmail.com]

As far as I'm concerned if it doesn't start with "function" it's a non-starter, because the terse syntax isn't worth the cost. The brevity of anything else doesn't actually pay off (none of the examples posted convince me).

I'm not even sure that expression functions of the form "function (a) a*2" are really worth it, but at least I'll concede that we agreed to put those in. I doubt it makes any difference at all whether one writes "function (x) 37" or "function (x) => 37"; I think they're both slightly inferior to "function (x) { return 37 }".

The => is a deal breaker for me. It looks foreign in the context of the

function keyword.

Note I'm not opposed to punctuation / special syntax in general; the object/array initializers are obviously better than their more "general" counterparts. I'm just not convinced that that's the case for function expressions, which are already quite succinct in ES3.

Let expressions strengthen the case for statement-less function expressions. But let's be clear, both new forms exist for aesthetics only. They do nothing that isn't already being done by ES3 function expressions.

(Also, unless I'm mistaken something like the proposed "(a,b,c,d,e,f,g,h,i,j) => 37" requires arbitrary lookahead in a top-down parser, and I thought we were trying to avoid that. I understand it can be fixed in the same way we fix destructuring assignments, but it makes me uneasy, as the arbitrary lookahead is also required by the human reader. At least for destructuring the major use cases are in binding forms, not really in plain assignments.)

Good point.

# Brendan Eich (17 years ago)

On Mar 20, 2007, at 4:38 PM, Jeff Dyer wrote:

The => is a deal breaker for me. It looks foreign in the context of
the function keyword.

Yeah, having let or function and the => is a bit much (but I still

recall you wanting = at first in the same role for expression
closures ;-).

As Lars points out it requires bottom up parsing or a trivial mod to
formal top-down parsing to cope. But we have other such syntactic
exceptions to our top-down C-like heritage. I'm not sure this should
matter.

Note I'm not opposed to punctuation / special syntax in general; the object/array initializers are obviously better than their more "general" counterparts. I'm just not convinced that that's the case for function expressions, which are already quite succinct in ES3.

Let expressions strengthen the case for statement-less function expressions. But let's be clear, both new forms exist for aesthetics only. They do nothing that isn't already being done by ES3 function expressions.

It's not just aesthetics. Aesthetics is the science of beauty, but
usability is what users grok -- not always what is beautiful. IOW,
the pile of "function" keywords in the Y combinator:

function Y(le) { return function (f) { return f(f); }(function (f) { return le(function (x) { return f(f)(x); }); }); }

may be both ugly and unusable, scaled to higher degree of lambda
usage. We can make it prettier by supporting expression closures as
proposed (no { return and } noise), and more usable. But would it be
perhaps most usable, even if uglier by some tastes, to use syntax
such as => instead of the leading function?

# Lars T Hansen (17 years ago)

On 3/20/07, Brendan Eich <brendan at mozilla.org> wrote:

On Mar 20, 2007, at 4:38 PM, Jeff Dyer wrote:

The => is a deal breaker for me. It looks foreign in the context of the function keyword.

Yeah, having let or function and the => is a bit much (but I still recall you wanting = at first in the same role for expression closures ;-).

As Lars points out it requires bottom up parsing or a trivial mod to formal top-down parsing to cope. But we have other such syntactic exceptions to our top-down C-like heritage. I'm not sure this should matter.

I've started wondering how trivial that mod actually is; the parser must be prepared to cope with type annotations in there:

(a,b:int):int => a+b

so any paren expression will effectively be parsed twice: once to accumulate comma-separated possibly-annotated expressions, and a second time to figure it out once we know how to interpret it. Still not a show-stopper, I know.

Note I'm not opposed to punctuation / special syntax in general; the object/array initializers are obviously better than their more "general" counterparts. I'm just not convinced that that's the case for function expressions, which are already quite succinct in ES3.

Let expressions strengthen the case for statement-less function expressions. But let's be clear, both new forms exist for aesthetics only. They do nothing that isn't already being done by ES3 function expressions.

It's not just aesthetics. Aesthetics is the science of beauty, but usability is what users grok -- not always what is beautiful. IOW, the pile of "function" keywords in the Y combinator:

function Y(le) { return function (f) { return f(f); }(function (f) { return le(function (x) { return f(f)(x); }); }); }

may be both ugly and unusable, scaled to higher degree of lambda usage. We can make it prettier by supporting expression closures as proposed (no { return and } noise), and more usable. But would it be perhaps most usable, even if uglier by some tastes, to use syntax such as => instead of the leading function?

The Y combinator is not even remotely a compelling use case for ECMAScript. ;-) More compelling are those that have already been posted in this thread, and as I wrote before, I didn't find those very compelling either.

# Jeff Dyer (17 years ago)

From: lars.t.hansen at gmail.com [mailto:lars.t.hansen at gmail.com] On

Behalf

Of Lars T Hansen Sent: Tuesday, March 20, 2007 6:02 PM To: Brendan Eich Cc: Jeff Dyer; Dave Herman; es4-discuss at mozilla.org; Vassily

Gavrilyak;

Yuh-Ruey Chen Subject: Re: Expression closures - use-cases for shortcut lambda syntax(blocks)

On 3/20/07, Brendan Eich <brendan at mozilla.org> wrote:

On Mar 20, 2007, at 4:38 PM, Jeff Dyer wrote:

The => is a deal breaker for me. It looks foreign in the context

of

the function keyword.

Yeah, having let or function and the => is a bit much (but I still recall you wanting = at first in the same role for expression closures ;-).

As Lars points out it requires bottom up parsing or a trivial mod to formal top-down parsing to cope. But we have other such syntactic exceptions to our top-down C-like heritage. I'm not sure this should matter.

I've started wondering how trivial that mod actually is; the parser must be prepared to cope with type annotations in there:

(a,b:int):int => a+b

Yes and as I look at the grammar with the new syntax added, it has some ugly redundancy.

so any paren expression will effectively be parsed twice: once to accumulate comma-separated possibly-annotated expressions, and a second time to figure it out once we know how to interpret it. Still not a show-stopper, I know.

Note I'm not opposed to punctuation / special syntax in general;

the

object/array initializers are obviously better than their more "general" counterparts. I'm just not convinced that that's the

case

for function expressions, which are already quite succinct in

ES3.

Let expressions strengthen the case for statement-less function expressions. But let's be clear, both new forms exist for

aesthetics

only. They do nothing that isn't already being done by ES3

function

expressions.

It's not just aesthetics. Aesthetics is the science of beauty, but usability is what users grok -- not always what is beautiful.

Ah, thanks for clarifying.

I think the proposed syntax loses the usability case. It is redundant, and most users won't be able to hold enough of the grammar in their heads to easily see its meaning when they encounter it in the wild.

the pile of "function" keywords in the Y combinator:

function Y(le) { return function (f) { return f(f); }(function (f) { return le(function (x) { return f(f)(x); }); }); }

may be both ugly and unusable, scaled to higher degree of lambda usage. We can make it prettier by supporting expression closures as proposed (no { return and } noise), and more usable. But would it be perhaps most usable, even if uglier by some tastes, to use syntax such as => instead of the leading function?

The Y combinator is not even remotely a compelling use case for ECMAScript. ;-) More compelling are those that have already been posted in this thread, and as I wrote before, I didn't find those very compelling either.

Sorry Vassily et al, but I'm changing my mind. We should decide finally at the f2f tomorrow.

# Igor Bukanov (17 years ago)

On 21/03/07, Lars T Hansen <lth at acm.org> wrote:

On 3/20/07, Brendan Eich <brendan at mozilla.org> wrote:

It's not just aesthetics. Aesthetics is the science of beauty, but usability is what users grok -- not always what is beautiful. IOW, the pile of "function" keywords in the Y combinator:

function Y(le) { return function (f) { return f(f); }(function (f) { return le(function (x) { return f(f)(x); }); }); } The Y combinator is not even remotely a compelling use case for ECMAScript. ;-) More compelling are those that have already been posted in this thread, and as I wrote before, I didn't find those very compelling either.

Yet Y combinator is a useful boundary case to test syntax proposals that in fact caused me to cool for the proposal. Here are the alternatives:

function(le) (function(f) f(f))(function(f) le(function(x) f(f)(x)))

le => (f => f(f))(f => le(x => f(f)(x)))

Both are better then the original version and it seems that "name =>

expr" is significantly less clattered than the "function (name) exp" case. But I remember when I saw for the first time Y code written in Haskell (which is much more suited to write that kind stuff) then to grasp the idea I had to rewrite it using:

function selfApply(f) f(f)

which gives for the combinator:

function(le) selfApply(function(f) le(function(x) f(f)(x)))

le => selfApply(f => le(x => f(f)(x)))

Now the "=>" case does not look that much better and in fact I suspect

that to comprehend the code it can be worse than using function keyword since an explicit and long keyword gives a useful reference indeed.

The bottom line: "=>" allows to create incomprehensible but nicely

looking code yet for simpler down-to earth cases its advantages are minor if any.

, Igor

# Vassily Gavrilyak (17 years ago)

On 3/20/07, Lars T Hansen <lth at acm.org> wrote:

On 3/19/07, Brendan Eich <brendan at mozilla.org> wrote:

Anyway, I'd like to hear Lars's preference, but I don't think consistency favors one outcome, and brevity can't always win (we're talking JS here, not C/C++/Java/C#).

gmail threading is pretty much broken so I don't quite know where all of this stands as of now, but for the record I pretty much loathe these proposals :-)

As far as I'm concerned if it doesn't start with "function" it's a non-starter, because the terse syntax isn't worth the cost. The brevity of anything else doesn't actually pay off (none of the examples posted convince me).

That's probably because you've just looked at tha Well, that's ok.

I'm not even sure that expression functions of the form "function (a) a*2" are really worth it, but at least I'll concede that we agreed to put those in. I doubt it makes any difference at all whether one writes "function (x) 37" or "function (x) => 37"; I think they're both slightly inferior to "function (x) { return 37 }".

Note I'm not opposed to punctuation / special syntax in general; the object/array initializers are obviously better than their more "general" counterparts. I'm just not convinced that that's the case for function expressions, which are already quite succinct in ES3.

Yes, two times longer then everything else. That's why we have no "first-class" functions in ES. First-class means same rights, while functions are discriminated in syntax (and only in syntax). That's probably almost everybody knows that functions are first-class in Ruby, and very few know this fact about ES :-)

(Also, unless I'm mistaken something like the proposed "(a,b,c,d,e,f,g,h,i,j) => 37" requires arbitrary lookahead in a top-down parser, and I thought we were trying to avoid that. >

I wasn't in favour of this proposal much and exactly because of lookahead. Initially proposed haskell-like (\ it){} was free from lookahead problem, it event wasn't in parser, but in lexer. But it is not so pretty, I agree. => also plays well with typing of functional parameters (where YOU had

a pain to write and read).

I understand it can be fixed in the same way we fix destructuring assignments, but it makes me uneasy, as the arbitrary lookahead is also required by the human reader. At least for destructuring the major use cases are in binding forms, not really in plain assignments.)

--lars

Please take a minute and write this simple Ruby code in ES. Please do not refactor it, suppose we do not need any of logic from this function anywhere else and we do not want to bloat our class with miriads of methods. So. just one function.

def doSomethingWithNewPeople selectSQL ="SELECT code, name from people where status='new'" insertSQL = "INSERT INTO young(name,age) values (?,?)" connection.transaction do |tx| connection.prepare(selectSQL) do |selectStatement| connection.prepare(insertSQL) do |insertStatement| selectStatement.executeQuery do |record| name, age = record if name ~ /.*Joe/ and age % 2 then insertStatement.execute(name, age) end end end end end end

# Igor Bukanov (17 years ago)

On 21/03/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote:

Please take a minute and write this simple Ruby code in ES. Please do not refactor it, suppose we do not need any of logic from this function anywhere else and we do not want to bloat our class with miriads of methods. So. just one function.

def doSomethingWithNewPeople selectSQL ="SELECT code, name from people where status='new'" insertSQL = "INSERT INTO young(name,age) values (?,?)" connection.transaction do |tx| connection.prepare(selectSQL) do |selectStatement| connection.prepare(insertSQL) do |insertStatement| selectStatement.executeQuery do |record| name, age = record if name ~ /.*Joe/ and age % 2 then insertStatement.execute(name, age) end end end end end end

Some refactoring is inevitable as Ruby provides its own idioms, but here is its counterpart in JS:

function doSomethingWithNewPeople() { let selectSQL ="SELECT code, name from people where status='new'" let insertSQL = "INSERT INTO young(name,age) values (?,?)" connection.transaction(function(tx) { let selectStatement = connection.prepare(selectSQL); let insertStatement = connection.prepare(insertSQL); for (let [name, age] in selectStatement.executeQuery()) { if (name.match(/.*Joe/) && age % 2) insertStatement.execute(name, age); } }); }

, Igor

# Vassily Gavrilyak (17 years ago)

---------- Forwarded message ---------- From: Vassily Gavrilyak <gavrilyak at gmail.com>

Date: Mar 21, 2007 7:18 PM Subject: Re: Expression closures - use-cases for shortcut lambda syntax(blocks) To: Igor Bukanov <igor at mir2.org>

On 3/21/07, Igor Bukanov <igor at mir2.org> wrote:

On 21/03/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote:

Please take a minute and write this simple Ruby code in ES. Please do not refactor it, suppose we do not need any of logic from this function anywhere else and we do not want to bloat our class with miriads of methods. So. just one function.

def doSomethingWithNewPeople selectSQL ="SELECT code, name from people where status='new'" insertSQL = "INSERT INTO young(name,age) values (?,?)" connection.transaction do |tx| connection.prepare(selectSQL) do |selectStatement| connection.prepare(insertSQL) do |insertStatement| selectStatement.executeQuery do |record| name, age = record if name ~ /.*Joe/ and age % 2 then insertStatement.execute(name, age) end end end end end end

Some refactoring is inevitable as Ruby provides its own idioms, but here is its counterpart in JS:

function doSomethingWithNewPeople() { let selectSQL ="SELECT code, name from people where status='new'" let insertSQL = "INSERT INTO young(name,age) values (?,?)" connection.transaction(function(tx) { let selectStatement = connection.prepare(selectSQL); let insertStatement = connection.prepare(insertSQL); for (let [name, age] in selectStatement.executeQuery()) { if (name.match(/.*Joe/) && age % 2) insertStatement.execute(name, age); } }); }

, Igor


Es4-discuss mailing list Es4-discuss at mozilla.org, mail.mozilla.org/listinfo/es4-discuss

2 leaks. Statements must be closed too. They are prepared, and sometimes you will need to use them again, sometime not. So you can't rely on some "external statement manager" here. GC will not help too. So, simple code provokes 2 bugs in ES. I agree with for (you mean generator here) and let, that is very nice addition to ES. Direct translation with proposed syntax will be this. Using only

function doSomethingWithNewPeople(){ var selectSQL ="SELECT code, name from people where status='new'"; var insertSQL = "INSERT INTO young(name,age) values (?,?)" connection.transaction( tx => { connection.prepare(selectSQL).using(selectStatement => { connection.prepare(insertSQL).using(insertStatement => { selectStatement.query(selectSQL).fetch(record =>{ let [name, age] = record; if(isPersonYoung(name, age)){ insertStatement.execute(name, age); } }) }) }) }) }

Plain ES3 version

function doSomethingWithNewPeople(){ var selectSQL ="SELECT code, name from people where status='new'"; var insertSQL = "INSERT INTO young(name,age) values (?,?)" connection.transaction(function(tx){ connection.prepare(selectSQL).using(function(selectStatement){ connection.prepare(insertSQL).using(function(insertStatement){ selectStatement.query(selectSQL).fetch(function(record) { var name = record[0]; var age = record[1]; if(isPersonYoung(name, age)){ insertStatement.execute(name, age); } }) }) }) }) }

There are also versions with try-finally, even more painful. The same about every other use-cases. Now, I'm sure you are very experienced developer (I saw lot of your code). Now think how many bugs like those I see in novice code. And I can't supply them with clean pattern to avoid such kind of bugs. I can supply such example for every other use-case I provided, but I'm tired of this. Just belive, our team wrote much of such kind of code in server-side JS, client-side DHTML and AS and we have found the clear patterns for all of those use-cases. Ruby, Perl and C# developers found them too, so idioms are well-understood. Python has some workarounds with "with" keyword, but they don't cover all the use-cases. Java developers are discussing closures syntax right now. ES4 will be far behind.

, Vassily

# Igor Bukanov (17 years ago)

First, let me apologize about spreading FUD about Ruby do-blocks. They are indeed full lambdas at least since 2002 even if the originally they were indeed a body of do-loop integrated with generators.

Now back to to that Rubby example.

On 21/03/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote:

2 leaks. Statements must be closed too. They are prepared, and sometimes you will need to use them again, sometime not. So you can't rely on some "external statement manager" here. GC will not help too. So, simple code provokes 2 bugs in ES.

Well, I did not know that the transaction destructor would leave the prepared statements. In any case, a good library should reflect the language so a direct translation of idioms from one language to another may lead to ugly code.

I agree with for (you mean generator here) and let, that is very nice addition to ES. Direct translation with proposed syntax will be this. Using only

If one really wants to go to the direct translation, then one have to use the generators all the way down to much Ruby semantics as the example does not require lambdas as the do-blocks-as-lambdas do not leak:

function doSomethingWithNewPeople() { let selectSQL ="SELECT code, name from people where status='new'"; let insertSQL = "INSERT INTO young(name,age) values (?,?)"; for (let tx in connection.transaction()) { for (let selectStatement in connection.prepare(selectSQL)) { for (let insertStatement in connection.prepare(insertSQL)) { for (let [name, age] in selectStatement.executeQuery()) { if (name.match(/.*Joe/) && age % 2) insertStatement.execute(name, age); } } } } }

Except for the loop stigma that is associated with for-in statement, I do not see how this is worse than that Ruby's case. I suspect that do blocks in Ruby do no have the loop stigma since from the date one Ruby advocated such usage.

, Igor

# Brendan Eich (17 years ago)

On Mar 21, 2007, at 3:26 AM, Igor Bukanov wrote:

Yet Y combinator is a useful boundary case to test syntax proposals that in fact caused me to cool for the proposal. Here are the alternatives:

[...]

function selfApply(f) f(f)

which gives for the combinator:

function(le) selfApply(function(f) le(function(x) f(f)(x)))

le => selfApply(f => le(x => f(f)(x)))

Now the "=>" case does not look that much better and in fact I suspect that to comprehend the code it can be worse than using function keyword since an explicit and long keyword gives a useful reference indeed.

The bottom line: "=>" allows to create incomprehensible but nicely looking code yet for simpler down-to earth cases its advantages are minor if any.

I agree (nice demonstration based on Y -- thanks!). I'm quite cool to
=> now, and I'd like to point out that with expression closures as

proposed, the only issue that nags at us, the one Vassily raised
obliquely, is the cost of the leading 'function'.

I agree the top-down parse-ability of (a,b,c) => expr is poor. Lars

makes a good point about have to check the AST. I pointed out that we
have a similar case with destructuring assignment (but not
destructuring in declarative binding contexts). But two wrongs...

If we had proper macros, we could support |define λ function| and be
done. As Peter Hall points out, λ is a perfectly valid ES3
identifier. The one could rewrite:

function(le) (function(f) f(f))(function(f) le(function(x) f(f)(x)))

as:

λ(le) (λ(f) f(f))(λ(f) le(λ(x) f(f)(x)))

which looks awfully familiar. But that can wait for ES6 ;-).

# P T Withington (17 years ago)

On 2007-03-21, at 13:44 EDT, Vassily Gavrilyak wrote:

---------- Forwarded message ---------- From: Vassily Gavrilyak <gavrilyak at gmail.com> [...]

There are also versions with try-finally, even more painful. The same about every other use-cases.

Another solution would be to add macros to the language, so that the
try/finally version was cleaner. That's the lispy way:

macro withPreparedStatement { withPreparedStatement (??prepared:variable = ?statement:expression
on ?connection:expression) ?body:statement => { let ?prepared = ?connnection.prepare(?statement); try { ?body } finally { ?connnection.deallocate(?prepared); } } }

function doSomethingWithNewPeople() { withPreparedStatement(selectStatement = "SELECT code, name from people where status='new'" on
connection) { withPreparedStatement(insertStatement = "INSERT INTO young(name,age) values (?,?)" on connection){ connection.transaction(function(tx) { for (let [name, age] in selectStatement.executeQuery()) { if (name.match(/.*Joe/) && age % 2) insertStatement.execute(name, age); } }); } } }

:)

[Yes I know the macro idea has been deferred.]

# Vassily Gavrilyak (17 years ago)

I totally agree with both proposals, they will work. I will argue though :-)

Generators are the same as functions, that's true. And I understand you, Igor, as a primary generators author. Everything looks like 'for' :-). The same with me, everything looks like 'function' :-). We already talked about this, shortly. I understand that everything can be done with generators. Just why with this cool new concept, and not plain old function? I still have problems explaining generators even to most experienced members of our team. Functions were accepted almost in no time.

Macros are deferred, and rightly so, I do not see many major use-cases for macro in ES (as there are still no use for macros in Ruby, language just good enough). I would like having macros probably, but why use this heavy artillery when simple function will do? Especially when those functions are already here and they are doing everything and doing just right. Only a LITTLE clumsy. Why add new concepts into the language? And your example looks almost the same as mine, the only difference is in using = instead of =>

You removed parens somehow in withStatement, with just a little magic. What if I want to add another function argument after this block (2 function arguments)? Seems I can't, syntax problem, two blocks, and no possibility to do this in one macro (that was already discussed in this thread, Dave told about something similar). So we will need macro overloading by arguments counts or types, or something like this. I won't talk about well-known problems with macros from Lisp (why people avoid them?). Why complicate things? How I can explain this magic to newbie? I explained function shortcuts very easy, in one statement. No magic here. Just plain functions you already know, written with short syntax you have already seen and used for arrays, objects, strings and regexps (and dates in ES4). It's clear what they do, clear how to write, read and debug them (reusing knowledge).

You can defer functions to ES5 and you have all the rights to do so, you are the language designers. Just I do not understand the reasons. Every other language seems wanting to provide something in this area (C#, Java, even C++ discussed this) or already provided (Ruby etc). It was hard to do for designers of those languages, but user asks them and they just provide what users ask. ES had good functions almost from the start (thanks Brendan). Use-cases for them are clear from other languages and from ES itself. Why not just reuse their experience? We will also reuse developers knowledge this way.

Those were pros. Cons? I see only technical problems with lookahead parser, though lookahead is already here. Readability arguments - it seems nobody provided more readable code with current syntax. Project policy - one can always disable usage of shortcuts for a team. What else?

Best , Vassily

# Vassily Gavrilyak (17 years ago)

On 3/21/07, Igor Bukanov <igor at mir2.org> wrote:

First, let me apologize about spreading FUD about Ruby do-blocks. They are indeed full lambdas at least since 2002 even if the originally they were indeed a body of do-loop integrated with generators.

Now back to to that Rubby example.

On 21/03/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote:

2 leaks. Statements must be closed too. They are prepared, and sometimes you will need to use them again, sometime not. So you can't rely on some "external statement manager" here. GC will not help too. So, simple code provokes 2 bugs in ES.

Well, I did not know that the transaction destructor would leave the prepared statements. In any case, a good library should reflect the language so a direct translation of idioms from one language to another may lead to ugly code.

Yes, they can leave and that's good. Connections are pooled, statements can be reused later again.

I agree with for (you mean generator here) and let, that is very nice addition to ES. Direct translation with proposed syntax will be this. Using only

If one really wants to go to the direct translation, then one have to use the generators all the way down to much Ruby semantics as the example does not require lambdas as the do-blocks-as-lambdas do not leak:

function doSomethingWithNewPeople() { let selectSQL ="SELECT code, name from people where status='new'"; let insertSQL = "INSERT INTO young(name,age) values (?,?)"; for (let tx in connection.transaction()) { for (let selectStatement in connection.prepare(selectSQL)) { for (let insertStatement in connection.prepare(insertSQL)) { for (let [name, age] in selectStatement.executeQuery()) { if (name.match(/.*Joe/) && age % 2) insertStatement.execute(name, age); } } } } }

Except for the loop stigma that is associated with for-in statement, I do not see how this is worse than that Ruby's case. I suspect that do blocks in Ruby do no have the loop stigma since from the date one Ruby advocated such usage.

Hmm, maybe there is some idea here.... It looks nicer then closure example, for this particularly usage. At least variable are written before statement, not after as with functions and that's something I like. Maybe we can get rid of for stigma somehow... Need to think more about this.

Thanks, Vassily

# Chris Double (17 years ago)

On 3/22/07, P T Withington <ptw at pobox.com> wrote:

Another solution would be to add macros to the language, so that the try/finally version was cleaner.

Having a lightweight closure syntax reduces the need for many with-* style macros. This is how Smalltalk manages to have a lot of nice looking additions to the language by using their lightweight blocks. There was a discussion a while back on ll1-discuss about it:

people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg02060.html

I wrote a parser combinator library in Javascript and the noise of 'function' did get in the way a bit when dealing with on-the-fly constructed parsers.

Chris.

# P T Withington (17 years ago)

Hey Chris, you throw Bachrach, Weinreb, and Graham at me, I have to
cry uncle!

# Yuh-Ruey Chen (17 years ago)

Vassily Gavrilyak wrote:

If one really wants to go to the direct translation, then one have to use the generators all the way down to much Ruby semantics as the example does not require lambdas as the do-blocks-as-lambdas do not leak:

function doSomethingWithNewPeople() { let selectSQL ="SELECT code, name from people where status='new'"; let insertSQL = "INSERT INTO young(name,age) values (?,?)"; for (let tx in connection.transaction()) { for (let selectStatement in connection.prepare(selectSQL)) { for (let insertStatement in connection.prepare(insertSQL)) { for (let [name, age] in selectStatement.executeQuery()) { if (name.match(/.*Joe/) && age % 2) insertStatement.execute(name, age); } } } } }

Except for the loop stigma that is associated with for-in statement, I do not see how this is worse than that Ruby's case. I suspect that do blocks in Ruby do no have the loop stigma since from the date one Ruby advocated such usage. Hmm, maybe there is some idea here.... It looks nicer then closure example, for this particularly usage. At least variable are written before statement, not after as with functions and that's something I like. Maybe we can get rid of for stigma somehow... Need to think more about this.

Thanks, Vassily

, Igor


Es4-discuss mailing list Es4-discuss at mozilla.org, mail.mozilla.org/listinfo/es4-discuss


Es4-discuss mailing list Es4-discuss at mozilla.org, mail.mozilla.org/listinfo/es4-discuss

As I've mentioned before, Python has a "with" statement that seems to cover this issue, but I'm not that familiar with Python 2.5 features so I'm not really sure.

In any case, we can come up with a construct that explicitly evaluates a generator only once. Something like (reusing do keyword):

do (let tx in connection.transaction()) { ... }

But this still has a slight connotation of a loop (do...while). Or reusing the let syntax:

let (tx in connection.transaction()) { ... }

One possible disadvantage of this let syntax is that tx can't be an existing variable, i.e. tx is always a new variable within the block's scope. I say possible, since I don't see any use case that requires relaxing this restriction.

Just some brainstorming...

# Igor Bukanov (17 years ago)

On 21/03/07, Vassily Gavrilyak <gavrilyak at gmail.com> wrote:

And I understand you, Igor, as a primary generators author.

I has nothing to do with generator authorship in JS or any other language!

Everything looks like 'for' :-). The same with me, everything looks like 'function' :-). We already talked about this, shortly. I understand that everything can be done with generators. Just why with this cool new concept, and not plain old function?

Because the question was how to write the example in ES, not how to write it in ES using only functions.

The result was in fact very readable code which IMO looks better (if one forgets the loop stigma) than both function(name) expr and name =>

expr versions. That plus those Y-combinator examples tells me that in fact name => expr or (\name) expr as syntax sugar are not sufficiently

sweet to justify their existence compared with function(name) expr.

, Igor