Allen's lambda syntax proposal

# Brendan Eich (15 years ago)

At the TC39 meeting two weeks ago in Kona, we had a brief bikeshedding
discussion about lambda syntax and why it matters. Observation: blocks
in Smalltalk being lightweight means users don't mind writing them for
control abstractions, compared to JS functions in ES3. In Smalltalk,
ignoring JS, it's hard to beat [ and ] as overhead, although one must
count the message selector and its punctuation too.

Allen Wirfs-Brock put his proposal, which will not shock you who know
Smalltalk or Allen, on the whiteboard:

// Instead of lambda (a, b, c) { ... }, why not: { |a, b, c| ... } ?

I then started to write an example of return to label, and in need of
a nested lambda, got stuck for a split second trying to write
function^H^H^H^H^H^H^H^lambda. After thinking 0.3 more seconds I then
said "I will use Allen's proposed syntax". Pure win, readers and the
writer (me) agreed.

I think someone proposed pretty much the same syntax here on es*- discuss within the last two years, but I can't find that message at
the moment.

Bikeshed color is secondary to semantics, but lambda conciseness does
matter. I think Allen's homage to Smalltalk in JS wins. Every time I
reach for more verbose syntax my hand steers back to those ||
delimiters.

Am I an old Smalltalk fan? Sure, I have Byte magazine with the
balloons on the cover still (in a box somewhere; mildewed, sadly). I'm
the C hacker who took the "make it look like Java" orders and made it
look like C with some awk, Self, Scheme, and even HyperCard
(HyperTalk, actually) influences.

Eclecticism is not an end, but it could be a means to a better end
than a cramped non-eclectic grammar, if the deeper reasons for concise
lambda syntax are sound and valid. Syntax is for users, it must be
usably sweet. It's not all about theoretical completeness and
minimality.

Anyway, we need a fun weekend thread, and everyone loves syntax.
Comments? Huzzahs? The latter go to Allen. Go nuts.

# Peter Michaux (15 years ago)

2008/11/29 Brendan Eich <brendan at mozilla.com>:

// Instead of lambda (a, b, c) { ... }, why not: { |a, b, c| ... } ?

In know this syntax primarily from Ruby's blocks.

This would allow writing

var foo = {|a, b, c| ...};

which could be written even shorter as

var foo(a, b, c) {...};

The later is more consistent with the following which would also be necessary at times.

lambda(a, b, c) {...}

I'd prefer the last two options.

Anyway, we need a fun weekend thread, and everyone loves syntax. Comments?

The following please!

(lambda (a b c) ...)

;-)

Peter

# Breton Slivka (15 years ago)

Hey, I like that. In the spirit of fun, here's something that does something weird and random.

var fsm = { r: {|| Math.random()<0.5}, f: {|a| print(a); ( fsm.r() ? fsm.s : fsm.r() ? fsm.m : {||} )("f");}, s: {|a| print(a); ( fsm.r() ? fsm.m : fsm.r() ? fsm.f : {||} )("s") }, m: {|a| print(a); ( fsm.r() ? fsm.f : fsm.r() ? fsm.s : {||} )("m");} } fsm.f();

# Brendan Eich (15 years ago)

On Nov 29, 2008, at 11:33 PM, Peter Michaux wrote:

This would allow writing

var foo = {|a, b, c| ...};

which could be written even shorter as

var foo(a, b, c) {...};

Why ever would you want to use 'var' there? Could foo be reassigned to
denote 42, or "hello"?

Syntax for quite different semantic effects should be composable,
possibly with compound forms (short-hands for clichéd patterns). But
it would be wrong to conflate two unrelated ideas (declare a variable;
define a function) if no use-case wants the full combination.

Yes, ES1 didn't separate function from var enough in this regard, but
that's a bug (addressed by strict mode in ES3.1).

Some languages use def to introduce constants including constant
function bindings. So I could see an argument for const foo(a,b,c)
{...}. But not for var.

The later is more consistent with the following which would also be necessary at times.

lambda(a, b, c) {...}

Necessary for what use-case?

# Peter Michaux (15 years ago)

On Sat, Nov 29, 2008 at 11:53 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Nov 29, 2008, at 11:33 PM, Peter Michaux wrote:

This would allow writing

var foo = {|a, b, c| ...};

which could be written even shorter as

var foo(a, b, c) {...};

Why ever would you want to use 'var' there? Could foo be reassigned to denote 42, or "hello"?

Yes it could be reassigned.

It is analogous to

(define (foo a b c) ...)

Could also write

var foo(a, b, c) {...} let foo(a, b, c) {...} const foo(a, b, c) {...}

The later is more consistent with the following which would also be necessary at times.

lambda(a, b, c) {...}

Necessary for what use-case?

I believe the same ones as for {|a, b, c| ...}. For example, if one wants to pass an anonymous lambda to another lambda.


If this is a more serious thread about syntax, I think all the callable things should have semetrical declaration syntax

lambda(a, b, c) {...} function(a, b, c) {...} class(a, b, c) {...}

Especially the syntax for lambda and function declarations seem as though they should be symmetrical because they will be called the same way: "foo()". This also makes me thing that class's should be called the same way. That is, the "new" should be optional just like it is for Array constructor, for example.

There is beauty in symmetry and nature has shown over and over again that symmetry is a sign of good fitness. I think this is widely known to be related to Lisp and its strengths. One interesting thing about Lisp is that s-expressions were not the intended end syntax. M-expressions were going to be more like languages which have "syntax". Once the power enabled by the symmetry of s-expressions was subsequently discovered it was so persuasive that they stuck.

By adding more syntax ES could be digging a deeper hole which favors syntax over other semantically useful ideas.

Peter

# Brendan Eich (15 years ago)

On Nov 30, 2008, at 12:28 AM, Peter Michaux wrote:

On Sat, Nov 29, 2008 at 11:53 PM, Brendan Eich <brendan at mozilla.com>
wrote:

On Nov 29, 2008, at 11:33 PM, Peter Michaux wrote:

This would allow writing

var foo = {|a, b, c| ...};

which could be written even shorter as

var foo(a, b, c) {...};

Why ever would you want to use 'var' there? Could foo be reassigned
to denote 42, or "hello"?

Yes it could be reassigned.

You didn't say why, though -- what is the use-case?

The later is more consistent with the following which would also be necessary at times.

lambda(a, b, c) {...}

Necessary for what use-case?

I believe the same ones as for {|a, b, c| ...}. For example, if one wants to pass an anonymous lambda to another lambda.

But why do you need to start such a lambda expression with a Greek
letter-name keyword?

Function expressions and definitions in ES3 start with 'function',
true enough. Allen's lambda syntax is simply an alternative proposal
for the expression case. It doesn't make a compound lambda + binding
form.

lambda(a, b, c) {...} function(a, b, c) {...} class(a, b, c) {...}

Especially the syntax for lambda and function declarations seem as though they should be symmetrical because they will be called the same way: "foo()".

This seems good as far as it goes, but the 'lambda' keyword is
overlong and obscure to some (I already agreed with you that it's le
mot juste in a previous thread; but Allen's proposal avoids any mot at
all, in favor of punctuation).

But why have three defining forms for callables? Sure, compatibility
may require two. But three? The weakest link here is class, without
more motivation (which I believe exists, see below).

This also makes me thing that class's should be called the same way. That is, the "new" should be optional just like it is for Array constructor, for example.

Some constructors behave differently when called. This could be
considered a rare quirk but (1) people like doing it in their own
abstractions; (2) it's desirable for self-hosting the quirky built-ins.

But again, a class is not just another kind of function. It's a higher- integrity factory for higher-integrity instances. Whether one calls it
via new or directly as a function is secondary. Its syntax can differ
from function if necessary (not gratuitously).

IOW I think your argument oversimplifies.

I believe that TC39 agrees that classes should be generative (nesting
a class in another class or function makes a new, distinct class
"type" on each evaluation), and (unrelated point) optionally anonymous
as your list shows. But this does not follow simply from borrowed/ desugared-to function syntax or semantics. Classes may grow hair that
functions won't, and lambdas must not (if they are to be worthy of the
name).

# Peter Michaux (15 years ago)

On Sun, Nov 30, 2008 at 12:46 AM, Brendan Eich <brendan at mozilla.com> wrote:

On Nov 30, 2008, at 12:28 AM, Peter Michaux wrote:

On Sat, Nov 29, 2008 at 11:53 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Nov 29, 2008, at 11:33 PM, Peter Michaux wrote:

This would allow writing

var foo = {|a, b, c| ...};

which could be written even shorter as

var foo(a, b, c) {...};

Why ever would you want to use 'var' there? Could foo be reassigned to denote 42, or "hello"?

Yes it could be reassigned.

You didn't say why, though -- what is the use-case?

Currently it is relatively common to reassign to a variable holding a reference to a function. I don't know of cases where I want to assign an integer to a variable that references a function; however, reassigning a function to a variable referencing a function is common.

// file 1 : the program

var foo = function() {}

// file 2 : the debugging program

var foo = (function() { var original = foo; return function() { alert('debugging'); return original(); }; })();

I wrote a layered XMLHttpRequest library which used the above idea of wrapping and reassigning.

There is also the lazy function definition pattern.

peter.michaux.ca/articles/lazy-function-definition-pattern

The later is more consistent with the following which would also be necessary at times.

lambda(a, b, c) {...}

Necessary for what use-case?

I believe the same ones as for {|a, b, c| ...}. For example, if one wants to pass an anonymous lambda to another lambda.

But why do you need to start such a lambda expression with a Greek letter-name keyword?

It isn't necessary to start with a Greek letter-name keyword. It could be

gratitouslyDifferentNameWhichIgnoresLongStandingPrecedenceForUsingLambda(a, b, c) {...}

Hey, you said this thread was for fun! ;-)

All I meant was that if there was a short form

var foo(a, b, c) {...}

there would still need to be a lambda expression.

Function expressions and definitions in ES3 start with 'function', true enough. Allen's lambda syntax is simply an alternative proposal for the expression case. It doesn't make a compound lambda + binding form.

lambda(a, b, c) {...} function(a, b, c) {...} class(a, b, c) {...}

Especially the syntax for lambda and function declarations seem as though they should be symmetrical because they will be called the same way: "foo()".

This seems good as far as it goes, but the 'lambda' keyword is overlong

It is no different in the amount of typing. Both

lambda(){}

and

{||}

both take only two characters (e.g. ell tab) to type in any editor with snippets.

Even without snippets, I simply don't think "lambda" is very long. In fact, 99% of the time I type "function() {};" manually because I rarely remember eff tab will do the same thing for me and it doesn't bother me at all.

and obscure to some

Yes obscure at first but once they learn about lambda and its history, don't you think they will cherish the privilege to use it?

(I already agreed with you that it's le mot juste in a previous thread; but Allen's proposal avoids any mot at all, in favor of punctuation).

I like the "lambda" version and think the syntax fits in nicely and shows how lambdas and functions are related semantically.

But why have three defining forms for callables? Sure, compatibility may require two. But three? The weakest link here is class, without more motivation (which I believe exists, see below).

This also makes me thing that class's should be called the same way. That is, the "new" should be optional just like it is for Array constructor, for example.

Some constructors behave differently when called.

Yes.

This could be considered a rare quirk

Definitely.

but (1) people like doing it in their own abstractions;

They do?!

(2) it's desirable for self-hosting the quirky built-ins.

Yes.

But again, a class is not just another kind of function. It's a higher-integrity factory for higher-integrity instances. Whether one calls it via new or directly as a function is secondary. Its syntax can differ from function if necessary (not gratuitously).

Yes it could be different. Since Java has dispatch on argument type and hence can have multiple constructors, it wouldn't make much sense to have class(){} in Java.

IOW I think your argument oversimplifies. I believe that TC39 agrees that classes should be generative (nesting a class in another class or function makes a new, distinct class "type" on each evaluation),

I agree with this requirement. I'm not in favor of type-checking because I don't think it will work well in ES (not even as well as it doesn't work in other languages) but it will be interesting to see how type-checking is proposed to work with this requirement.

and (unrelated point) optionally anonymous as your list shows. But this does not follow simply from borrowed/desugared-to function syntax or semantics. Classes may grow hair that functions won't, and lambdas must not (if they are to be worthy of the name).

Indeed.

Peter

# Chris Pine (15 years ago)

Brendan Eich wrote:

// Instead of lambda (a, b, c) { ... }, why not: { |a, b, c| ... } ?

Looks like Ruby to me, so you know I love it. :)

Nice lambda syntax really matters. If that tool is too heavy, I only use it half as often as I should.

Chris

# Peter Michaux (15 years ago)

On Mon, Dec 1, 2008 at 2:06 AM, Chris Pine <chrispi at opera.com> wrote:

Brendan Eich wrote:

// Instead of lambda (a, b, c) { ... }, why not: { |a, b, c| ... } ?

Looks like Ruby to me, so you know I love it. :)

Nice lambda syntax really matters. If that tool is too heavy, I only use it half as often as I should.

What is to be made of that last sentence? If you have a choice between the following, The "lambda" version is still shorter.

function() {} lambda() {}

Peter

# Chris Pine (15 years ago)

Peter Michaux wrote:

On Mon, Dec 1, 2008 at 2:06 AM, Chris Pine <chrispi at opera.com> wrote:

Brendan Eich wrote:

// Instead of lambda (a, b, c) { ... }, why not: { |a, b, c| ... } ? Looks like Ruby to me, so you know I love it. :)

Nice lambda syntax really matters. If that tool is too heavy, I only use it half as often as I should.

What is to be made of that last sentence? If you have a choice between the following, The "lambda" version is still shorter.

function() {} lambda() {} {||}

Not from where I'm sitting... 'lambda' is shorter than 'function', but '{||}' is shortest of all... or perhaps I don't understand you?

Chris

# P T Withington (15 years ago)

On 2008-11-30, at 01:30EST, Brendan Eich wrote:

// Instead of lambda (a, b, c) { ... }, why not: { |a, b, c| ... } ?

I would rather have a more literate syntax, lest we degenerate to
where practically any comic book blasphemy is a valid program.

(BTW, I'm pretty sure I have that same Byte issue, in a similar box,
with a similar musty smell, and "the blue book". Back then,
worrying that 'line noise' or the death throes of your modem hanging
up would write code for you was a legitimate concern. Today, it is
just my old eyes that might gloss over {|| and wonder why the vars
in that block are not visible in the enclosing function...)

# Mark S. Miller (15 years ago)

On Mon, Dec 1, 2008 at 7:31 AM, P T Withington <ptw at pobox.com> wrote:

On 2008-11-30, at 01:30EST, Brendan Eich wrote:

// Instead of lambda (a, b, c) { ... }, why not:

{ |a, b, c| ... } ?

I would rather have a more literate syntax, lest we degenerate to where practically any comic book blasphemy is a valid program.

(BTW, I'm pretty sure I have that same Byte issue, in a similar box, with a similar musty smell, and "the blue book". Back then, worrying that 'line noise' or the death throes of your modem hanging up would write code for you was a legitimate concern. Today, it is just my old eyes that might gloss over {|| and wonder why the vars in that block are not visible in the enclosing function...)

Since it's a lambda, the 'var's will be visible in the enclosing function.

The point of having a very compact syntax for lambda is too make it pleasant to write control abstractions, as one does casually in Smalltalk. With the verbose "lambda" spelling, people will avoid those, or invent macro systems (as Scheme programmers do) mostly so they can avoid seeing all those extra "lambda" letters in the code.

Think of lambdas as blocks plus a bit more, rather than function minus a bit. Viewed this way, their block-like syntax is a virtue.

# P T Withington (15 years ago)

On 2008-12-01, at 11:30EST, Mark S. Miller wrote:

On Mon, Dec 1, 2008 at 7:31 AM, P T Withington <ptw at pobox.com> wrote:

On 2008-11-30, at 01:30EST, Brendan Eich wrote:

// Instead of lambda (a, b, c) { ... }, why not:

{ |a, b, c| ... } ?

I would rather have a more literate syntax, lest we degenerate to
where practically any comic book blasphemy is a valid program.

(BTW, I'm pretty sure I have that same Byte issue, in a similar
box, with a similar musty smell, and "the blue book". Back then, worrying
that 'line noise' or the death throes of your modem hanging up would write
code for you was a legitimate concern. Today, it is just my old eyes that might
gloss over {|| and wonder why the vars in that block are not visible
in the enclosing function...)

Since it's a lambda, the 'var's will be visible in the enclosing
function.

Eh? So:

function () { var foo = 42; {|| var foo = 3; } return foo; }

and:

function () { var foo = 42; { var foo = 3; } return foo; }

Give the same answer?

The point of having a very compact syntax for lambda is too make it
pleasant to write control abstractions, as one does casually in Smalltalk.
With the verbose "lambda" spelling, people will avoid those, or invent macro
systems (as Scheme programmers do) mostly so they can avoid seeing all those
extra "lambda" letters in the code.

Think of lambdas as blocks plus a bit more, rather than function
minus a bit. Viewed this way, their block-like syntax is a virtue.

I agree with the goal of compactness. I just don't like it to be too
compact. Call me a curmudgeon. I don't like that not is spelt !
or that it is so easy to make a one-letter misspelling of eql and
end up with setq either.

# Mark S. Miller (15 years ago)

On Mon, Dec 1, 2008 at 8:47 AM, P T Withington <ptw at pobox.com> wrote:

Eh? So:

function () { var foo = 42; {|| var foo = 3; } return foo; }

and:

function () { var foo = 42; { var foo = 3; } return foo; }

Give the same answer?

No, because you forgot to call it.

function () { var foo = 42; {|| var foo = 3; }(); return foo; }

and:

function () { var foo = 42; { var foo = 3; } return foo; }

do give the same answer.

# P T Withington (15 years ago)

On 2008-12-01, at 11:54EST, Mark S. Miller wrote:

On Mon, Dec 1, 2008 at 8:47 AM, P T Withington <ptw at pobox.com> wrote:

Eh? So:

function () { var foo = 42; {|| var foo = 3; } return foo; }

and:

function () { var foo = 42; { var foo = 3; } return foo; }

Give the same answer?

No, because you forgot to call it.

Cool. So I can use {|| and } to comment out blocks of code... :P

function () { var foo = 42; {|| var foo = 3; }(); return foo; }

and:

function () { var foo = 42; { var foo = 3; } return foo; }

do give the same answer.

Ok, your suggestion of 'block plus' not 'function minus' is making
more sense to me. Still hard for me to understand the newspeak.
Trying to wrap my mind around var meaning "free"; I think I get it.
But, I still think {|| looks like my cat stepped on the keyboard.

# Peter Michaux (15 years ago)

On Mon, Dec 1, 2008 at 7:31 AM, P T Withington <ptw at pobox.com> wrote:

On 2008-11-30, at 01:30EST, Brendan Eich wrote:

// Instead of lambda (a, b, c) { ... }, why not: { |a, b, c| ... } ?

I would rather have a more literate syntax, lest we degenerate to where practically any comic book blasphemy is a valid program.

I agree with this sentiment. The phrase "ASCII vomit" comes to mind and becomes a worry.

Take an ES program and replace all if-else with ?: and then most functions with {||} and it starts to look quite cryptic.

A reader would have a difficult time even knowing what to look up in a book index to find out what the {||} that they see in some code is supposed to be. It wouldn't necessarily be clear that the {} and the || are related.

Peter

# Brendan Eich (15 years ago)

On Dec 1, 2008, at 10:17 AM, Peter Michaux wrote:

On Mon, Dec 1, 2008 at 7:31 AM, P T Withington <ptw at pobox.com> wrote:

On 2008-11-30, at 01:30EST, Brendan Eich wrote:

// Instead of lambda (a, b, c) { ... }, why not: { |a, b, c| ... } ?

I would rather have a more literate syntax, lest we degenerate to
where practically any comic book blasphemy is a valid program.

I agree with this sentiment. The phrase "ASCII vomit" comes to mind and becomes a worry.

I don't think it's a big worry. It seems to me ES3 (never mind new
syntax) is far from Perl, but not nearly Python or an even more
pedagogical language in which

 There should be one-- and preferably only one --obvious way to do  

it.

There are already several ways to say the same thing, for many values
of "thing", in JS.

Take an ES program and replace all if-else with ?: and then most functions with {||} and it starts to look quite cryptic.

But functions remain. I doubt lambdas in any synax will replace them.

A reader would have a difficult time even knowing what to look up in a book index to find out what the {||} that they see in some code is supposed to be. It wouldn't necessarily be clear that the {} and the || are related.

A novice reader, you mean. Readers who know the language may prefer
brevity. This is not a simple "read over write" optimization issue.

# Peter Michaux (15 years ago)

On Mon, Dec 1, 2008 at 10:24 AM, Brendan Eich <brendan at mozilla.com> wrote:

On Dec 1, 2008, at 10:17 AM, Peter Michaux wrote:

Take an ES program and replace all if-else with ?: and then most functions with {||} and it starts to look quite cryptic.

But functions remain. I doubt lambdas in any synax will replace them.

Actually, I think the combination of Object.create, lambda, rest parameters and class will probably eliminate function and prototype from most code.

lambda will be slightly faster and lighter than function. Programmers like faster and lighter when it comes at no cost.

People seem to want to think in terms of classes and not the prototype property of a function object. Colin Moock's book on AS2 recommended against using prototype after the introduction of class to that language.

Peter

# Allen Wirfs-Brock (15 years ago)

Just to clarify some speculation, the syntax I proposed ({||}) was solely inspired by Smalltalk and tempered by the parsing realities of a C-like syntax. Any similarities to Ruby constructs are probably examples of parallel evolution under similar environmental pressures. I suspect that designers of other languages with C-like syntax (C# comes to mind with its () => expr "lambda" syntax) did not have the experience or goal of using closures to create control abstractions (which often requires passing multi-statement closures) and so arrived at a more function-like concise closure syntax.

(more below)

-----Original Message----- From: es-discuss-bounces at mozilla.org [mailto:es-discuss- bounces at mozilla.org] On Behalf Of Peter Michaux Sent: Monday, December 01, 2008 10:17 AM To: P T Withington ...

// Instead of lambda (a, b, c) { ... }, why not: { |a, b, c| ... } ?

I would rather have a more literate syntax, lest we degenerate to where practically any comic book blasphemy is a valid program.

I agree with this sentiment. The phrase "ASCII vomit" comes to mind and becomes a worry.

The use of {} as grouping syntax is inherent in the early (and irrevocable) decision for JavaScript to use a C-like syntax rather than an Algol/Pascal or Lisp like syntax. We already overload {} to bracket both statement blocks and object constructors so it doesn't seem too burdensome to have another syntactically distinct form that from some perspectives is the semantic union of the other two forms.

JavaScript already has a concise "ASCII vomit" syntax for constructing arrays and objects. Given the direction the language seems to be heading, having a similar concise syntax for constructing closures seems quite appropriate.

# Peter Michaux (15 years ago)

On Mon, Dec 1, 2008 at 12:19 PM, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:

JavaScript already has a concise "ASCII vomit" syntax for constructing arrays and objects.

I didn't mean to imply that JavaScript currently deserves the "ASCII vomit" label. I also didn't mean to imply the {||} was so bad as to deserve name calling. It is more the potential for the cumulative effect to cause trouble.

Given the direction the language seems to be heading,

Can you expand on what you mean by that please?

having a similar concise syntax for constructing closures seems quite appropriate.

Peter

# Maciej Stachowiak (15 years ago)

On Nov 29, 2008, at 10:30 PM, Brendan Eich wrote:

At the TC39 meeting two weeks ago in Kona, we had a brief
bikeshedding discussion about lambda syntax and why it matters.
Observation: blocks in Smalltalk being lightweight means users don't
mind writing them for control abstractions, compared to JS functions
in ES3. In Smalltalk, ignoring JS, it's hard to beat [ and ] as
overhead, although one must count the message selector and its
punctuation too.

Allen Wirfs-Brock put his proposal, which will not shock you who
know Smalltalk or Allen, on the whiteboard:

// Instead of lambda (a, b, c) { ... }, why not: { |a, b, c| ... } ?

I like the brevity, but having the formals inside the block and in ||
delimiters seems like it will look weird in an ECMAScript program. For
function declarations the parameters go in parentheses, and for calls
(presumably also for lambda calls), the arguments go in parens. If
brevity is important, why not lift the lambda syntax from modern pure
functional languages:

(a, b, c) { ... }

That's backslash as a lambda operator. This has one more character
than your version, but will make formals and parameters look the same
for functions, lambdas, and calls, and will avoid putting the formals
inside the body which I think is confusing and visually hard to scan.

, Maciej

# Breton Slivka (15 years ago)

Question: How would I write a recursive function with that syntax? Is there a way to name the lambda, other than var = {||}; ?

# P T Withington (15 years ago)

On 2008-12-01, at 15:59EST, Maciej Stachowiak wrote:

(a, b, c) { ... }

+1

# Allen Wirfs-Brock (15 years ago)

{|a,b,c| ...} or (a,b,c) {...} or {(a,b,c) ...}

I could be happy with any of them and can find pros and cons with each. I think the high order bit should be that a concise closure syntax is possible and desirable. If we agree on that then we just need to pick one.

The use of \ slightly bothers me because it is takes a character that now is exclusively used in the lexical (token) grammar ( Unicode escapes, string escapes, line continuations) and gives it syntactic significance. This is probably not a fatal flaw but it would mean that the lexical uses of \ become less visually distinctive.

Whether someone prefers the parameters inside or outside the braces may be another symptom of whether they are focusing on control abstraction or functional abstraction. With control abstraction you use closures as "blocks" of code that form pieces of the abstraction so it may seems natural from that perspective for the braces to surround the entire "block". This is closer to the syntactic feel of the built-in control statements. If you are building functional abstractions then you are probably thinking about the closures as functions so placing the formals before the body seems natural.

It's fairly common with control abstractions to use 0 argument closures so the readability of {||...}, (){...}, and {()...} are probably also worth considering. Ideally a 0 argument closure would be written as {...} but resolution of the syntactic ambiguities between object literal and such closures (particularly in the presence of statement labels) seems too difficult to contend with.

My focus on supporting control abstraction may be mute. While Smalltalk and Ruby show that power of such abstraction mechanisms it takes more than just a concise block-like closure notation to achieve it. The complexity of the BGGA Java closure proposal (and the associated controversy) shows how hard it can be to fully support control abstraction using C syntax (and, of course, Java semantics).

# Douglas Crockford (15 years ago)

Allen Wirfs-Brock wrote:

{|a,b,c| ...} or (a,b,c) {...} or {(a,b,c) ...}

The use of \ slightly bothers me because it is takes a character that now is exclusively used in the lexical (token) grammar ( Unicode escapes, string escapes, line continuations) and gives it syntactic significance. This is probably not a fatal flaw but it would mean that the lexical uses of \ become less visually distinctive.

The language overuses / recklessly, and still it didn't die. I suppose it could survive the overuse of \ as well.

Is recursion still desirable in this form. If so, then of the three I like

 \(a,b,c) {}

because you can think of the \ as being an abbreviation of function.

 \ name(a,b,c) {}

Just don't start your function name with u.

# Felix (15 years ago)

@ is unused @(a, b, c){}

# Brendan Eich (15 years ago)

On Dec 1, 2008, at 3:47 PM, Felix wrote:

@ is unused @(a, b, c){}

@ is already used by ECMA-357 (E4X). Also some tradition of denoting
dereference (Cyclone, managed C++ IIRC).

Hard to beat \ if we want the parenthesized formal parameter list in
front.

# Breton Slivka (15 years ago)

Is recursion still desirable in this form. If so, then of the three I like

(a,b,c) {}

because you can think of the \ as being an abbreviation of function.

\ name(a,b,c) {}

Just don't start your function name with u.

well if we're thinking about lambdas as blocks++, then why not

name: {|a,b,c| }

Since we already have labeled blocks. This also slides neatly into an object literal

{ name: {|a,b,c| } }

Or does this horribly break parsing again?

# Felix (15 years ago)

oh, right, forgot e4x.

ok, one objection to (){} is that it looks too much like a function. in particular, it "feels like" I should write var a = (x){ return x; }; but that isn't right, {||} is sufficiently different that it "feels like" I should write var a = {|x| x};

# Allen Wirfs-Brock (15 years ago)

-----Original Message----- From: es-discuss-bounces at mozilla.org [mailto:es-discuss- bounces at mozilla.org] On Behalf Of Douglas Crockford ... because you can think of the \ as being an abbreviation of function.

\ name(a,b,c) {}

Just don't start your function name with u.

Exactly, that's why I didn't mention this possibility. I don't think it would be acceptable to have that \u ambiguity.

Smalltalk does not have a way (other than variable assignment) to name closures for recursive reference and it rarely has proven to be a problem. If you really want to pass a recursive lambda to a function g you could define it using a const: const fact = {|n| (n<=1) ? 1 : fact(n-1)* n}; g(fact) or more perversely: g({||const fact = {|n| (n<=1) ? 1 : fact(n-1)* n}; fact}() );

(the second statement of the outer lambda wouldn't be needed if const evaluates to the value of its initialization expression)

# Maciej Stachowiak (15 years ago)

On Dec 1, 2008, at 2:32 PM, Allen Wirfs-Brock wrote:

{|a,b,c| ...} or (a,b,c) {...} or {(a,b,c) ...}

I could be happy with any of them and can find pros and cons with
each. I think the high order bit should be that a concise closure
syntax is possible and desirable. If we agree on that then we just
need to pick one.

The use of \ slightly bothers me because it is takes a character
that now is exclusively used in the lexical (token) grammar
( Unicode escapes, string escapes, line continuations) and gives it
syntactic significance. This is probably not a fatal flaw but it
would mean that the lexical uses of \ become less visually
distinctive.

Whether someone prefers the parameters inside or outside the braces
may be another symptom of whether they are focusing on control
abstraction or functional abstraction. With control abstraction you
use closures as "blocks" of code that form pieces of the abstraction
so it may seems natural from that perspective for the braces to
surround the entire "block". This is closer to the syntactic feel of
the built-in control statements. If you are building functional
abstractions then you are probably thinking about the closures as
functions so placing the formals before the body seems natural.

Can you give an example? Since ECMAScript's built-in control
structures already have special syntax, it's hard to make anything
look exactly like them.

Here's my best shot at an example. Let's pretend you have a
withOpenFile function that opens a file, does some work using the
resulting file handle (assuming there were such things in some host
extension), and guarantees the file handle will be closed whether on
normal exit or exception, but passing exceptions through. How would a
site of use look?

withOpenFile(fileName, { |handle| doSomething(handle); doSomethingElse(handle); });

withOpenFile(fileName, (handle) { doSomething(handle); doSomethingElse(handle); });

Both look a little odd but I'd say the latter resembles built-in
constructs like for..in a tiny bit more.

It's fairly common with control abstractions to use 0 argument
closures so the readability of {||...}, (){...}, and {()...} are
probably also worth considering. Ideally a 0 argument closure would
be written as {...} but resolution of the syntactic ambiguities
between object literal and such closures (particularly in the
presence of statement labels) seems too difficult to contend with.

Perhaps with the backslash idea, a 0-argument lambda could have a
short form of just \ {...}. I'm trying to think of a useful control
construct that takes thunks. Maybe implementing memoized lazy lists, a
la scheme, so you can do stuff like:

lazyPair(val1, \ { computeRestOfList(); } );

var fibonacciNumbers = \ { let fib = (a, b) { lazyPair(b, \ { fib(b, a + b) }) }; lazyPair(0, fib(0, 1)); }();

(I'm skipping the definition of lazyPair and the lazyCar / lazyCdr
you'd need to walk the stream).

Seems adequately readable.

For comparison:

lazyPair(val1, { || computeRestOfList(); } );

var fibonacciNumbers = { || let fib = { |a, b| lazyPair(b, { || fib(b, a + b) }) }; lazyPair(0, fib(0, 1)); }();

Maybe it's just lack of familiarity but the second version seems less
readable and natural to me.

My focus on supporting control abstraction may be mute. While
Smalltalk and Ruby show that power of such abstraction mechanisms it
takes more than just a concise block-like closure notation to
achieve it. The complexity of the BGGA Java closure proposal (and
the associated controversy) shows how hard it can be to fully
support control abstraction using C syntax (and, of course, Java
semantics).

I think it's possible to build useful control abstractions with a
concise block-like syntax. But an important factor here is making
these look natural in the context of the language as well as concise.
Ruby and Smalltalk have overall designs that make them friendly to a
smalltalkish block syntax, so your custom control constructs look just
like the built-in ones (or conversely you could claim none are built
in). In ECMAScript you can't have them look exactly the same, but I
think the Haskellish backslash style fits in a little better.

, Maciej

# Maciej Stachowiak (15 years ago)

On Dec 1, 2008, at 5:37 PM, Allen Wirfs-Brock wrote:

-----Original Message----- From: es-discuss-bounces at mozilla.org [mailto:es-discuss- bounces at mozilla.org] On Behalf Of Douglas Crockford ... because you can think of the \ as being an abbreviation of function.

\ name(a,b,c) {}

Just don't start your function name with u.

Exactly, that's why I didn't mention this possibility. I don't
think it would be acceptable to have that \u ambiguity.

I think requiring a space after the \ if a name is provided would
remove the potential ambiguity.

Smalltalk does not have a way (other than variable assignment) to
name closures for recursive reference and it rarely has proven to be
a problem. If you really want to pass a recursive lambda to a
function g you could define it using a const: const fact = {|n| (n<=1) ? 1 : fact(n-1)* n}; g(fact) or more perversely: g({||const fact = {|n| (n<=1) ? 1 : fact(n-1)* n}; fact}() );

(the second statement of the outer lambda wouldn't be needed if
const evaluates to the value of its initialization expression)

g(\ fact(n) { (n<=1) ? 1 : fact(n-1) * n });

That seems sweeter.

# Peter Michaux (15 years ago)

On Mon, Dec 1, 2008 at 5:37 PM, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:

-----Original Message----- From: es-discuss-bounces at mozilla.org [mailto:es-discuss- bounces at mozilla.org] On Behalf Of Douglas Crockford ... because you can think of the \ as being an abbreviation of function.

\ name(a,b,c) {}

Just don't start your function name with u.

Exactly, that's why I didn't mention this possibility. I don't think it would be acceptable to have that \u ambiguity.

I agree that the ambiguity is not acceptable. It is too confusing.

Peter

# David-Sarah Hopwood (15 years ago)

Breton Slivka wrote:

Is recursion still desirable in this form. If so, then of the three I like

(a,b,c) {}

because you can think of the \ as being an abbreviation of function.

\ name(a,b,c) {}

Just don't start your function name with u.

Too ambiguous, even with the space.

well if we're thinking about lambdas as blocks++, then why not

name: {|a,b,c| }

Since we already have labeled blocks.

Because '{|a,b,c| }' is an expression, and labelling an ExpressionStatement already has a different meaning.

# Allen Wirfs-Brock (15 years ago)

Below

-----Original Message----- From: Maciej Stachowiak [mailto:mjs at apple.com] Can you give an example? Since ECMAScript's built-in control structures already have special syntax, it's hard to make anything look exactly like them.

Here's my best shot at an example. Let's pretend you have a withOpenFile function that opens a file, does some work using the resulting file handle (assuming there were such things in some host extension), and guarantees the file handle will be closed whether on normal exit or exception, but passing exceptions through. How would a site of use look?

withOpenFile(fileName, { |handle| doSomething(handle); doSomethingElse(handle); });

withOpenFile(fileName, (handle) { doSomething(handle); doSomethingElse(handle); });

Both look a little odd but I'd say the latter resembles built-in constructs like for..in a tiny bit more.

Just so everybody knows where I'm coming from with this, in Smalltalk this might look something like:

FileHandle forName: filename do: [:handle| self dosomething: handle. Self doSomethingElse: handle]

Which is syntactically very similar to the "built-in" control constructs such as:

1 to: max do: [:n| self doSomething: n].

The BGGA Java closure proposal attempts to support control abstractions that look like built in control constructs by allowing trailing literal closure arguments to appear after the parenthesized argument list (shades of Ruby). If we did something similar in JavaScript you might then code your example as:

withOpenFile(fileName) { |handle| doSomething(handle); doSomethingElse(handle); };

which (to me) feels a little more like a built-in control structure than

withOpenFile(fileName) {handle) { doSomething(handle); doSomethingElse(handle); };

However, neither one is perfect. The Java closure proposal has some additional syntactic embellishments that attempt to make it even a bit closer but in my opinion they may have exceeded to point of diminishing returns.

...

I think it's possible to build useful control abstractions with a concise block-like syntax. But an important factor here is making these look natural in the context of the language as well as concise. Ruby and Smalltalk have overall designs that make them friendly to a smalltalkish block syntax, so your custom control constructs look just like the built-in ones (or conversely you could claim none are built in). In ECMAScript you can't have them look exactly the same, but I think the Haskellish backslash style fits in a little better.

I largely agree with this view. Preserving the existing feel of a language is very important and it is hard to retrofit control abstraction facilities into a language that didn't have them in its initial design. Neither the backslash or vertical bar syntax is a perfect fit with the existing language and I could live with either although I personally like the vertical bar's homage to Smalltalk.

# Peter Michaux (15 years ago)

On Mon, Dec 1, 2008 at 9:33 PM, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:

The BGGA Java closure proposal attempts to support control abstractions that look like built in control constructs by allowing trailing literal closure arguments to appear after the parenthesized argument list (shades of Ruby). If we did something similar in JavaScript you might then code your example as:

withOpenFile(fileName) { |handle| doSomething(handle); doSomethingElse(handle); };

I find the idea that a lambda comes after the argument list, but that it is somehow still an argument, an inconsistent and bad idea. I've used it in Ruby and don't like it there. In Ruby, I believe these trailing {||} "blocks" are the only place where the {||} syntax will create a lambda and so there is appeal to those who like the extremely compact notation. They cannot get that compact notation elsewhere. In ES we won't have that restriction.

One problem is if the argument list needs to take two lambdas, then where would the second lambda go? One inside the arguments list and one after? Why is one lambda more important or deserving of a special place compared with the other lambda?

Some API designers won't like the trailing lambda syntax and some will. Then we end up with inconsistent APIs. If all arguments go in the argument list then this problem will not arise.

The following is better

withOpenFile(fileName, {|handle| doSomething(handle); doSomethingElse(handle); });

and the following is better still

withOpenFile(fileName, lambda(handle) { doSomething(handle); doSomethingElse(handle); });

Peter

# Eric Suen (15 years ago)

What about:

.(a,b,c) {}

or

..(a,b,c) {}

or

...(a,b,c) {}

,

Eric Suen

----- Original Message ---

# Aaron Gray (15 years ago)

i still prefer 'lambda (a,b,c) { ... }' as it is readable to the uninitiated and can then at least give a handle for someone to lookup.

# Maciej Stachowiak (15 years ago)

On Dec 2, 2008, at 5:31 AM, Aaron Gray wrote:

i still prefer 'lambda (a,b,c) { ... }' as it is readable to the
uninitiated and can then at least give a handle for someone to lookup.

I think the truly uninitiated would not find "lambda" any more obvious
in meaning than "" or "||".

, Maciej

# Maciej Stachowiak (15 years ago)

On Dec 2, 2008, at 5:03 AM, Eric Suen wrote:

What about:

.(a,b,c) {}

or

..(a,b,c) {}

or

...(a,b,c) {}

As long as we're giving the bikeshed a few more coats of paint,
Objective-C is adding block-like closures with ^ as the prefix, so we
could take inspiration from that:

^(a, b, c) { ... }

I'm not sure if this would introduce ambiguities in the grammar since
"^" is already an operator; I tend to think not, because "+" and "-"
manage to be both unary and binary operators without ambiguity. "^"
also has a slight resemblance to the greek lambda, which is the reason
Haskell uses "". This could support named lambdas without risk of
clashing with the \u escape.

, Maciej

# P T Withington (15 years ago)

On 2008-12-02, at 11:48EST, Maciej Stachowiak wrote:

As long as we're giving the bikeshed a few more coats of paint,
Objective-C is adding block-like closures with ^ as the prefix, so
we could take inspiration from that:

^(a, b, c) { ... }

That's cute. Mnemonic for Λ (capital λ), also 'pointer-ish',
implying an object reference.

# Aaron Gray (15 years ago)

On Dec 2, 2008, at 5:31 AM, Aaron Gray wrote:

i still prefer 'lambda (a,b,c) { ... }' as it is readable to the uninitiated and can then at least give a handle for someone to lookup.

I think the truly uninitiated would not find "lambda" any more obvious in meaning than "" or "||".

People can google "lambda" they cannot google "", or "||". Also keywords seem better suited to Javascript syntax.

Aaron

# Eric Suen (15 years ago)

'~' is ambiguities usless you using two NO_LINE_BREAK like:

'~' NO_LINE_BREAK (...) NO_LINE_BREAK {...}

'.' may not looks good, but it does not introduce new token like '' or 'lambda'

,

Eric Suen

----- Original Message ---

# Jeff Watkins (15 years ago)

On 1 Dec, 2008, at 10:18 PM, Peter Michaux wrote:

One problem is if the argument list needs to take two lambdas, then where would the second lambda go? One inside the arguments list and one after? Why is one lambda more important or deserving of a special place compared with the other lambda?

I've been writing JS a long time and I don't frequently find myself
wanting to pass more than one function/lambda to another function. On
the (very) rare occasion when I do, I suspect I'm not calling a
control-structure-like function. Then I'd be comfortable passing two
lambdas as arguments.

Since the goal seems to be allowing control structures like Smalltalk
(yay!), how about specifying that one lambda that follows a function
invocation is passed as the final argument to the invocation. You can
write the invocation:

withNode(node, {|n| ... })

and I'll write it as:

withNode(node) { |n| ... }

Some API designers won't like the trailing lambda syntax and some will. Then we end up with inconsistent APIs.

I think if lambdas can be specified outside the parameter list, API
designers will feel pressure from those who use their APIs to adopt
the trailing lambda style. This would only be a significant issue when
the API is expecting a single lambda.

And regarding using the full term lambda in the syntax, I think I'm
not alone that I'd have a hard time adopting lambdas over functions
without a significant improvement in syntax -- and changing the name
from function to lambda isn't significant. Especially considering that
the initial implementations of lambdas might not hold a significant
performance improvement over functions.

Of course, it would also be super cool if I could write something like:

{ |n|

hide: function()
{
    n.style.display='none';
},

show: function()
{
    n.style.display='';
},

etc...

}

Of course if I didn't have to pay the cost of instantiating a new
function for every object instance, that would make something like
this worthwhile.

# Peter Michaux (15 years ago)

2008/12/2 Jeff Watkins <watkins at apple.com>:

Since the goal seems to be allowing control structures like Smalltalk (yay!), how about specifying that one lambda that follows a function invocation is passed as the final argument to the invocation.

Brendan seemed to reject the idea that "this" could be passed as the first argument to a constructor, so that "this" could be explicitly named, on the grounds that the parameter lists would not match. I agree that parameter lists should match. I was just asking.

esdiscuss/2008-November/008203


If a trailing block outside the parameter list is passed as the last argument, then what happens when there is a rest parameter as the last argument in the parameter list? It gets a bit messy to determine if the last argument passed in was the last argument of some rest parameters or an optional trailing lambda.

Peter

# Jeff Watkins (15 years ago)

On 2 Dec, 2008, at 10:28 AM, Peter Michaux wrote:

If a trailing block outside the parameter list is passed as the last argument, then what happens when there is a rest parameter as the last argument in the parameter list? It gets a bit messy to determine if the last argument passed in was the last argument of some rest parameters or an optional trailing lambda.

Er, yeah. That's a really good point. I suppose you could bake that
into the specification, but I think that way lies madness.

# Brian Kardell (15 years ago)

On Tue, Dec 2, 2008 at 10:36 AM, Aaron Gray <aaronngray.lists at googlemail.com

wrote:

On Dec 2, 2008, at 5:31 AM, Aaron Gray wrote:

i still prefer 'lambda (a,b,c) { ... }' as it is readable to the

uninitiated and can then at least give a handle for someone to lookup.

I think the truly uninitiated would not find "lambda" any more obvious in meaning than "" or "||".

People can google "lambda" they cannot google "", or "||". Also keywords seem better suited to Javascript syntax.

Aaron

Is the argument that the uninitiated should be able understand complex code concepts via a google search of the thing that they don't understand?

There are already all kinds of things that one couldn't understand via a simple google search for something in the code, and which people of various levels of experience might experience: Object literals come to mind or "||" and "&&" as guard/default, "?" as ternary if, or even "++" or "%=" and there are plenty more things which might or might not have a readily identifiable thing to allow someone to google for like default values, coercion rules, or scoping concepts.

Google is an excellent tool, but I don't think that it's necessarily a great way to interpret code into something understandable to the uninitiated. Doesn't seem like a good deciding factor.

# David-Sarah Hopwood (15 years ago)

Peter Michaux wrote:

2008/12/2 Jeff Watkins <watkins at apple.com>:

Since the goal seems to be allowing control structures like Smalltalk (yay!), how about specifying that one lambda that follows a function invocation is passed as the final argument to the invocation.

Brendan seemed to reject the idea that "this" could be passed as the first argument to a constructor, so that "this" could be explicitly named, on the grounds that the parameter lists would not match. I agree that parameter lists should match. I was just asking.

esdiscuss/2008-November/008203


If a trailing block outside the parameter list is passed as the last argument, then what happens when there is a rest parameter as the last argument in the parameter list? It gets a bit messy to determine if the last argument passed in was the last argument of some rest parameters or an optional trailing lambda.

Will ES-Harmony have labelled arguments? If it does then both of these problems go away, since the block argument can have a standard label, and similarly for the this argument.

# Peter Michaux (15 years ago)

On Tue, Dec 2, 2008 at 11:09 AM, David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk> wrote:

Peter Michaux wrote:

2008/12/2 Jeff Watkins <watkins at apple.com>:

Since the goal seems to be allowing control structures like Smalltalk (yay!), how about specifying that one lambda that follows a function invocation is passed as the final argument to the invocation.

Brendan seemed to reject the idea that "this" could be passed as the first argument to a constructor, so that "this" could be explicitly named, on the grounds that the parameter lists would not match. I agree that parameter lists should match. I was just asking.

esdiscuss/2008-November/008203


If a trailing block outside the parameter list is passed as the last argument, then what happens when there is a rest parameter as the last argument in the parameter list? It gets a bit messy to determine if the last argument passed in was the last argument of some rest parameters or an optional trailing lambda.

Will ES-Harmony have labelled arguments? If it does then both of these problems go away, since the block argument can have a standard label, and similarly for the this argument.

(David, These comments aren't particularly in response to yours but to the trailing lambda idea in general.)

This would lead to a "reserved label" which is adding yet another complication to enabled a trailing lambda after the parameter list.

The language already has a nice consistent way to pass parameters to a function: the parameters go inside the parameter list. Why add new syntax, a trailing lambda, basically for the sake of whim, with know consequences which require other changes to the language and likely unknown consequences, when the language can already do what is necessary? Using the currently available mechanism is something people already understand and works. The only difference is an LPAREN is in a different place. Is that worth all the problems and inconsistencies? I don't think so. I don't even think it is even aesthetically appealing which is supposedly the only real gain anyway.

Peter

# David-Sarah Hopwood (15 years ago)

Maciej Stachowiak wrote:

As long as we're giving the bikeshed a few more coats of paint, Objective-C is adding block-like closures with ^ as the prefix, so we could take inspiration from that:

^(a, b, c) { ... }

I'm not sure if this would introduce ambiguities in the grammar since "^" is already an operator; I tend to think not, because "+" and "-" manage to be both unary and binary operators without ambiguity.

Correct, it would be grammatically unambiguous. It might still be mildly confusing, though (unlike unary minus, which is related to subtraction by -x = 0-x, lambda abstraction is unrelated to bitwise exclusive-or). But any notation for lambdas is likely to be mildly confusing to the uninitiated.

"^" also has a slight resemblance to the greek lambda, which is the reason Haskell uses "".

"^" also has some historical relevance:

Felice Cardone, J. Roger Hindley, History of Lambda-calculus and Combinatory Logic 2006, Swansea University Mathematics Department Research Report No. MRRS-05-06. www-maths.swan.ac.uk/staff/jrh/papers/JRHHislamWeb.pdf

(By the way, why did Church choose the notation "λ"? In [Church, 1964, §2]

he stated clearly that it came from the notation "x̂" used for

class-abstraction by Whitehead and Russell, by first modifying "x̂" to

"∧x" to distinguish function-abstraction from class-abstraction, and

then changing "∧" to "λ" for ease of printing. This origin was also

reported in [Rosser, 1984, p.338]. On the other hand, in his later years

Church told two enquirers that the choice was more accidental: a symbol

was needed and "λ" just happened to be chosen.)

[Church, 1964] A. Church, 7 July 1964. Unpublished letter to Harald Dickson.

[Rosser, 1984] J. B. Rosser. Highlights of the history of the lambda calculus. Annals of the History of Computing, 6:337–349, 1984.

[If your email client doesn't display the above correctly: "x̂" is an x with a hat operator (similar to a circumflex) above it. "∧" is an upward-pointing wedge. "λ" is a lowercase Greek lambda.]

# Yuh-Ruey Chen (15 years ago)

I've been casually following this discussion, and I can't help but rail against the inherent limitations of ES's C heritage.

# Peter Michaux (15 years ago)

On Tue, Dec 2, 2008 at 4:08 PM, Yuh-Ruey Chen <maian330 at gmail.com> wrote:

(As a side note, sometimes I wonder if all this talk about sugar is moot, because I suspect more and more languages will start compiling down to ES.)

I keep a list of all the X->ES3 compilers I encounter in the middle of

the following page

peter.michaux.ca/articles/compiling-down-to-javascript-1-5

This list has grown quite long with the majority of popular and revered languages included. I wonder how many companies are using these technologies and if that group is growing very quickly. ES is "good enough" for most developers that I think the attraction is low given the extra difficulties in development process and compiled code speed (i.e. trampolines for tail calls are slow.)

If ES is the byte code of the browser then the better ES is, the better the compiled code can be. Tail calls and continuations would be valuable additions to ES. Threads are probably a trickier issue. If these features cannot be added to ES directly then it would be great if ES ran on a VM which exposed these features. Then all languages could compile to that VM's byte code. I vaguely recall a web page where Brendan Eich listed some of these features as part of JavaScript 3 (perhaps ES5). I can't find the page at the moment. The really unfortunate part is the time lines to reach such goals are likely very long...available in 95% of browsers in the year 2020? Who knows?

Peter

# Eric Suen (15 years ago)

I really don't think prefix is neccessary, what about:

(a;b;c) {...}

or

(a:b:c) {...}

2nd one may have problem with '::' or TypedIdentifier in ES4.

,

Eric Suen

----- Original Message ---

# Brendan Eich (15 years ago)

On Dec 2, 2008, at 6:26 PM, Eric Suen wrote:

I really don't think prefix is neccessary, what about:

(a;b;c) {...}

or

(a:b:c) {...}

2nd one may have problem with '::' or TypedIdentifier in ES4.

This loses almost all connection with the rest of the language.
Arguments are passed in a comma separated list. Wrapped in
parentheses. The Smalltalk hommage loses the parens but keeps the
commas. Any other separator is just wrong, sorry.

C# uses (a, b, c) => ... but in JS the comma operator makes that nasty

to parse top-down. I think the only candidates have to be of the form

^(a, b, c) {...}

(^ could be another character, but it seems to beat \ as others have
noted), or else the Smalltalky

{ |a, b, c| ... }

At this point we need a bake-off, or a convincing argument against the
unusual vertical bar usage.

# Neil Mix (15 years ago)

On Dec 2, 2008, at 8:57 PM, Brendan Eich wrote:

C# uses (a, b, c) => ... but in JS the comma operator makes that
nasty to parse top-down. I think the only candidates have to be of
the form

^(a, b, c) {...}

(^ could be another character, but it seems to beat \ as others have
noted), or else the Smalltalky

{ |a, b, c| ... }

At this point we need a bake-off, or a convincing argument against
the unusual vertical bar usage.

How's this for a strawman: the choice is to follow either Objective-C
or Smalltalk. Given that Objective-C and JS share syntactical roots
in C, it makes more sense to follow the Objective-C precedent.

# Peter Michaux (15 years ago)

On Tue, Dec 2, 2008 at 7:11 PM, Neil Mix <nmix at pandora.com> wrote:

How's this for a strawman: the choice is to follow either Objective-C or Smalltalk.

Or Scheme which, after all, was the first Lisp with lexical scoping and first-class lambdas.

Peter

# Rob Arnold (15 years ago)

2008/12/2 Brendan Eich <brendan at mozilla.com>

This loses almost all connection with the rest of the language. Arguments are passed in a comma separated list. Wrapped in parentheses. The Smalltalk hommage loses the parens but keeps the commas. Any other separator is just wrong, sorry.

C# uses (a, b, c) => ... but in JS the comma operator makes that nasty to parse top-down. I think the only candidates have to be of the form

^(a, b, c) {...}

(^ could be another character, but it seems to beat \ as others have noted), or else the Smalltalky

{ |a, b, c| ... }

At this point we need a bake-off, or a convincing argument against the unusual vertical bar usage.

/be

The double bar syntax looks ok except when there are no arguments since it then looks like logical or which is confusing to my eyes. And when there is just a space between them, it looks awkward as well.

I like the idea of a short prefix (shorter than lambda), but there aren't any single character symbols that don't already have meaning somewhere else it seems.

With these two points in mind, I'd like to propose using the SML syntax for lambdas. This introduces a new keyword but it is only two characters and I think it better describes what is going on than ^ or some other character. There are probably other short 2 letter keywords that would work as well.

# Eric Suen (15 years ago)

In that case, some binary operator are ok to use and no ambiguities,

like:

^ (a,b,c) { ... } & (a,b,c) { ... } % (a,b,c) { ... } | (a,b,c) { ... }

# David-Sarah Hopwood (15 years ago)

Eric Suen wrote:

I really don't think prefix is neccessary, what about:

(a;b;c) {...}

or

(a:b:c) {...}

Not compatible:

(a) {...}

A semicolon would be inserted after "(a)" in ES3, and adding the above construct would change that.

Besides, prefixes are good: they allow a human reader to see precisely what construct is coming next when reading strictly left-to-right, without having to look ahead.

# Yuh-Ruey Chen (15 years ago)

Brendan Eich wrote:

C# uses (a, b, c) => ... but in JS the comma operator makes that nasty to parse top-down. I think the only candidates have to be of the form

^(a, b, c) {...}

(^ could be another character, but it seems to beat \ as others have noted), or else the Smalltalky

{ |a, b, c| ... }

At this point we need a bake-off, or a convincing argument against the unusual vertical bar usage.

Here's a possible technical issue that might not apply to ES: Ruby blocks params can't have default arguments according to eigenclass.org/hiki.rb?Changes+in+Ruby+1.9#l9 :

The new syntax allows to specify default values for block arguments,
since

 {|a,b=1| ... } 
  

is said to be impossible with Ruby's current LALR(1) parser, built
with bison.
# Eric Suen (15 years ago)

In feature(ES4), ES may have OptionalParameter, that will cause trouble

{ |a ,b = 1 | c | d | e | f }

is

{ (|a ,b = 1 |) c | d | e | f }

or

{ (|a ,b = 1 | c) | d | e | f }

,

Eric Suen

----- Original Message ---

# P T Withington (15 years ago)

On 2008-12-02, at 20:32EST, Peter Michaux wrote:

I keep a list of all the X->ES3 compilers I encounter in the middle of the following page

peter.michaux.ca/articles/compiling-down-to-javascript-1-5

Clarification on:

OpenLaszlo is the opposite direction: JavaScript compiled into Flash.

OL compiles an XML language to a subset of ECMAScript4 (or ECMAScript3
with the more mainstream parts of ES4: classes, type declarations,
parameter defaults, rest args, etc.) and has multiple back-ends that
compile that ECMAScript4 subset to (for example) JavaScript,
ActionScript 2, or ActionScript 3. So, in a sense, we use Javascript
as both an intermediate language and as an assembly language.

# Michael (15 years ago)

If I understand the proposals correctly, then of the following e4x:

var doc = {{|a,b,c|...}(d,e,f)}

var doc = {^(a,b,c){...}(d,e,f)}

var doc = {{||...}()}

var doc = {^(){...}()}

I honestly don't see the clarity of one over the other especially if someone writes larger, asinine e4x.

At least with the vertical bars it has similarities/inspiration from other languages as mentioned. This may be important for those migrating

from one of those languages. When I first saw ^(){...}, the vision that came to my head was the pointer operator in Visual C++

# Jon Zeppieri (15 years ago)

2008/12/2 Brendan Eich <brendan at mozilla.com>:

I think the only candidates have to be of the form ^(a, b, c) {...} (^ could be another character, but it seems to beat \ as others have noted),

Was there a problem with \ other than the fact that it can't easily be turned into a binding form, à la

\userFn() {...}

... due to the \u ambiguity? Because the Smalltalk-like syntax doesn't have an obvious binding form either.

{ userFn |a, b, c| ... }

is potentially already valid ES3, depending on the contents of '...' .

# Yuh-Ruey Chen (15 years ago)

Eric Suen wrote:

In feature(ES4), ES may have OptionalParameter, that will cause trouble

{ |a ,b = 1 | c | d | e | f }

is

{ (|a ,b = 1 |) c | d | e | f }

or

{ (|a ,b = 1 | c) | d | e | f }

,

Eric Suen

That should be a syntax error. Parenthesis should be required in that case to avoid ambiguity: {|a, b = (1 | c)| ... }

# Eric Suen (15 years ago)

In that case, you have to rewrite the grammar from:

OptionalParameter ::= Parameter '=' NonAssignmentExpression[allowIn]

to

OptionalParameter ::= Parameter '=' NonAssignmentExpression[allowIn,noOR]

Make it necessary complicated.

# Brendan Eich (15 years ago)

Good point -- you scored a direct hit on the Smalltalk-homage
battleship here, IMHO.

# Mark S. Miller (15 years ago)

Despite my nostalgic longing for Smalltalk, I agree ;). But since the purpose is to be friendlier to control abstraction patterns, it remains important to have a very lightweight syntax for the no-parameter (thunk) case. The parameter list between the ^ and the { should be optional.

Btw, I see no reason to allow a name after the ^, so I agree the name issue by itself doesn't argue against . But my eyes just like ^ better.

2008/12/3 Brendan Eich <brendan at mozilla.com>

# Peter Michaux (15 years ago)

2008/12/3 Michael <Michael at lanex.com>:

If I understand the proposals correctly, then of the following e4x:

Why do you mention e4x? I'm not familiar with it but thought it only adds XML literals to the language.

Peter

# Anton van Straaten (15 years ago)

Brendan Eich wrote:

Allen Wirfs-Brock put his proposal, which will not shock you who know Smalltalk or Allen, on the whiteboard:

// Instead of lambda (a, b, c) { ... }, why not: { |a, b, c| ... } ?

Can't resist a historical note (which I haven't seen mentioned, but perhaps I missed something): the Clipper language[1] in its version 5.0, circa 1990, used this exact syntax for its "code block" data type, which were in fact lexically scoped lambdas.[2]

Not surprisingly, Clipper adapted its code block syntax from Smalltalk. Curly brackets were used instead of Smalltalk's square brackets, presumably to fit better with existing Clipper syntax, which used curly brackets for array definitions like {1,2,3} and square brackets for array subscripts like x[2].

Googling for "clipper code block" turns up various pages with example uses. Unfortunately, the examples that come up are all quite simple ones, but code blocks, being lambdas, were capable of much more.

Anton

[1] Clipper was originally a compiler for Ashton-Tate's dBASE database system/language, and was developed by the now-defunct Nantucket Corporation. It was ultimately sold to Computer Associates.

[2] The terminology surrounding lambdas was unfamiliar to most Clipper programmers at the time - the Clipper community referred to captured lexical variables as "detached locals". Googling for that exact term will turn up various examples of its use in the context of Clipper and its descendants such as Harbour and xHarbour.

# P T Withington (15 years ago)

If we are voting on the color of paint to use, I sure like:

^(a, b, ...) { ... }

  • ^ is mnemonic for Λ (Greek capital lambda)
  • ^ is mnemonic for 'pointer' so I am reminded that I am creating and
    object here
  • () keeps parameters in a parameter list so I don't have to learn a
    new syntax (as I might with any other delimiter)
  • prefix ^ might be confused with the infix operator of the same name
# Steven Johnson (15 years ago)

On 12/2/08 7:11 PM, "Neil Mix" <nmix at pandora.com> wrote:

How's this for a strawman: the choice is to follow either Objective-C or Smalltalk. Given that Objective-C and JS share syntactical roots in C, it makes more sense to follow the Objective-C precedent.

I find Objective-C's syntax to be its weakest point, so I'm not sure I think either one of these is a good idea.

My question is whether a new keyword (or token) is needed for lambda at all. Can't the existing "function" keyword be repurposed here?

# Michael (15 years ago)

Since one of the issues brought up was the issue of readability, I figured it was fitting to mention that in the context of E4X code the new syntax could potentially be even more difficult to discern due to the double nested braces depending on the details, whether it be the examples I gave or one where it could be generated as such:

var doc = <{{|a,b,c|...}(d,e,f)}><foo>{{|a,b,c|...}(d,e,f)}</foo></{{|a,b,c|...}(d,e,f )}>

One would obviously hope never to see something like that, but if its legal syntax I wouldn't put it past someone to try (in a larger document perhaps) vice a named function call

# Brendan Eich (15 years ago)

On Dec 3, 2008, at 10:45 AM, Steven Johnson wrote:

On 12/2/08 7:11 PM, "Neil Mix" <nmix at pandora.com> wrote:

How's this for a strawman: the choice is to follow either Objective-C or Smalltalk. Given that Objective-C and JS share syntactical roots in C, it makes more sense to follow the Objective-C precedent.

I find Objective-C's syntax to be its weakest point, so I'm not sure
I think either one of these is a good idea.

My question is whether a new keyword (or token) is needed for lambda
at all. Can't the existing "function" keyword be repurposed here?

How? ES3 already defines function expressions as well as definitions.
They are not the lambdas we seek.

# YR Chen (15 years ago)

2008/12/3 P T Withington <ptw at pobox.com>

If we are voting on the color of paint to use, I sure like: ^(a, b, ...) { ... }

  • ^ is mnemonic for Λ (Greek capital lambda)
  • ^ is mnemonic for 'pointer' so I am reminded that I am creating and object here
  • () keeps parameters in a parameter list so I don't have to learn a new syntax (as I might with any other delimiter)
  • prefix ^ might be confused with the infix operator of the same name

Though use of the infix ^ is so rare that I bet many JS users don't even know about it. The first article found when googling "javascript operators" (w3schools.com) doesn't even mention it.

# Jon Zeppieri (15 years ago)

2008/12/3 P T Withington <ptw at pobox.com>:

  • prefix ^ might be confused with the infix operator of the same name

With semicolon insertion, isn't this a bigger problem?

The opening brace will need to be on the same line as the formals, otherwise the syntax is ambiguous:

^(x) { x = x * x ^(a,b,c,d,e,f,g) { x } }

And, if it is on the same line, it's still bad for a top-down parser:

^(x) { x = x * x ^(a,b,c,d,e,f,g) {x} }

Will semicolon insertion be illegal inside a lambda body?

# Maciej Stachowiak (15 years ago)

On Dec 2, 2008, at 6:57 PM, Brendan Eich wrote:

This loses almost all connection with the rest of the language.
Arguments are passed in a comma separated list. Wrapped in
parentheses. The Smalltalk hommage loses the parens but keeps the
commas. Any other separator is just wrong, sorry.

C# uses (a, b, c) => ... but in JS the comma operator makes that
nasty to parse top-down. I think the only candidates have to be of
the form

^(a, b, c) {...}

(^ could be another character, but it seems to beat \ as others have
noted), or else the Smalltalky

{ |a, b, c| ... }

At this point we need a bake-off, or a convincing argument against
the unusual vertical bar usage.

As a potential intuition pump, here's an example of some existing
built-in JS control structures, and how one would write the equivalent
with either form of lambda syntax. I am assuming the ^ version allows
omitting the parameter list when empty. They are formatted to look as
much as practical like the standard syntax.

if (x < 3) { handleSmallNum(x); } else { handleLargeNum(x); }

if_ (^{x < 3}, ^{ handleSmallNum(x); }, ^{ handleLargeNum(x); });

if_ ({ || x < 3}, { || handleSmallNum(x); }, { || handleLargeNum(x); });

while (x != null) { x.process(); x = x.next(); }

while_ (^{x != null}, ^{ x.process(); x = x.next(); });

while_ ({ || x != null}, { || x.process(); x = x.next(); });

for (var i = 1; i < 10; i++) { total += vec[i]; }

for_ (^{var i = 1}, ^{i < 10}, ^{i++}, ^{ total += vec[i]; });

for_ ({ || var i = 1}, { || i < 10}, { || i++}, { || total += vec[i]; });

for (var prop in obj) { props.push(prop); }

forIn_ (obj, ^(prop) { props.push(prop); });

forIn_ (obj, { |prop| props.push(prop); });

Here's examples with some built in JS functions that take function
arguments:

arr.sort(function (a, b) { return aa < bb; }); arr.sort(^(a, b) { aa < bb }); arr.sort({ |a, b| aa < bb });

arr.map(function (x) { return x * (x - 1); }); arr.map(^(x) { x * (x-1)}); arr.map({ |x| x * (x-1) });

function doubleBs(str) { return str.replace(/b*/, function (substr) { return substr +
substr; }); }

function doubleBs(str) { str.replace(/b*/, ^(substr) { substr + substr }); }

function doubleBs(str) { str.replace(/b*/, { |substr| substr + substr }); }

I like the ^ solution better because:

  1. It makes parameter lists look like function parameter lists and
    call argument lists.
  2. It separates the parameter from the body more clearly, which seems
    more readable, especially when there lambda is passed to something
    that also takes other arguments.
  3. ^{ ... } seems like a less visually noisy way to express a 0- argument thunk than { || ... }

, Maciej

# Peter Michaux (15 years ago)

Summary of what I've read:

The syntax (){} has a named lambda problem if the lambda name starts with a 'u'. Depending on whitespace between the backslash and the identifier seems like it will cause bugs

\u03c0(){} \ u03c0(){}

The syntax ^(){} has a semicolon insertion ambiguity. What does the following mean?

x = x ^(){}

The {||} syntax has an optional parameters problem. What does the following mean?

{|a, b = 1 | c | d}

The other suggestion which seems to be on the table has been a new keyword "lambda" or something shorter.

lambda() {} lmbd() {} ld() {}

Any parsing problems are already ones programmers know how to work around. The only whinge has been that "lambda" is too long. Changing from the 6 character "lambda" keyword to something shorter would work technically. I think just a one character keyword would be too short as they are commonly used for loop variables. I think the sometimes used "fn" can't be used because "function" is already taken. Both "lmbd" or "ld" are abbreviations which are not in the character of ES language keywords. "var" is the only keyword abbreviation in ES3. All other keywords are complete words and the word for the concept desired here is "lambda".

Peter

# Yuh-Ruey Chen (15 years ago)

Yuh-Ruey Chen wrote:

Brendan Eich wrote:

C# uses (a, b, c) => ... but in JS the comma operator makes that nasty to parse top-down. I think the only candidates have to be of the form

^(a, b, c) {...}

(^ could be another character, but it seems to beat \ as others have noted), or else the Smalltalky

{ |a, b, c| ... }

At this point we need a bake-off, or a convincing argument against the unusual vertical bar usage.

Here's a possible technical issue that might not apply to ES: Ruby blocks params can't have default arguments according to eigenclass.org/hiki.rb?Changes+in+Ruby+1.9#l9 :

The new syntax allows to specify default values for block
arguments, since

 {|a,b=1| ... } 
  

is said to be impossible with Ruby's current LALR(1) parser, built
with bison.

That Ruby 1.9 page also lists yet another possible syntax:

->(a, b, ...) {...}

Using Maciej's examples:

if_ (->{x < 3}, ->{ handleSmallNum(x); }, ->{ handleLargeNum(x); });

while_ (->{x != null}, ->{ x.process(); x = x.next(); });

for_ (->{var i = 1}, ->{i < 10}, ->{i++}, ->{ total += vec[i]; });

forIn_ (obj, ->(prop) { props.push(prop); });

arr.sort(->(a, b) { aa < bb });

arr.map(->(x) { x * (x-1)});

function doubleBs(str) { str.replace(/b*/, ->(substr) { substr + substr }); }

The control abstractions just don't look right, regardless of which lambda syntax we choose. I'd rather wait for a more powerful macro system, instead of choosing the syntax based off how it would look in control abstractions.

# Peter Michaux (15 years ago)

2008/12/3 Yuh-Ruey Chen <maian330 at gmail.com>:

The control abstractions just don't look right, regardless of which lambda syntax we choose. I'd rather wait for a more powerful macro system, instead of choosing the syntax based off how it would look in control abstractions.

I agree completely.

Peter

# Michael (15 years ago)

This may be a stupid question, but is the current let expression syntax defined in JavaScript 1.7 too fundamentally different from the sought out lambda expression to be repurposed? Or would this wreak havoc on current uses?

# Breton Slivka (15 years ago)

On Thu, Dec 4, 2008 at 9:28 AM, Michael <Michael at lanex.com> wrote:

This may be a stupid question, but is the current let expression syntax defined in JavaScript 1.7 too fundamentally different from the sought out lambda expression to be repurposed? Or would this wreak havoc on current uses?

I had kind of a similar thought about let statements. Would it be possible to simply turn a let statement into an assignable/callable value? Would that just break too many things? would it get us at least close to the lambda functionality we seek?

# Michael (15 years ago)

Just so there is no confusion on meaning, I'm not referring to let statements nor let definitions but to this portion in particular: developer.mozilla.org/en/New_in_JavaScript_1.7#let_expressions

# Brendan Eich (15 years ago)

First, let expressions as implemented in JS1.7 are ambiguous in a
bottom up grammar. See

esdiscuss/2008-October/007890

Second, let statements and expressions in JS1.7 are evaluated when
reached. They are not implicitly quoted for later application.

Third, let is the wrong keyword if there's no need to bind a name --
as there indeed is no need with many (most?) lambda use-cases.

# Sam Ruby (15 years ago)

2008/11/30 Brendan Eich <brendan at mozilla.com>:

At the TC39 meeting two weeks ago in Kona, we had a brief bikeshedding discussion about lambda syntax and why it matters.

Has anybody given any thought to the C# (ECMA TC49) syntax?

(input parameters) => {statement;}

where the parens are optional if there is only one parameter, and the braces are optional if the statement is a simple expression. Example:

(a,b) => a+b

More specifics here:

msdn.microsoft.com/en-us/library/bb397687.aspx

  • Sam Ruby
# Brendan Eich (15 years ago)

On Dec 3, 2008, at 4:06 PM, Sam Ruby wrote:

2008/11/30 Brendan Eich <brendan at mozilla.com>:

At the TC39 meeting two weeks ago in Kona, we had a brief
bikeshedding discussion about lambda syntax and why it matters.

Has anybody given any thought to the C# (ECMA TC49) syntax?

Yes, it has come up on this list. Head of thread message is:

esdiscuss/2007-March/003893

In JS, the C# syntax creates an ambiguity with the comma expression in
the n-ary n > 1 case. Bottom-up parsers can cope; top-down have a

harder time changing their minds about what they are parsing when the
see the => after the parameter list. It can be handled either way,

though.

Having had several threads over a couple of years on this, my
impression is almost no one has championed an "infix" syntax such as
C#'s. The prefix crowd is split between those wanting lambda vs. a one- char punctuator. The "postfix" (not accurate but you know what I mean)
position favors the Smalltalk homage.

# Maciej Stachowiak (15 years ago)

On Dec 3, 2008, at 1:23 PM, Peter Michaux wrote:

Summary of what I've read:

The syntax (){} has a named lambda problem if the lambda name starts with a 'u'. Depending on whitespace between the backslash and the identifier seems like it will cause bugs

\u03c0(){} \ u03c0(){}

The syntax ^(){} has a semicolon insertion ambiguity. What does the following mean?

x = x ^(){}

I think this has only one possible meaning: an expression statement
consisting of an assignment of x to itself, followed by an expression
statement that creates an empty no-argument closure. Do you see
another possible meaning?

More generally, I can imagine cases that might actually be ambiguous
but I think they can be fixed in the same way that this is:

x = x +x

, Maciej

# Brendan Eich (15 years ago)

On Dec 3, 2008, at 6:18 PM, Maciej Stachowiak wrote:

x = x +x

That is equivalent to

x = x + x;

so the case with ^ should not differ. (Were you testing in an
interactive REPL?)

That the case Peter showed:

x = x ^(){}

cannot be parsed as a bitwise-xor expression doesn't help in general,
if we do not want to mandate bottom-up parsing (we don't).

# Blake Kaplan (15 years ago)

On 12/03/2008 06:18 PM, Maciej Stachowiak wrote:

On Dec 3, 2008, at 1:23 PM, Peter Michaux wrote:

x = x ^(){}

I think this has only one possible meaning: an expression statement consisting of an assignment of x to itself, followed by an expression statement that creates an empty no-argument closure. Do you see another possible meaning?

According to the wording in E-262, that's a syntax error, and your second example:

x = x +x

Is equivalent to |x = x + x| because, when at the 2nd |x|, the next token is valid.

# Eric Suen (15 years ago)

No,

^(x) is not a legal expression, so you don't have to make block in same line, no semicolon insertion will happens here.

see this post:

esdiscuss/2008-December/008296

----- Original Message ---

# Eric Suen (15 years ago)

I suggest following grammar:

Lambda ::= '&' '(' Parameters ')' Block | '&' Block //if no parameters

I can comfire that this rule is no problem for a LALR(k) parser, ES4 is not LALR(1) anyway. and I think ^ is not good for eyes, it is too small and may confused with ~. & look more like C/C++ style

a = & { ... }

a = & (a,b) { ... }

# Jon Zeppieri (15 years ago)

On Wed, Dec 3, 2008 at 9:36 PM, Eric Suen <eric.suen.tech at gmail.com> wrote:

No,

^(x) is not a legal expression, so you don't have to make block in same line, no semicolon insertion will happens here.

You're looking at the wrong part of the example. Sorry -- the "wrapper" lambda is superfluous, and I should have left it out for clarity.

Using the GNU bracing style:

x = x * x ^(a,b,c,d,e,f,g) { x }

The above parses as an xor expression followed by a block, like so:

x = x * x ^ (a,b,c,d,e,f,g) { x } ... which is odd, to be sure, but perfectly legal.

The second example:

x = x * x ^(a,b,c,d,e,f,g) {x}

is not ambiguous, but it's unsuitable for top-down parsing. (I tried to underscore the point by using a long list of formals.) The parser has to get to the opening brace before it can determine that it isn't dealing with an xor expression.

# Eric Suen (15 years ago)

Because it is a xor expression, for lambda, it will be

x = x * x ^ ^ (a,b,c,d,e,f,g) { x }

it is same for regexp,

x = x / x /i

is not regexp, but

x = x / /x/i

is regexp

seems that ^ will confuse lots of people

----- Original Message ---

# Eric Suen (15 years ago)

It even can be writen as:

Lambda ::= '&' IdentifierOpt ParametersOpt Block

IdentifierOpt ::= %Empty | PropertyIdentifier

ParametersOpt ::= %Empty | '(' Parameters ')'

# Jon Zeppieri (15 years ago)

On Wed, Dec 3, 2008 at 9:52 PM, Eric Suen <eric.suen.tech at gmail.com> wrote:

Because it is a xor expression, for lambda, it will be

x = x * x ^ ^ (a,b,c,d,e,f,g) { x }

I don't understand what you're getting at. That's a syntax error. My example:

x = x * x ^(a,b,c,d,e,f,g) { x }

is not a syntax error, but it also (unfortunately) doesn't contain a lambda expression. Or am I missing something?

# Jon Zeppieri (15 years ago)

On Wed, Dec 3, 2008 at 10:00 PM, Jon Zeppieri <jaz at bu.edu> wrote:

On Wed, Dec 3, 2008 at 9:52 PM, Eric Suen <eric.suen.tech at gmail.com> wrote:

Because it is a xor expression, for lambda, it will be

x = x * x ^ ^ (a,b,c,d,e,f,g) { x }

I don't understand what you're getting at. That's a syntax error.

Sorry -- I take that back. It's not (or likely would not be) a syntax error, but I still don't understand its relevance.

# Eric Suen (15 years ago)

Yes, it doesn't contain a lambda expression, just like:

a = x /x/i

is not same as:

a = x; /x/i

they both right but has different meaning...

# Peter Michaux (15 years ago)

On Wed, Dec 3, 2008 at 7:16 PM, Eric Suen <eric.suen.tech at gmail.com> wrote:

Yes, it doesn't contain a lambda expression, just like:

a = x /x/i

is not same as:

a = x; /x/i

they both right but has different meaning...

Is adding more confusion like this in the language desirable?

Peter

# Jon Zeppieri (15 years ago)

On Wed, Dec 3, 2008 at 10:16 PM, Eric Suen <eric.suen.tech at gmail.com> wrote:

Yes, it doesn't contain a lambda expression, just like:

a = x /x/i

is not same as:

a = x; /x/i

they both right but has different meaning...

Okay -- so we agree. In that case, it's clear that your proposed syntax:

&(a,b,c) {...}

has the same problem, right? Any valid ES3 infix operator will have the same problem, if we use it as a prefix lambda operator.

# Eric Suen (15 years ago)

Why not using two version, one is for definition like:

Lambda name (a,b,c) { }

and for expression, you can use both, like:

a = lambda (a,b,c) { }

and

a = &(a,b,c) { }

# André Bargull (15 years ago)

My example:

x = x * x ^(a,b,c,d,e,f,g) { x }

is not a syntax error, but it also (unfortunately) doesn't contain a lambda expression. Or am I missing something?

Or a bit more obvious than the use of the comma-operator: As soon as named lambdas are introduced (the weak spot on the \ proposal), you'll get big problems with the ^ proposal, too. Consider the following snippet which is valid Javascript code, but certainly not a lambda expression.

var f = function (){return 8;} var x = 5 ^f(x) { x=x*x }

# P T Withington (15 years ago)

Would it work to move the parameter list inside the block (as in the
Smalltalk way, but as a regular parameter list, not using ||'s)?

{(a, b) a + b}

AFAICT, {( is a syntax error for an expression in es3.

# André Bargull (15 years ago)

On 12/4/2008 3:15 PM, P T Withington wrote:

Would it work to move the parameter list inside the block (as in the Smalltalk way, but as a regular parameter list, not using ||'s)?

{(a, b) a + b}

AFAICT, {( is a syntax error for an expression in es3.

And if you just execute the lambda expression? For example the following code is legal Javascript:

var a = b = 0; {(a,b) a+b}(10, 5)

# Michael (15 years ago)

Would this form also be ambiguous and/or too difficult to parse?

{=> 9*9}() {a => a+b}(12) {(a,b) => a+b}(12,6)

# Maciej Stachowiak (15 years ago)

On Dec 3, 2008, at 6:30 PM, Brendan Eich wrote:

On Dec 3, 2008, at 6:18 PM, Maciej Stachowiak wrote:

x = x +x

That is equivalent to

x = x + x;

so the case with ^ should not differ. (Were you testing in an
interactive REPL?)

I didn't test, I just knew this case must be disambiguated somehow and
didn't test which way. I don't think it matters much which way, since
you can avoid any such problems in your own code by using semicolons
for line endings.

That the case Peter showed:

x = x ^(){}

cannot be parsed as a bitwise-xor expression doesn't help in
general, if we do not want to mandate bottom-up parsing (we don't).

I think it would be fine for this case to be a syntax error.

, Maciej

# Maciej Stachowiak (15 years ago)

On Dec 4, 2008, at 7:18 AM, Michael wrote:

Would this form also be ambiguous and/or too difficult to parse?

{=> 9*9}() {a => a+b}(12) {(a,b) => a+b}(12,6)

I imagine it would be problematic for a top-down parser because you
may have to parse an unbounded number of characters to determine if
the initial parameter list is in fact a parameter list or a comma
expression.

# Mark S. Miller (15 years ago)

On Wed, Dec 3, 2008 at 7:25 PM, Jon Zeppieri <jaz at bu.edu> wrote:

Okay -- so we agree. In that case, it's clear that your proposed syntax:

&(a,b,c) {...}

has the same problem, right? Any valid ES3 infix operator will have the same problem, if we use it as a prefix lambda operator.

Welcome to the syntax races. "lambda" takes an early lead, but drops back because of too much weight. For a while, it's neck and neck between "||" and "^", with "" following closely and "fn", "&", and other trailing. Many old timers (including your commentator) are rooting for "||" because of its previous historic performances. But "||" trips up over ambiguities not present on its original track. "^" is now in the lead. Oh no! It trips on a different ambiguity. This track seems riddled with more ambiguities than any of these contenders have ever trained on. Seeing "^" stumble, "&" and other contenders saddled with "binary operator"ness, drop back and concede. "" has taken the lead....

# Peter Michaux (15 years ago)

2008/12/4 Mark S. Miller <erights at google.com>:

Welcome to the syntax races. "lambda" takes an early lead, but drops back because of too much weight. For a while, it's neck and neck between "||" and "^", with "" following closely and "fn", "&", and other trailing. Many old timers (including your commentator) are rooting for "||" because of its previous historic performances. But "||" trips up over ambiguities not present on its original track. "^" is now in the lead. Oh no! It trips on a different ambiguity. This track seems riddled with more ambiguities than any of these contenders have ever trained on. Seeing "^" stumble, "&" and other contenders saddled with "binary operator"ness, drop back and concede. "" has taken the lead....

I have my money on "lambda". I'm thinking it has the endurance necessary. It has already lasted longer in history than all the others.

Peter

# Brendan Eich (15 years ago)

On Dec 4, 2008, at 10:28 AM, Maciej Stachowiak wrote:

On Dec 4, 2008, at 7:18 AM, Michael wrote:

Would this form also be ambiguous and/or too difficult to parse?

{=> 9*9}() {a => a+b}(12) {(a,b) => a+b}(12,6)

I imagine it would be problematic for a top-down parser because you
may have to parse an unbounded number of characters to determine if
the initial parameter list is in fact a parameter list or a comma
expression.

Right -- especially if one includes destructuring parameters.
Typically a top-down cover grammar is parsed, and then disamiguated
based on right context after the AST is built, with any adjustments to
the AST encoding made retrospectively. This can get ugly.

Worse, as Waldemar pointed out, you can end up with a failure to
backtrack and find the valid sentential form that a bottom up parser
would find via shifting and reducing.

Combined, this says to me that the C# syntax is no-go for JS.

# David-Sarah Hopwood (15 years ago)

Yuh-Ruey Chen wrote:

Brendan Eich wrote:

C# uses (a, b, c) => ... but in JS the comma operator makes that nasty to parse top-down. I think the only candidates have to be of the form

^(a, b, c) {...}

(^ could be another character, but it seems to beat \ as others have noted), or else the Smalltalky

{ |a, b, c| ... }

At this point we need a bake-off, or a convincing argument against the unusual vertical bar usage.

Here's a possible technical issue that might not apply to ES: Ruby blocks params can't have default arguments according to eigenclass.org/hiki.rb?Changes+in+Ruby+1.9#l9 :

The new syntax allows to specify default values for block arguments,
since

 {|a,b=1| ... }

is said to be impossible with Ruby's current LALR(1) parser, built
with bison.

There's an ambiguity here that is independent of what kind of parser is used (I don't know whether it is the same issue as in Ruby). Consider:

{|a=1|2, b|c}

Is the argument list (a = 1|2, b) with body {c}, or is it (a = 1) with body {2, b|c}?

This ambiguity could be resolved by restricting expressions used as default argument initialisers, but it is not clear why they should be restricted, given that other possible concrete syntaxes for lambda do not have this problem.

IMHO this does count as a "convincing argument against the unusual vertical bar usage", if we want default arguments (and I think we do).

However, I'd like to suggest that it may be premature to be deciding on the concrete syntax of lambda, without knowing its abstract syntax. (The issue that MarkM raised about unintentionally leaking a value that happens to be evaluated in tail position, for example, needs to be dealt with at the abstract syntax level.) The same point applies to the syntax of object literals.

That will require some notation for discussing abstract syntax, on which I'll start another thread.

# David-Sarah Hopwood (15 years ago)

Yuh-Ruey Chen wrote:

Eric Suen wrote:

In feature(ES4), ES may have OptionalParameter, that will cause trouble

{ |a ,b = 1 | c | d | e | f }

is

{ (|a ,b = 1 |) c | d | e | f }

or

{ (|a ,b = 1 | c) | d | e | f }

,

Eric Suen

That should be a syntax error. Parenthesis should be required in that case to avoid ambiguity: {|a, b = (1 | c)| ... }

Yuck. The restrictions on 'in' are bad enough; this is essentially the same kind of ambiguity in another context. Requiring expressions containing '|' to be parenthesized would impose the same degree of grammar complexity overhead again as that imposed by 'NoIn':

ArgumentInitialiser : = ExpressionNoBar = ( Expression )

... unless all default argument initialisers have to be parenthesized, but that is just ugly.

# David-Sarah Hopwood (15 years ago)

Jon Zeppieri wrote:

2008/12/3 P T Withington <ptw at pobox.com>:

  • prefix ^ might be confused with the infix operator of the same name

With semicolon insertion, isn't this a bigger problem?

The opening brace will need to be on the same line as the formals, otherwise the syntax is ambiguous:

^(x) { x = x * x ^(a,b,c,d,e,f,g) { x } }

Strictly speaking, the syntax is not ambiguous; it just is not parsed how you might expect. The semicolons would be inserted in this example as follows:

^(x) { x = (x * x)^(a, b, c, d, e, f, g); { x; } };

Arguably, the problem here is that semicolon insertion is and always was a bad idea.

And, if it is on the same line, it's still bad for a top-down parser:

^(x) { x = x * x ^(a,b,c,d,e,f,g) {x} }

Same result as above.

Will semicolon insertion be illegal inside a lambda body?

That's worth considering. It does not prevent lambdas from being used to desugar other constructs, because semicolon insertion would be performed on the original program before desugaring.

# P T Withington (15 years ago)

On 2008-12-04, at 15:23EST, David-Sarah Hopwood wrote:

Arguably, the problem here is that semicolon insertion is and always was a bad idea.

<whinge>

That and not requiring whitespace around operators, thus taking away a
huge domain of possible multi-symbol names (such as := for
initialization/assignment to preclude the =/== trap, or say, )\ for λ,
and forcing camelCasing or carpal_tunnel_syndrome upon everyone who
prefers descriptive-symbol-names...) </whinge>

# Jon Zeppieri (15 years ago)

On Thu, Dec 4, 2008 at 3:23 PM, David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk> wrote:

Jon Zeppieri wrote:

And, if it is on the same line, it's still bad for a top-down parser:

^(x) { x = x * x ^(a,b,c,d,e,f,g) {x} }

Same result as above.

Actually, I think we're both wrong. If I'm reading the spec correctly, no semicolon would be inserted, and the whole thing would be a syntax error. The "offending token" here is '{', but it's not separated from the previous token -- namely, ')' -- by at least one LineTerminator.

At any rate, it's a problem.

# David-Sarah Hopwood (15 years ago)

Mark S. Miller wrote:

[...] "" has taken the lead....

There's still #, @, and ` (and of course keywords like lambda and fn). None of these are as mnemonic as , but they leave \ as a purely lexical escape character.

It's quite ironic that we are still limited, as Church was, in which characters we can use for the modern equivalent of "typographical reasons".

# David-Sarah Hopwood (15 years ago)

David-Sarah Hopwood wrote:

Jon Zeppieri wrote: [...]

The opening brace will need to be on the same line as the formals, otherwise the syntax is ambiguous:

^(x) { x = x * x ^(a,b,c,d,e,f,g) { x } }

Strictly speaking, the syntax is not ambiguous; it just is not parsed how you might expect. The semicolons would be inserted in this example as follows:

^(x) { x = (x * x)^(a, b, c, d, e, f, g); { x; } };

Arguably, the problem here is that semicolon insertion is and always was a bad idea.

And, if it is on the same line, it's still bad for a top-down parser:

^(x) { x = x * x ^(a,b,c,d,e,f,g) {x} }

Same result as above.

Sorry, not the same result. This would be formally a syntax error, although note that some implementations do perform semicolon insertion even at non-line-boundaries.

# Brendan Eich (15 years ago)

On Dec 4, 2008, at 12:52 PM, David-Sarah Hopwood wrote:

Sorry, not the same result. This would be formally a syntax error, although note that some implementations do perform semicolon insertion even at non-line-boundaries.

Yes, that bothers me (I'm feeling guilty here: I could use a
bugzilla.mozilla.org bug on file). But is it required for web interop?
If IE JScript does it and has since the old days, then the default
answer has to be "yes", and we should think about specifying the de- facto standard.

# Breton Slivka (15 years ago)

On Fri, Dec 5, 2008 at 7:49 AM, David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk> wrote:

Mark S. Miller wrote:

[...] "" has taken the lead....

There's still #, @, and ` (and of course keywords like lambda and fn). None of these are as mnemonic as , but they leave \ as a purely lexical escape character.

It's quite ironic that we are still limited, as Church was, in which characters we can use for the modern equivalent of "typographical reasons".

-- David-Sarah Hopwood

this may be a stupid question, but why? Is it really so impossible to have λ(a,b,c){} ? You guys seem to have no trouble typing it. It's not that much trouble to remap a key, and you can always keep lambda(a,b,c){} as a more verbose but more accessable alternative. IDEs could make a macro out of it so you wouldn't even have to bother with going to the trouble of remapping. Nearly all computers on the planet have a greek alphabet installed on them. And keep in mind, we're not designing a language for tomorrow. We're designing a language for 10 years from now. λ could be way more convenient to enter by then, particularly if it's in an upcoming spec for a programming language.

I admit this seems ludicrous at its face, but admittedly I have not really seen the arguments against λ as an abbreviated lambda syntax yet.

# Brendan Eich (15 years ago)

On Dec 4, 2008, at 2:31 PM, Breton Slivka wrote:

I admit this seems ludicrous at its face, but admittedly I have not really seen the arguments against λ as an abbreviated lambda syntax yet.

Not compatibly: ES3 already allows Unicode identifiers, including
Greek Lambda. Other Mathematical Lambda characters are not in the BMP:

www.mail-archive.com/[email protected]/msg15581.html

It's still too hard to type.

# Jon Zeppieri (15 years ago)

[oops, sent from the wrong address...]

2008/12/4 Breton Slivka <zen at zenpsycho.com>:

this may be a stupid question, but why? Is it really so impossible to have λ(a,b,c){} ?

Last time I brought this up, Brendan made fun of me on a podcast. :(

You guys seem to have no trouble typing it. It's not that much trouble to remap a key, and you can always keep lambda(a,b,c){} as a more verbose but more accessable alternative. IDEs could make a macro out of it so you wouldn't even have to bother with going to the trouble of remapping.

Exactly what I wrote then.

I admit this seems ludicrous at its face, but admittedly I have not really seen the arguments against λ as an abbreviated lambda syntax yet.

Well aside from the "random guy doesn't know how to map a key" problem (which is perfectly true), I could see some character set issues in the field.

On Thu, Dec 4, 2008 at 5:35 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Dec 4, 2008, at 2:31 PM, Breton Slivka wrote:

I admit this seems ludicrous at its face, but admittedly I have not really seen the arguments against λ as an abbreviated lambda syntax yet.

Not compatibly: ES3 already allows Unicode identifiers, including Greek Lambda.

Also including the word 'lambda' -- but that hasn't stopped it from being seriously considered.

# Breton Slivka (15 years ago)

On Fri, Dec 5, 2008 at 9:35 AM, Brendan Eich <brendan at mozilla.com> wrote:

On Dec 4, 2008, at 2:31 PM, Breton Slivka wrote:

I admit this seems ludicrous at its face, but admittedly I have not really seen the arguments against λ as an abbreviated lambda syntax yet.

Not compatibly: ES3 already allows Unicode identifiers, including Greek Lambda. Other Mathematical Lambda characters are not in the BMP:

www.mail-archive.com/[email protected]/msg15581.html

It's still too hard to type.

/be

picasaweb.google.com/eileen.world.traveler/EileenBestOfGreece#5139474493916668850

# Michael (15 years ago)

For some reason I'm reminded of this quote:

"APL, in which you can write a program to simulate shuffling a deck of cards and then dealing them out to several players in four characters, none of which appear on a standard keyboard." David Given

# Brendan Eich (15 years ago)

On Dec 4, 2008, at 2:45 PM, Jon Zeppieri wrote:

2008/12/4 Breton Slivka <zen at zenpsycho.com>:

this may be a stupid question, but why? Is it really so impossible to have λ(a,b,c){} ?

Last time I brought this up, Brendan made fun of me on a podcast. :(

Not you personally! I hope that was at least a :-/ emoticon...

On Thu, Dec 4, 2008 at 5:35 PM, Brendan Eich <brendan at mozilla.com>
wrote:

On Dec 4, 2008, at 2:31 PM, Breton Slivka wrote:

I admit this seems ludicrous at its face, but admittedly I have not really seen the arguments against λ as an abbreviated lambda syntax yet.

Not compatibly: ES3 already allows Unicode identifiers, including
Greek Lambda.

Also including the word 'lambda' -- but that hasn't stopped it from being seriously considered.

True enough. And 'lambda' is likelier to be in use in web JS as an
identifier than λ, at a guess.

If we have to go to one character, though, I'd rather we use an ASCII
punctuation character, for the reasons given (hard to type, slight
incompatibility). But you λ fans need to help me here: how does one
type λ on a Mac laptop? How about on a standard Windows machine? Pick
a Linux and lay the clues on there, too.

# Peter Michaux (15 years ago)

2008/11/29 Brendan Eich <brendan at mozilla.com>:

At the TC39 meeting two weeks ago in Kona, we had a brief bikeshedding discussion about lambda syntax and why it matters.

Who would have thought a discussion about lambda syntax in JavaScript would go over 120 posts while a simultaneous thread about class syntax has had little attention outside a handful of posters?

Would this have been reverse 10 years ago?

Sign of the paradigm shift? Maybe folks want an immutable cons cells too?

Modern attention span?

Peter

# Jon Zeppieri (15 years ago)

On Thu, Dec 4, 2008 at 6:10 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Dec 4, 2008, at 2:45 PM, Jon Zeppieri wrote:

2008/12/4 Breton Slivka <zen at zenpsycho.com>:

this may be a stupid question, but why? Is it really so impossible to have λ(a,b,c){} ?

Last time I brought this up, Brendan made fun of me on a podcast. :(

Not you personally! I hope that was at least a :-/ emoticon...

Oops. You see the typographical limitations we're still saddled with? The mock-wounded :( and the actually-wounded :( aren't slated to have distinct code points until Unicode 17.

If we have to go to one character, though, I'd rather we use an ASCII punctuation character, for the reasons given (hard to type, slight incompatibility). But you λ fans need to help me here: how does one type λ on a Mac laptop? How about on a standard Windows machine? Pick a Linux and lay the clues on there, too.

I'm a lot more likely to do this within emacs (or an editor, in general) than at the system / window system level. Anyhow, ASCII punctuation is great, if we can settle on a candidate.

# Felix (15 years ago)

Brendan Eich wrote:

If we have to go to one character, though, I'd rather we use an ASCII punctuation character, for the reasons given (hard to type, slight incompatibility). But you λ fans need to help me here: how does one type λ on a Mac laptop? How about on a standard Windows machine? Pick a Linux and lay the clues on there, too.

you can add a greek keyboard to your input methods, and set up a kb shortcut to switch easily.

like, for mac osx: system preferences, international, input menu. enable greek keyboard. enable "show input menu in menu bar".

click on "keyboard shortcuts". in "input menu", enable "select the next input source", assign it a shortcut that doesn't conflict with anything you use, like maybe option-space.

then, to type lambda, type option-space until you're at the greek flag, then type lowercase-L (on u.s. qwerty),

windows is pretty similar to osx, it's in "regional and language options"

I think modern linux is also similar, but I'm not near one at the moment.

# Eugene Lazutkin (15 years ago)

If you started to recap the history of this discussion, could you (or anybody else in the know) verbalize following things:

  1. What is the difference between the function and the lambda? I am not talking about their syntax, I want to understand the semantic difference, if there is any.

  2. Why is it important for a lambda to have an optional name? What's wrong with using a function, if we want a name? IMHO lambda should have the minimalistic syntax.

  3. Why is it important to be able to specify parameter defaults in lambda? Again, it looks like an extra sugar to me that can be covered by a function with parameter defaults.

The reason I ask is a lot of discussion is going around "but if it has a name" and "but if it has a default". If it doesn't have a name I would be satisfied personally with (a, b) {...} --- it doesn't clash with anything. Or even with (a, b) expr.

Thanks,

Eugene

# Brendan Eich (15 years ago)

On Dec 4, 2008, at 5:44 PM, Eugene Lazutkin wrote:

If you started to recap the history of this discussion, could you (or anybody else in the know) verbalize following things:

  1. What is the difference between the function and the lambda? I am
    not talking about their syntax, I want to understand the semantic difference, if there is any.

Please read

strawman:lambdas

  1. Why is it important for a lambda to have an optional name?

It may not be.

What's wrong with using a function, if we want a name? IMHO lambda should
have the minimalistic syntax.

"Minimalistic" does not define itself. The question is what is the
minimal syntax given various constraints.

Church's Lambdas take one argument only. One can curry by hand. Why
isn't that the minimum minimum?

  1. Why is it important to be able to specify parameter defaults in lambda? Again, it looks like an extra sugar to me that can be
    covered by a function with parameter defaults.

See

esdiscuss/2008-October/007715

Also consider that default parameters are a convenience we want
lambdas to have if we believe functions should be avoided for much
lambda-coding by hand. The countervailing argument is that lambdas
have unintended completion value hazards, but Schemers and others
don't worry about these and would prefer not to have to run back to
functions and lose Tennent's Correspondence Principle every time
default parameters beckon.

The reason I ask is a lot of discussion is going around "but if it
has a name" and "but if it has a default". If it doesn't have a name I would be satisfied personally with (a, b) {...} --- it doesn't clash with anything. Or even with (a, b) expr.

You're right to question name to rescue , but trying to minimize
lambdas won't save all the proposed syntaxes. We're making progress in
finding some to be in trouble, if not fatally flawed.

# Brendan Eich (15 years ago)

I thought this might be the answer. It's clearly too much to ask of
all lambda-coders and would-be lambda-coders in the world.

My two cents, perhaps I'm wrong and the Schemers and others will
switch their kbd configs. Or the code generators will rise and
exterminate lambda-coding humans. But I doubt it.

# Breton Slivka (15 years ago)

On Fri, Dec 5, 2008 at 1:10 PM, Brendan Eich <brendan at mozilla.com> wrote:

I thought this might be the answer. It's clearly too much to ask of all lambda-coders and would-be lambda-coders in the world.

My two cents, perhaps I'm wrong and the Schemers and others will switch their kbd configs. Or the code generators will rise and exterminate lambda-coding humans. But I doubt it.

/be

That's why you'd map it to l <<tab>>

in your ide.

Also, you wouldn't be inconveniencing all lambda coders in the world. Only the ones without greek keyboards. Are there just not enough greek javascripters to matter?

# Breton Slivka (15 years ago)

On Fri, Dec 5, 2008 at 1:10 PM, Brendan Eich <brendan at mozilla.com> wrote:

I thought this might be the answer. It's clearly too much to ask of all lambda-coders and would-be lambda-coders in the world.

My two cents, perhaps I'm wrong and the Schemers and others will switch their kbd configs. Or the code generators will rise and exterminate lambda-coding humans. But I doubt it.

/be

approaching it from the other side of the question, it seems that people with german keyboards would have a similarly difficult time with the pipe character.

example: forums.macosxhints.com/archive/index.php/t-29410.html

It's the same issue with possibly any of the other symbols that have been discussed for the syntax. It doesn't really matter what you pick. If it's not "lambda", you're inconveniencing someone.

# Brendan Eich (15 years ago)

On Dec 4, 2008, at 6:39 PM, Breton Slivka wrote:

On Fri, Dec 5, 2008 at 1:10 PM, Brendan Eich <brendan at mozilla.com>
wrote:

I thought this might be the answer. It's clearly too much to ask of
all lambda-coders and would-be lambda-coders in the world.

My two cents, perhaps I'm wrong and the Schemers and others will
switch their kbd configs. Or the code generators will rise and exterminate lambda-coding humans. But I doubt it.

/be

That's why you'd map it to l <<tab>> in your ide.

I don't have an ide -- March has some, but they bode ill :-P.

Seriously, of course most users could figure out how to inject a Greek
Lambda, but add up all that effort imposed on probably thousands to
millions. It's an imposition. It is not a cost free good. Why is it so
important to use a non-ASCII character?

Also, you wouldn't be inconveniencing all lambda coders in the world. Only the ones without greek keyboards. Are there just not enough greek javascripters to matter?

Heh. While I would like to think so, I doubt it. But you never know...

# Michael Day (15 years ago)

Please read

strawman:lambdas

There is a lot of discussion over whether it is necessary to introduce syntax sugar instead of a "lambda" keyword, but is there any remaining controversy over the semantics of lambdas in JavaScript, or is that considered settled at this point?

(To throw some more kerosene on the syntax fire, I would point out that "fun" for function nicely resembles "var" for variable:

 var x = fun y z => y + z;

but it's not big deal :)

Best ,

Michael

# Eric Suen (15 years ago)

No, "" worse than '^' or '&', why not use

function ^ Identifier ( parameters ) block for declaration

and use

^ IdentifierOpt ( parameters ) block for expression

ExpressionStatement ::= [lookahead !{ {, function, ^ }] CommaExpression

# Brendan Eich (15 years ago)

On Dec 4, 2008, at 7:09 PM, Michael Day wrote:

Hi Brendan,

Please read strawman:lambdas

There is a lot of discussion over whether it is necessary to
introduce syntax sugar instead of a "lambda" keyword, but is there
any remaining controversy over the semantics of lambdas in
JavaScript, or is that considered settled at this point?

I've created a bikeshedding monster, I admit. It was not entirely
misdirection on my part :-P.

The main contention about lambdas ignoring syntax is whether the
completion-value creates a hazard that needs to be treated somehow, or
even judged as fatal to the proposal.

(To throw some more kerosene on the syntax fire, I would point out
that "fun" for function nicely resembles "var" for variable:

var x = fun y z => y + z;

but it's not big deal :)

Not bad but you lost the necessary (destructuring, default parameters)
parenthesized formal list.

I toyed with 'fun' instead of 'function' in 1995 but it would have
been a misfit in the Java-esque/C-like keyword set, even with 'var'
included.

It's not bad, even now, but it may be that something shorter, or at
least spelled different from any word derived from function (fun, fn)
-- precisely because lambdas as proposed are not like functions in
many ways: arguments, this, return, completion-value.

# Michael Day (15 years ago)

The main contention about lambdas ignoring syntax is whether the completion-value creates a hazard that needs to be treated somehow, or even judged as fatal to the proposal.

Completion value, like the last thing to be evaluated in the lambda? What exactly is the nature of the hazard?

(To throw some more kerosene on the syntax fire, I would point out that "fun" for function nicely resembles "var" for variable:

var x = fun y z => y + z;

but it's not big deal :)

Not bad but you lost the necessary (destructuring, default parameters) parenthesized formal list.

Right, an arguments list should still look like an arguments list:

 var x = fun (y, z) => y + z

or with an identifier:

 var x = fun fact(n) => (x < 2 ? 1 : n * fact(n-1))

I toyed with 'fun' instead of 'function' in 1995 but it would have been a misfit in the Java-esque/C-like keyword set, even with 'var' included.

In an alternate universe, you might have used 'method' for functions with a 'this' value, saving two characters and the name function for real functions :)

# Brendan Eich (15 years ago)

On Dec 4, 2008, at 7:45 PM, Michael Day wrote:

Hi Brendan,

The main contention about lambdas ignoring syntax is whether the
completion-value creates a hazard that needs to be treated somehow,
or even judged as fatal to the proposal.

Completion value, like the last thing to be evaluated in the lambda?
What exactly is the nature of the hazard?

Functional programming favors using completion values -- function call
results propagate back up naturally this way. Chaining, filters, etc.
all work the way you want. Here's the Y combinator:

const fact = lambda(proc) { return lambda (n) { (n <= 1) ? 1 : n * proc(n-1); } }

const Y = lambda(outer) { const inner = lambda (proc) { outer(lambda (arg) { proc(proc)(arg); }); } inner(inner); }

print("5! is " + Y(fact)(5));

Adding return keywords just adds overhead, noise. One might even want
to get rid of the curly braces around lambda bodies, but the only way
to do it in the ES grammar and avoid ambiguity is to replace braces
with mandatory parentheses.

On the other hand, much JS on the web is imperative, and a lot uses a
mixed functional/imperative style. Often the last value in a function
is not the return value you want callers to be able to get, and with
functions all is well: falling off the end returns undefined.

But with lambdas, falling off the end returns the last statement's
completion value. This means people will have to write

lambda (secret) { compute(secret); void 0; }

and similar. Of course too few will remember to do this, so implicit
return values will tend to leak.

How severe a problem this might be is arguable, but we don't want to
gamble. We want user feedback based on trial implementations, and
other convincing evidence for or against.

# Brendan Eich (15 years ago)

On Dec 4, 2008, at 10:12 PM, Brendan Eich wrote:

On Dec 4, 2008, at 7:45 PM, Michael Day wrote:

Hi Brendan,

The main contention about lambdas ignoring syntax is whether the
completion-value creates a hazard that needs to be treated
somehow, or even judged as fatal to the proposal.

Completion value, like the last thing to be evaluated in the
lambda? What exactly is the nature of the hazard?

Functional programming favors using completion values -- function
call results propagate back up naturally this way. Chaining,
filters, etc. all work the way you want. Here's the Y combinator:

const fact = lambda(proc) { return lambda (n) { (n <= 1) ? 1 : n * proc(n-1); }

D'oh -- I wrote return incorrectly there. That means, by Tennent's
Correspondence Principle, that if the above were embedded in a
function, the return would force control flow to return from the
function as well as the outer lambda (the one assigned to const fact),
and the return value would be the inner lambda.

This is the other hazard with lambdas. The program equivalences
Tennent's Correspondence Principle enables are good for refactoring,
but bad for thinkos like the above.

(Honest, I didn't do it on purpose!)

# Michael Day (15 years ago)

This is the other hazard with lambdas. The program equivalences Tennent's Correspondence Principle enables are good for refactoring, but bad for thinkos like the above.

It seems like most of the problems come from lambdas being able to contain statements as well as expressions. If there were only lambda expressions, I think they would be much easier to reason about, but currently there is no way to embed loops in expressions, right?

# Maciej Stachowiak (15 years ago)

On Dec 4, 2008, at 10:27 PM, Brendan Eich wrote:

On Dec 4, 2008, at 10:12 PM, Brendan Eich wrote:

On Dec 4, 2008, at 7:45 PM, Michael Day wrote:

Hi Brendan,

The main contention about lambdas ignoring syntax is whether the
completion-value creates a hazard that needs to be treated
somehow, or even judged as fatal to the proposal.

Completion value, like the last thing to be evaluated in the
lambda? What exactly is the nature of the hazard?

Functional programming favors using completion values -- function
call results propagate back up naturally this way. Chaining,
filters, etc. all work the way you want. Here's the Y combinator:

const fact = lambda(proc) { return lambda (n) { (n <= 1) ? 1 : n * proc(n-1); }

D'oh -- I wrote return incorrectly there. That means, by Tennent's
Correspondence Principle, that if the above were embedded in a
function, the return would force control flow to return from the
function as well as the outer lambda (the one assigned to const
fact), and the return value would be the inner lambda.

This is the other hazard with lambdas. The program equivalences
Tennent's Correspondence Principle enables are good for
refactoring, but bad for thinkos like the above.

(Honest, I didn't do it on purpose!)

What exactly does return from a lambda mean? Let's say I do this:

function F(x) { return lambda(n) { return x + n; } } function G(h) { return h(1) +1; } var H = F(1); G(H);

What is the value of the last expression and why?

, Maciej

# Jon Zeppieri (15 years ago)

On Fri, Dec 5, 2008 at 12:39 PM, Maciej Stachowiak <mjs at apple.com> wrote:

What exactly does return from a lambda mean? Let's say I do this:

function F(x) { return lambda(n) { return x + n; } } function G(h) { return h(1) +1; } var H = F(1); G(H);

What is the value of the last expression and why?

Based on the lambda and return-to-label strawmen, It's an error. Dave presents a desugaring of function to lambda, where each function body is given a label at the bottom:

lambda(x0,...,xn,...$rest) { let $THIS = thisRegister; let arguments = makeAlias([[lambda() x1, lambda($x1) x1 = $x1], ..., [lambda() xn, lambda($xn) xn = $xn]], $rest); $RETURN: { Body; void 0 } }

'return e' without a label desugars to:

return : $RETURN e

But, from the return-to-label strawman:

"The dynamic return point associated with the label must still be live when the return statement is invoked; otherwise it is a dynamic error, i.e., an exception is raised."

A label is an escape continuation. Once control has returned past the point where the continuation was captured, it's dead, and it can't be resumed.

# Maciej Stachowiak (15 years ago)

On Dec 5, 2008, at 10:00 AM, Jon Zeppieri wrote:

On Fri, Dec 5, 2008 at 12:39 PM, Maciej Stachowiak <mjs at apple.com>
wrote:

What exactly does return from a lambda mean? Let's say I do this:

function F(x) { return lambda(n) { return x + n; } } function G(h) { return h(1) +1; } var H = F(1); G(H);

What is the value of the last expression and why?

Based on the lambda and return-to-label strawmen, It's an error. Dave presents a desugaring of function to lambda, where each function body is given a label at the bottom:

lambda(x0,...,xn,...$rest) { let $THIS = thisRegister; let arguments = makeAlias([[lambda() x1, lambda($x1) x1 = $x1], ..., [lambda() xn, lambda($xn) xn = $xn]], $rest); $RETURN: { Body; void 0 } }

'return e' without a label desugars to:

return : $RETURN e

But, from the return-to-label strawman:

"The dynamic return point associated with the label must still be live when the return statement is invoked; otherwise it is a dynamic error, i.e., an exception is raised."

A label is an escape continuation. Once control has returned past the point where the continuation was captured, it's dead, and it can't be resumed.

So return from a lambda is sometimes but not always a runtime error?
Other times it can return through multiple levels of function calls
without raising an exception? That seems pretty bad for ease of
understanding and for performance of implementations. If you do this:

[1 2 3].map(lambda (x) { return x + 1; })

I think it would be better for that to be a syntax error than to make
the containing function return 2.

It seems to me the current design prioritizes lambda as a desugaring
construct or building block for imperative-style control flow, over
use as an actual first-class function. I assume break and continue
inside a lambda have similar issues.

, Maciej

# Mark Miller (15 years ago)

On Fri, Dec 5, 2008 at 10:22 AM, Maciej Stachowiak <mjs at apple.com> wrote:

It seems to me the current design prioritizes lambda as a desugaring construct or building block for imperative-style control flow, over use as an actual first-class function. I assume break and continue inside a lambda have similar issues.

Yes. I was initially hopeful that lambdas would be usable in general as a replacement for function, in much the same way that we are all hopeful than const and let are usable as a replacement for var. However, after talking about the hazards Waldemar raised of leaking completion value, I think your statement above is accurate. It results in the sugared language having the same two levels as in Smalltalk and E: The "function" level[1], where a "return" is required in order to provide a value to one's caller, and a "lambda" level, recommended for use only for desugaring and control abstraction, where completion values leak, and where "return" remains bound to the enclosing "function".

In other words, "let" is the new "var", but "lambda" is less than the new "function" and more than the new block. That's why I argued against David-Sarah's otherwise very pleasant pattern for writing an enhanced object literal to express high integrity class-like abstractions.

[1] ES-Harmony's "function" like Smalltalk's method like E's "to" ES-Harmony's "lambda" like Smalltalk's block "[" like E's "method". ES-Harmony's "return" like Smalltalk's "^" like E's "return"

# Allen Wirfs-Brock (15 years ago)

-----Original Message----- From: es-discuss-bounces at mozilla.org [mailto:es-discuss- bounces at mozilla.org] On Behalf Of Mark Miller ... In other words, "let" is the new "var", but "lambda" is less than the new "function" and more than the new block. That's why I argued against David-Sarah's otherwise very pleasant pattern for writing an enhanced object literal to express high integrity class-like abstractions. ...

In which case perhaps we should abandon this style of lambda and work harder at developing a concise construct that is a reformed function.

I'll have to hunt down David-Sarah's proposal which I haven't seen as this is a direction for object literals is something I have also thought about and found attractive.

Given, the nature of the language we have (as opposed to the language we wish we had) I might choose class-like abstractions based upon object literals over conflicting features for support concise expression of control abstraction.

# Brendan Eich (15 years ago)

On Dec 5, 2008, at 11:47 AM, Allen Wirfs-Brock wrote:

Given, the nature of the language we have (as opposed to the
language we wish we had) I might choose class-like abstractions
based upon object literals over conflicting features for support
concise expression of control abstraction.

The return hazard exists in Smalltalk too. What's different here that
makes you choose differently? Of course, there are more choices than
Tennent-to-the-max lambdas or-else classes-as-sugar.

# Allen Wirfs-Brock (15 years ago)

From: Brendan Eich [mailto:brendan at mozilla.com]

The return hazard exists in Smalltalk too. What's different here that makes you choose differently? Of course, there are more choices than Tennent-to-the-max lambdas or-else classes-as-sugar.

The difference is in the foundation language we are starting with. Because of the central role of c-style syntactic control constructs in JavaScript it is unlikely that lambda-based control abstractions will ever be as important in JavaScript as they are in Smalltalk. On the other hand, object literals are core to JavaScript as they are the only mechanism in the current language for declaratively defining a new "kind" of object. Using JavaScript, every significant application probably needs to define new object abstractions but far fewer apps need to define new control abstractions. If it is necessary to make a choice I'm inclined to prioritize enhancing object literals to make them be a better object/class abstraction mechanisms over enhancing lambdas to make them a better control abstraction mechanism.

The return hazard is not a significant problem in Smalltalk. This is probably because of the pervasive use of blocks (closures) for all control structures including the simplest if statements. Every Smalltalk programmer learns at the outset that the lexical occurrence of a ^ (ie, "return") anywhere in a method (even with within a nested block) means to return from that method. They generally learn this even before they learn that full semantics of [ ] (ie, lambda). So, there is really never any confusion about whether a ^ was intended to mean return from the block as opposed to return from a method. Occasionally, (actually pretty rarely) situations arise where it would be convenient to explicitly express returning from a block evaluation rather than the method. However, I've never seen a situations where that result couldn't be achieved by restructuring the method so the return case falls off the bottom. Various people have toyed with creating some sort of explicit local block return syntax for Smalltalk (for example ^^) but there are significant complications (since Smalltalk only has block based conditionals the local return would really be a situation of an inner block forcing a return from an outer block) and the need is quite limited. Finally, if restructuring the block doesn't solve the problem, Smalltalk's very flexible exception handling abstractions can probably be used to accomplish a similar result.

Don't get me wrong, I like the semantics of break/continue/return that have been proposed for JavaScript lambdas but given our legacy I can see the return hazard being a real problem. And if it is a choice between enhanced object literals and control abstracting lambdas I'd probably go with the object literals.

# Eugene Lazutkin (15 years ago)

Thank you for useful links and explanations. Correct me if I am wrong but in the current form lambda is a facility that duplicates a function. More than that it reminds my old languages that had separate keywords for functions (our "lambda"?) and procedures (our "function"?).

Writing code I frequently need small functions (or lambdas). The smaller the code the better --- it allows to be concise and does not obscure the intent. That's why I prefer to use lambda's proposed by Oliver Steele (osteele.com/sources/javascript/functional), which have a lot of problems being a pure JavaScript implementation. Writing a factorial using a linear recursion combinator with lambdas:

var fact1 = linrec("<= 1", "1", "[n - 1]", "m * n[0]");

is simple and more readable than the equivalent:

var fact2 = linrec( function(n){ return n <= 1; }, function(){ return 1; }, function(n){ return [n - 1]; }, function(m, n){ return m * n[0]; });

I typed 200% more text, and the readability went down by the same 200% --- because I added 200% of the technical noise not relevant to the algorithm itself. Anything that improves on that is good in my book. Lambdas are good:

var fact3 = linrec( lambda(n) n <= 1, lambda() 1, lambda(n) [n - 1], lambda(m, n) m * n[0]);

Shortcuts for lambdas are better:

var fact4 = linrec((n) n <= 1, () 1, (n) [n - 1], (m, n) m * n[0]);

I perceive them as less noisy.

The link you gave features long lambdas and I don't see what they buy vs. the regular functions. This is the example from that page:

lambda(i) { if (!isNumeric(i)) // etc. else if (i < 0) // etc. else if (i < params.length) params[i]0 else { i -= params.length; if (i < rest.length) rest[i] else // etc. } }

Written as function it is not that long or less clear:

function(i) { if (!isNumeric(i)) // etc. else if (i < 0) // etc. else if (i < params.length) return params[i]0 else { i -= params.length; if (i < rest.length) return rest[i] else // etc. } }

My point is we gain more by concentrating on small light-weight snippets than on one more way to code a big function.

So concentrating on small snippets:

  1. Reducing "technicalities" and the boilerplate improves the clarity of the code.

1a) I don't mind if lambdas don't have their own "this", "arguments", or a scope --- from my experience they are rarely used in small snippets.

1b) I am all for skipping "return" --- it reduces the boilerplate for small snippets.

  1. Named lambdas, and parameter defaults are of little value. Use functions if you truly need a named functionality. Otherwise assign it to a variable and pass around (rarely needed).

  2. Losing the keyword "lambda" in favor of a small shortcut (e.g., ) will be of great value --- less noise, less boilerplate, less typing, less opportunities to mistype.

I suggest paring down "lambda" by shedding names, default parameters, and possibly the keyword "lambda" itself --- it reduces complexity, no chance for ambiguity, easier to implement, easier to use.

Thanks,

Eugene

# Brendan Eich (15 years ago)

On Dec 5, 2008, at 12:49 PM, Allen Wirfs-Brock wrote:

From: Brendan Eich [mailto:brendan at mozilla.com]

The return hazard exists in Smalltalk too. What's different here that makes you choose differently? Of course, there are more choices than Tennent-to-the-max lambdas or-else classes-as-sugar.

The difference is in the foundation language we are starting with.
Because of the central role of c-style syntactic control constructs
in JavaScript it is unlikely that lambda-based control abstractions
will ever be as important in JavaScript as they are in Smalltalk.

Agreed so far. The long-term plan here would be macros. Post-Harmony
at this point.

[snip] If it is necessary to make a choice I'm inclined to
prioritize enhancing object literals to make them be a better
object/class abstraction mechanisms over enhancing lambdas to make
them a better control abstraction mechanism.

I question the need to make a choice (yet).

I'm actually concerned about usability of lambdas as anything similar
to functions. Say they're added, and they prove popular for control
abstractions and other purposes, including "better functions". Then
not only will completion-value leaks bite people -- misplaced return
probably will too, if "better functions" involves porting existing
code from function to lambda.

This is all speculation, but here's my non-speculative claim: lambda
syntax should not look so much like function syntax if return within
the body behaves completely differently. We would want syntax very
different from function (i.e., not lambda (args) {body} -- sorry,
Peter Michaux). Or else we should revisit the wisdom of applying TCP
to lambdas.

[Smalltalk observations snipped -- thanks for those, they make sense
but I want to keep pushing on what doesn't work in the current
strawman: wiki space.]

Don't get me wrong, I like the semantics of break/continue/return
that have been proposed for JavaScript lambdas but given our legacy
I can see the return hazard being a real problem.

I agree if lambda looks like function or is sold as a "better
function". If it looks more like a block, or something else, that
might mitigate the return hazard. Michael Day wondered if we confined
the body to an expression language -- that would eliminate the return
hazard.

And if it is a choice between enhanced object literals and control
abstracting lambdas I'd probably go with the object literals.

No false dilemmas yet, please!

# Michael Day (15 years ago)

This is all speculation, but here's my non-speculative claim: lambda syntax should not look so much like function syntax if return within the body behaves completely differently. We would want syntax very different from function (i.e., not lambda (args) {body} -- sorry, Peter Michaux). Or else we should revisit the wisdom of applying TCP to lambdas.

I'm unconvinced that TCP needs to apply to statements; it seems like a more valuable property when applied to expressions, even though JavaScript is not referentially transparent to begin with.

Anyway, these three options look good to me:

(1) Expression lambdas: lambdas whose body is an expression.

var x = lambda(y, z) y + z

Solves the problem with completion leakage, solves the nested return/break/continue issue. However, quite limited in usage, and makes it difficult to use lambdas to replace functions as they can't contain loop statements. (Hello, recursion! :)

(2) Function lambdas: objects just like functions, except no "this" or "arguments", and perhaps some guarantees about tail calls?

var x = lambda(y, z) { return y + z }

This seems the easiest for programmers to understand, and avoids the return/break issues. It violates TCP for statements, but I don't think that really matters in practice; after all, so do functions.

(3) Parametric blocks: where a block, possibly taking arguments, can be passed around as an object. The key use-case for this seems to be creating new control abstractions. I would argue that blocks should not be usable as expressions, and the completion value cannot be captured (unless using eval) for consistency with existing statement behaviour.

var a = block { ... statements ... } var b = block(x, y) { ... statements using x and y ... }

call b(1, 2); // this is a statement, not an expression

Unfortunately, object literals also look like blocks, and there is no perfect syntax for this that fits neatly into the existing language. without using bulky keywords. While this option preserves TCP, I don't think JavaScript really needs this feature, and the power/complexity ratio doesn't measure up.

I agree if lambda looks like function or is sold as a "better function". If it looks more like a block, or something else, that might mitigate the return hazard. Michael Day wondered if we confined the body to an expression language -- that would eliminate the return hazard.

I like options (1) and (2) above. The current proposal on the wiki feels like all three options mashed together, and I find it difficult make sense of it as a basic construct.

# Brendan Eich (15 years ago)

On Dec 5, 2008, at 2:57 PM, Michael Day wrote:

This is all speculation, but here's my non-speculative claim:
lambda syntax should not look so much like function syntax if
return within the body behaves completely differently. We would
want syntax very different from function (i.e., not lambda (args)
{body} -- sorry, Peter Michaux). Or else we should revisit the
wisdom of applying TCP to lambdas.

I'm unconvinced that TCP needs to apply to statements; it seems like
a more valuable property when applied to expressions, even though
JavaScript is not referentially transparent to begin with.

Good points.

Anyway, these three options look good to me:

(1) Expression lambdas: lambdas whose body is an expression.

var x = lambda(y, z) y + z

Would need parens around the body, if this is a primary expression,
else reduce-reduce conflict.

Solves the problem with completion leakage, solves the nested return/ break/continue issue. However, quite limited in usage, and makes it
difficult to use lambdas to replace functions as they can't contain
loop statements. (Hello, recursion! :)

We could mandate tail recursive call sites in the spec so people could
count on it cross-browser.

bugs.ecmascript.org/ticket/215, bugs.ecmascript.org/ticket/215 (not be necessary for expression- enclosed tail calls)

(2) Function lambdas: objects just like functions, except no "this"
or "arguments", and perhaps some guarantees about tail calls?

var x = lambda(y, z) { return y + z }

This seems the easiest for programmers to understand, and avoids the
return/break issues. It violates TCP for statements, but I don't
think that really matters in practice; after all, so do functions.

I must agree, since this looks like function syntax, with a new
introductory keyword. TCP must yield.

(3) Parametric blocks: where a block, possibly taking arguments, can
be passed around as an object. The key use-case for this seems to be
creating new control abstractions. I would argue that blocks should
not be usable as expressions, and the completion value cannot be
captured (unless using eval) for consistency with existing statement
behaviour.

var a = block { ... statements ... } var b = block(x, y) { ... statements using x and y ... }

call b(1, 2); // this is a statement, not an expression

Unfortunately, object literals also look like blocks, and there is
no perfect syntax for this that fits neatly into the existing
language. without using bulky keywords. While this option preserves
TCP, I don't think JavaScript really needs this feature, and the
power/complexity ratio doesn't measure up.

Agreed. Past efforts to add just this kind of block have failed to get
anywhere.

I agree if lambda looks like function or is sold as a "better
function". If it looks more like a block, or something else, that
might mitigate the return hazard. Michael Day wondered if we
confined the body to an expression language -- that would eliminate
the return hazard.

I like options (1) and (2) above. The current proposal on the wiki
feels like all three options mashed together, and I find it
difficult make sense of it as a basic construct.

Including Dave to get his thoughts, in case he is reading es-discuss
in a digest or deferred fashion.

# David-Sarah Hopwood (15 years ago)

Breton Slivka wrote:

On Fri, Dec 5, 2008 at 7:49 AM, David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk> wrote:

Mark S. Miller wrote:

[...] "" has taken the lead.... There's still #, @, and ` (and of course keywords like lambda and fn). None of these are as mnemonic as , but they leave \ as a purely lexical escape character.

It's quite ironic that we are still limited, as Church was, in which characters we can use for the modern equivalent of "typographical reasons".

this may be a stupid question, but why? Is it really so impossible to have λ(a,b,c){} ? You guys seem to have no trouble typing it.

To type λ, I usually have to cut-and-paste it from somewhere else, which is quite inconvenient. But more importantly, having a non-US-ASCII character in the basic syntax means that parsing is dependent on recognising character encoding accurately. In practice, that is very hit-and-miss, with files' encoding often being labelled or guessed incorrectly. Currently, the effects of this are restricted to programs that use non-US-ASCII characters in strings without escaping, and therefore only the files containing such programs have to be labelled accurately.

(I wish it weren't so, and often the reason why it is so is inexcusably poor attention to standards by application writers, but we have to be realistic.)

# David-Sarah Hopwood (15 years ago)

Breton Slivka wrote:

On Fri, Dec 5, 2008 at 9:35 AM, Brendan Eich <brendan at mozilla.com> wrote:

On Dec 4, 2008, at 2:31 PM, Breton Slivka wrote:

I admit this seems ludicrous at its face, but admittedly I have not really seen the arguments against λ as an abbreviated lambda syntax yet. [...]

It's still too hard to type.

picasaweb.google.com/eileen.world.traveler/EileenBestOfGreece#5139474493916668850

Using a Greek keyboard might, for the sake of argument, also be feasible for users whose main language is English, but not for those who require other keyboard layouts in order to type their main language.

# David-Sarah Hopwood (15 years ago)

Maciej Stachowiak wrote:

What exactly does return from a lambda mean? Let's say I do this:

function F(x) { return lambda(n) { return x + n; } } function G(h) { return h(1) +1; } var H = F(1); G(H);

What is the value of the last expression and why?

The lambda would return from F (specifically, from the activation of F in which the lambda was created), but since that activation of F has already returned when the lambda is called, this is a dynamic error -- presumably, resulting in an exception.

What that example doesn't show it is whether the exception is thrown only when the offending 'return' statement is executed, or when a a lambda that could execute an offending return is entered.

Also note that this example could be affected by the resolution of the "leakage" issue, if we decide for example that the syntax 'lambda(n) { return x + n; }' returns from the lambda rather than the enclosing function (which it could do without necessarily violating the Tennent Correspondence rationale for lambdas, as long as there is another syntax that doesn't bind 'return').

# David-Sarah Hopwood (15 years ago)

Maciej Stachowiak wrote:

On Dec 5, 2008, at 10:00 AM, Jon Zeppieri wrote:

A label is an escape continuation. Once control has returned past the point where the continuation was captured, it's dead, and it can't be resumed.

So return from a lambda is sometimes but not always a runtime error?

That in itself does not seem much of a problem (it doesn't seem to be in Smalltalk or E).

Other times it can return through multiple levels of function calls without raising an exception? That seems pretty bad for ease of understanding and for performance of implementations.

I agree, although I would express this as a safety issue: the safety hazard is that you may call a lambda without expecting it to perform a jump within the calling scope. The performance issue is that all calls that are within the scope of a labelled statement or loop must check for an escape.

There is a possibility for solving both of these issues while still allowing all of the suggested uses of lambdas. It involves permitting a given call to perform an escape (that is, break, continue or return) only if it is explicitly marked as being able to do so. If an unmarked call would escape, then it instead throws an exception.

For example (syntax intended only as a strawman; I know it is incompatible to add an 'escape' keyword):

CallExpression : ... 'escape' CallExpression Arguments

Then a user-defined 'while' could be written as:

const _while = (cond, body) { // escapes from 'cond' are intentionally disallowed; // only 'body' can escape. if (cond()) { escape body(); escape while(cond, body); } };

and a use of '_while' would be:

foo: escape _while({bar}, { ... if (baz) break foo; });

This doesn't impede the use of lambdas in control abstractions or in desugaring, because those uses can easily call the lambda using an 'escape' call. Nor does it impede "lambda as the new function", because uses of lambda in place of function won't attempt to 'break', 'continue' or 'return' (and that can be made a static error in the syntactic contexts where lambda is intended to replace functions, such as in my suggestion for object literals).

It does introduce the syntactic inconvience of having to call control abstractions using 'escape' (or whatever syntax is used for these calls). For use of lambda to desugar built-in constructs, this is not an issue.

If at some point macros were added (even a very limited form of macros), then the syntactic inconvience could be alleviated, and nothing would have to be changed in the semantics of lambda at that point.

If you do this:

[1 2 3].map(lambda (x) { return x + 1; })

I think it would be better for that to be a syntax error than to make the containing function return 2.

In my proposal above, that would be a dynamic error (the call to the lambda from 'map' would throw an exception).

This case could in principle be detected statically; that is, passing a lambda expression directly as an argument without using 'escape' would be a static error.

Unfortunately it's not possible to detect all of the errors introduced by this proposal statically, because the lambda could have been obtained from anywhere, so without static typing (or making lambdas not-first-class, which I don't recommend), it's not possible to know in all cases when a lambda is being passed as an argument.

It seems to me the current design prioritizes lambda as a desugaring construct or building block for imperative-style control flow, over use as an actual first-class function. I assume break and continue inside a lambda have similar issues.

They do, but the proposal above addresses 'break', 'continue' and 'return' together.

It may be considered too complicated, too restrictive, or not sufficiently easy to understand. I'm undecided about that, although it does solve both the safety and performance objections to escape continuations, and the semantics are easy to specify.

# David-Sarah Hopwood (15 years ago)

Peter Michaux wrote:

Summary of what I've read:

The syntax (){} has a named lambda problem if the lambda name starts with a 'u'.

Why do we need named lambdas (or more precisely, why do we need them to be named by the lambda syntax)? A lambda can always be named by assigning it to a 'const' variable.

# David-Sarah Hopwood (15 years ago)

Peter Michaux wrote:

The syntax ^(){} has a semicolon insertion ambiguity. What does the following mean?

x = x ^(){}

It's not ambiguous; it would mean "x = x^(){}", which is (in this case) a syntax error.

'-' and '/' already have a similar issue of possibly unexpected parsing when they occur at the beginning of a line. It's just that it is rare to intend to start an ExpressionStatement with a unary minus or a regexp literal, so this doesn't occur very often in practice. It's not clear how often programmers will intend to start an ExpressionStatement with a lambda, although I can see that this might be more common.

Note that programmers who never deliberately rely on semicolon insertion will not be surprised by this example; they will consider writing "x = x" without an terminating semicolon to have been their mistake.

# David-Sarah Hopwood (15 years ago)

David-Sarah Hopwood wrote:

Peter Michaux wrote:

The syntax ^(){} has a semicolon insertion ambiguity. What does the following mean?

x = x ^(){}

It's not ambiguous; it would mean "x = x^(){}", which is (in this case) a syntax error.

'-' and '/' already have a similar issue of possibly unexpected parsing when they occur at the beginning of a line.

and '+', '++', '--', '(', and (for a different reason) 'function'.

Actually, the case of '(' is arguably worse than '^', because '(' at the start of an ExpressionStatement is very common:

x = x (foo)

(parsed as 'x = x(foo);', but intended to be 'x = x; (foo);').

# David-Sarah Hopwood (15 years ago)

David-Sarah Hopwood wrote:

David-Sarah Hopwood wrote:

'-' and '/' already have a similar issue of possibly unexpected parsing when they occur at the beginning of a line.

and '+', '++', '--', '(', and (for a different reason) 'function'.

Correction: not '++' or '--', because they are "restricted productions".

# David-Sarah Hopwood (15 years ago)

P T Withington wrote:

Would it work to move the parameter list inside the block (as in the Smalltalk way, but as a regular parameter list, not using ||'s)?

{(a, b) a + b}

AFAICT, {( is a syntax error for an expression in es3.

I think this is unambiguous, but I don't like it because it has no symbol or combination of symbols that is specific to a lambda. ( "{(" can occur as the start of a block.)

# Breton Slivka (15 years ago)

On Sat, Dec 6, 2008 at 9:57 AM, Michael Day <mikeday at yeslogic.com> wrote:

(1) Expression lambdas: lambdas whose body is an expression.

var x = lambda(y, z) y + z

Solves the problem with completion leakage, solves the nested return/break/continue issue. However, quite limited in usage, and makes it difficult to use lambdas to replace functions as they can't contain loop statements. (Hello, recursion! :)

This idea appeals to me for a couple reasons:

  1. I have occassionally found myself using and repeating fairly complex expressions in the conditions of if statements. Functions would work, but I tend to avoid them out of a (possibly misplaced?) Perception that refactoring the expressions isn't quite worth the performance cost of an extra function call. This is bad code practice I will admit, but if there were something that didn't have quite the performance cost, and could promote DRYness in expressions, that would be great.

  2. It would be really nice to have a callable value that was garaunteed not to have side effects. a lambda with an expression body might not be that. Nevertheless, this would enable a parallelized array "map" function that's safe to use. In the absence of "real" multithreading, this kind of parallelism would be a boon for applications like 3d games, or image processing.

2008/12/6 David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk>:

To type λ, I usually have to cut-and-paste it from somewhere else, which is quite inconvenient. But more importantly, having a non-US-ASCII character in the basic syntax means that parsing is dependent on recognising character encoding accurately. In practice, that is very hit-and-miss, with files' encoding often being labelled or guessed incorrectly. Currently, the effects of this are restricted to programs that use non-US-ASCII characters in strings without escaping, and therefore only the files containing such programs have to be labelled accurately.

(I wish it weren't so, and often the reason why it is so is inexcusably poor attention to standards by application writers, but we have to be realistic.)

-- David-Sarah Hopwood

I thought as such, fair enough. It was just naive and boundless optimism that in 5-10 years time, this would cease to be an issue, but this is magical thinking.

# Jon Zeppieri (15 years ago)

On Fri, Dec 5, 2008 at 11:07 PM, David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk> wrote:

Maciej Stachowiak wrote:

On Dec 5, 2008, at 10:00 AM, Jon Zeppieri wrote:

A label is an escape continuation. Once control has returned past the point where the continuation was captured, it's dead, and it can't be resumed.

So return from a lambda is sometimes but not always a runtime error?

That in itself does not seem much of a problem (it doesn't seem to be in Smalltalk or E).

Other times it can return through multiple levels of function calls without raising an exception? That seems pretty bad for ease of understanding and for performance of implementations.

I agree, although I would express this as a safety issue: the safety hazard is that you may call a lambda without expecting it to perform a jump within the calling scope. The performance issue is that all calls that are within the scope of a labelled statement or loop must check for an escape.

I don't get it. What issue is raised by return-to-label that isn't already raised by exceptions? They're practically the same thing, only return-to-label is easier to analyze statically, because 'return' can only jump to a label that is lexically (not just dynamically) in scope.

There is a possibility for solving both of these issues while still allowing all of the suggested uses of lambdas. It involves permitting a given call to perform an escape (that is, break, continue or return) only if it is explicitly marked as being able to do so. If an unmarked call would escape, then it instead throws an exception.

For example (syntax intended only as a strawman; I know it is incompatible to add an 'escape' keyword):

CallExpression : ... 'escape' CallExpression Arguments

Then a user-defined 'while' could be written as:

const _while = (cond, body) { // escapes from 'cond' are intentionally disallowed; // only 'body' can escape. if (cond()) { escape body(); escape while(cond, body); } };

Can the call to cond throw? If so, what did you gain from this?

# Eric Suen (15 years ago)

This is ambiguous

{(a, b) a + b}

is

{(a,b); a+b }

unless use no line break restrict and it is difficult to parse

# Maciej Stachowiak (15 years ago)

On Dec 5, 2008, at 11:12 PM, Jon Zeppieri wrote:

I don't get it. What issue is raised by return-to-label that isn't already raised by exceptions? They're practically the same thing, only return-to-label is easier to analyze statically, because 'return' can only jump to a label that is lexically (not just dynamically) in scope.

If you want to call a function and make sure control flow does not
escape, then in the face of exceptions alone you can wrap it in try/ catch. However, with multi-level returning lambdas, if you are passed
a function then you have no way to prevent it from returning early,
since it could be a lambda in the lexical scope of your caller.

, Maciej

# Jon Zeppieri (15 years ago)

On Sat, Dec 6, 2008 at 11:49 AM, Maciej Stachowiak <mjs at apple.com> wrote:

On Dec 5, 2008, at 11:12 PM, Jon Zeppieri wrote:

I don't get it. What issue is raised by return-to-label that isn't already raised by exceptions? They're practically the same thing, only return-to-label is easier to analyze statically, because 'return' can only jump to a label that is lexically (not just dynamically) in scope.

If you want to call a function and make sure control flow does not escape, then in the face of exceptions alone you can wrap it in try/catch. However, with multi-level returning lambdas, if you are passed a function then you have no way to prevent it from returning early, since it could be a lambda in the lexical scope of your caller.

The strawman contains the following text:

"Unwinding the execution context may pass through finally blocks, which execute and may perform their own control effects, effectively canceling the unwinding."

So, you have a dynamic-wind-like mechanism, if you need it.

Also, what was the performance issue?

# Maciej Stachowiak (15 years ago)

On Dec 6, 2008, at 9:57 AM, Jon Zeppieri wrote:

On Sat, Dec 6, 2008 at 11:49 AM, Maciej Stachowiak <mjs at apple.com>
wrote:

On Dec 5, 2008, at 11:12 PM, Jon Zeppieri wrote:

I don't get it. What issue is raised by return-to-label that isn't already raised by exceptions? They're practically the same thing, only return-to-label is easier to analyze statically, because 'return' can only jump to a label that is lexically (not just dynamically) in scope.

If you want to call a function and make sure control flow does not
escape, then in the face of exceptions alone you can wrap it in try/catch.
However, with multi-level returning lambdas, if you are passed a function
then you have no way to prevent it from returning early, since it could be a
lambda in the lexical scope of your caller.

The strawman contains the following text:

"Unwinding the execution context may pass through finally blocks, which execute and may perform their own control effects, effectively canceling the unwinding."

So, you have a dynamic-wind-like mechanism, if you need it.

I guess then the damage can be contained, but it's unusual to use a
mechanism like this for normal control flow rather than just
exceptional conditions.

Also, what was the performance issue?

It turns return inside a lambda into a construct that has to unwind
the stack (and apparently run finally handlers), which makes its cost
more like the cost of throwing an exception than the cost of a normal
return. In most implementations, throwing an exception is much more
expensive. Actually, it could be worse than throwing an exception,
since if you can't actually unwind the call stack until you find
whether the lambda's containing function is currently on the stack.

, Maciej

# Jon Zeppieri (15 years ago)

On Sat, Dec 6, 2008 at 2:03 PM, Maciej Stachowiak <mjs at apple.com> wrote:

On Dec 6, 2008, at 9:57 AM, Jon Zeppieri wrote:

On Sat, Dec 6, 2008 at 11:49 AM, Maciej Stachowiak <mjs at apple.com> wrote:

On Dec 5, 2008, at 11:12 PM, Jon Zeppieri wrote:

I don't get it. What issue is raised by return-to-label that isn't already raised by exceptions? They're practically the same thing, only return-to-label is easier to analyze statically, because 'return' can only jump to a label that is lexically (not just dynamically) in scope.

If you want to call a function and make sure control flow does not escape, then in the face of exceptions alone you can wrap it in try/catch. However, with multi-level returning lambdas, if you are passed a function then you have no way to prevent it from returning early, since it could be a lambda in the lexical scope of your caller.

The strawman contains the following text:

"Unwinding the execution context may pass through finally blocks, which execute and may perform their own control effects, effectively canceling the unwinding."

So, you have a dynamic-wind-like mechanism, if you need it.

I guess then the damage can be contained, but it's unusual to use a mechanism like this for normal control flow rather than just exceptional conditions.

I'd say that under the proposed semantics, return from lambda isn't normal control flow; it's a (potentially) non-local jump. Normal "return" inside a lambda is just falling off the end.

Also, what was the performance issue?

It turns return inside a lambda into a construct that has to unwind the stack (and apparently run finally handlers), which makes its cost more like the cost of throwing an exception than the cost of a normal return.

Yes, because it is very similar to throwing an exception. Would you prefer that return inside lambda instead return from the lambda's own activation? That could be done, with some violence to TCP.

In the most common case, however -- namely, return from function, which, under Dave's proposal, desugars to a return from lambda -- couldn't the additional cost be optimized away easily? You can determine statically that the jump doesn't unwind the stack, so the cost of returning should remain the same.

In most implementations, throwing an exception is much more expensive. Actually, it could be worse than throwing an exception, since if you can't actually unwind the call stack until you find whether the lambda's containing function is currently on the stack.

Okay, but you only incur this expense when you actually take the non-local exit. There is no reason why normal returns should be more expensive.

# David-Sarah Hopwood (15 years ago)

Eric Suen wrote:

This is ambiguous

{(a, b) a + b}

is

{(a,b); a+b }

This example isn't ambiguous, because an ExpressionStatement cannot start with '{', therefore this is a block. However the fact that a lambda starting an ExpressionStatement would have to be parenthesized is a valid argument against this syntax, and also against {|a, b| a+b}.

unless use no line break restrict and it is difficult to parse

It's not actually difficult to parse (since an object literal cannot have '(' after the '{'), but I don't think it has any advantages over syntaxes starting with '^' or ''.

('^' is back on the table given that the semicolon insertion hazard that caused us to be suspicious of it, already exists when a line starts with '(', for example.)

# David-Sarah Hopwood (15 years ago)

Jon Zeppieri wrote:

On Sat, Dec 6, 2008 at 11:49 AM, Maciej Stachowiak <mjs at apple.com> wrote:

On Dec 5, 2008, at 11:12 PM, Jon Zeppieri wrote:

I don't get it. What issue is raised by return-to-label that isn't already raised by exceptions? [...] Also, what was the performance issue?

The (minor) performance issue is that if there is a lambda that returns from a given function, all calls within that function body must check for an escape, even if the lambda is never passed to them or otherwise accessible to them. Similarly for calls within the scope of a labelled statement or iteration that contains a lambda with a corresponding 'break' or 'continue'.

Exceptions, OTOH, are implementable without an explicit check on each function call.

Just this performance issue on its own wouldn't be significant -- it might matter in a language that is otherwise highly optimizable, but not in ECMAScript. I'll address the safety issue separately.

# Eric Suen (15 years ago)

But in page:

strawman:lambdas

lambda is not just a expression, it could be a Declaration.

If lambda is only a expression, that is why I suggest in post:

esdiscuss/2008-December/008382

# David-Sarah Hopwood (15 years ago)

Eric Suen wrote:

But in page:

strawman:lambdas

lambda is not just a expression, it could be a Declaration.

If lambda is only a expression, that is why I suggest in post:

esdiscuss/2008-December/008382

No, "" worse than '^' or '&', why not use

function ^ Identifier ( parameters ) block for declaration

and use

^ IdentifierOpt ( parameters ) block for expression

ExpressionStatement ::= [lookahead !{ {, function, ^ }] CommaExpression

The keyword 'function' shouldn't be used for this because a lambda is not a function. However,

const name(formals) ... let name(formals) ...

could be sugar for

const name = lambda(formals) ...; let name = lambda(formals) ...;

(replacing 'lambda' with whatever symbol or keyword is chosen). Then there is no need to change the negative lookahead in ExpressionStatement.

# Peter Michaux (15 years ago)

On Sat, Dec 6, 2008 at 7:51 PM, David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk> wrote:

The keyword 'function' shouldn't be used for this because a lambda is not a function. However,

const name(formals) ... let name(formals) ...

could be sugar for

const name = lambda(formals) ...;

Does "const" have "var" or "let" scoping...or is it even a declaration at all? Although it is bulky it might be better to write "const var" and "const let". A variable being constant or not is orthogonal to its scoping and should be controlled independently.

let name = lambda(formals) ...;

I mentioned this a while back. I think it might be a good idea. Scheme has

(define foo (lambda (a b) 1)) (define (foo a b) 1)

which could be translated to ES

var foo = lambda(a, b) 1; var foo(a, b) 1;

Peter

# Brendan Eich (15 years ago)

On Dec 6, 2008, at 9:19 PM, Peter Michaux wrote:

On Sat, Dec 6, 2008 at 7:51 PM, David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk> wrote:

The keyword 'function' shouldn't be used for this because a lambda is not a function. However,

const name(formals) ... let name(formals) ...

could be sugar for

const name = lambda(formals) ...;

Does "const" have "var" or "let" scoping...or is it even a declaration at all? Although it is bulky it might be better to write "const var"

No, const var is an oxymoron.

and "const let". A variable being constant or not is orthogonal to its scoping and should be controlled independently.

The preferred approach is to make let and const have the same binding
scope, namely block, and leave var alone.

let name = lambda(formals) ...;

I mentioned this a while back. I think it might be a good idea.
Scheme has

(define foo (lambda (a b) 1)) (define (foo a b) 1)

which could be translated to ES

var foo = lambda(a, b) 1; var foo(a, b) 1;

I still think this is bad form. A compound that does not create a
variable that must denote the defined function some time later (via
eval, arguments aliasing, hidden assignment if this is global code,
etc.) misstates what is usefully meant by the proposed syntax.

JS is not Scheme, and while you could argue assignment is like set!
the binding forms (including var extensions, but especially function,
const, and let) should have more definite and (under a strict mode or
future version) immutable meaning.

# David-Sarah Hopwood (15 years ago)

Peter Michaux wrote:

On Sat, Dec 6, 2008 at 7:51 PM, David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk> wrote:

The keyword 'function' shouldn't be used for this because a lambda is not a function. However,

const name(formals) ... let name(formals) ...

could be sugar for

const name = lambda(formals) ...;

Does "const" have "var" or "let" scoping...or is it even a declaration at all?

When 'let' and 'const' were proposed to be added to ES3.1, 'const' would have been a declaration with the same scoping as 'let'. This part of the ES3.1 proposal wasn't controversial, I think.

Although it is bulky it might be better to write "const var" and "const let". A variable being constant or not is orthogonal to its scoping and should be controlled independently.

That would be the case if the scoping of 'let' wasn't a strict improvement on, and intended replacement for, that of 'var'.

# David-Sarah Hopwood (15 years ago)

David-Sarah Hopwood wrote:

Jon Zeppieri wrote:

On Sat, Dec 6, 2008 at 11:49 AM, Maciej Stachowiak <mjs at apple.com> wrote:

On Dec 5, 2008, at 11:12 PM, Jon Zeppieri wrote:

I don't get it. What issue is raised by return-to-label that isn't already raised by exceptions? [...] Also, what was the performance issue?

The (minor) performance issue is that if there is a lambda that returns from a given function, all calls within that function body must check for an escape, even if the lambda is never passed to them or otherwise accessible to them. Similarly for calls within the scope of a labelled statement or iteration that contains a lambda with a corresponding 'break' or 'continue'.

Please disregard this -- I had overlooked a way to make the performance of escape continuations identical to that of exceptions. No explicit per-call checks are needed; the jump to the break/continue/return target can be handled using the same mechanism as try/catch handlers, with the same possible optimizations. In the case where the control abstraction is built-in or its implementation is inlined, the performance can be the same as conventional break/continue/return.

# P T Withington (15 years ago)

On 2008-12-06, at 00:23EST, David-Sarah Hopwood wrote:

P T Withington wrote:

Would it work to move the parameter list inside the block (as in the Smalltalk way, but as a regular parameter list, not using ||'s)?

{(a, b) a + b}

AFAICT, {( is a syntax error for an expression in es3.

I think this is unambiguous, but I don't like it because it has no symbol or combination of symbols that is specific to a lambda. ( "{(" can occur as the start of a block.)

^{(a, b) a +b}

Perhaps? An expression cannot start with {(, a statement cannot
start with ^.

# Alex Russell (15 years ago)

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1

Indeed, it can look like an expression to begin a name assignment:

var thinger = {("foo"+"bar"): ... };

Has a syntax like this been shot down yet?:

var thinger = {{ foo, bar }: ... };

Since objects (much less literals) aren't used as keys in hashes
often, this strikes me as being somewhat less ambiguous. The short (no
args) version might then be:

var thinger = {: ... };

Arguments against?

# Yuh-Ruey Chen (15 years ago)

Alex Russell wrote:

Indeed, it can look like an expression to begin a name assignment:

var thinger = {("foo"+"bar"): ... };

Has a syntax like this been shot down yet?:

var thinger = {{ foo, bar }: ... };

Since objects (much less literals) aren't used as keys in hashes
often, this strikes me as being somewhat less ambiguous. The short (no
args) version might then be:

var thinger = {: ... };

Arguments against?

I think that would require LALR(k) for bottom-up parsers, which we're trying to avoid mandating.

# David-Sarah Hopwood (15 years ago)

Alex Russell wrote:

On Dec 5, 2008, at 9:23 PM, David-Sarah Hopwood wrote:

P T Withington wrote:

Would it work to move the parameter list inside the block (as in the Smalltalk way, but as a regular parameter list, not using ||'s)?

{(a, b) a + b}

AFAICT, {( is a syntax error for an expression in es3.

I think this is unambiguous, but I don't like it because it has no symbol or combination of symbols that is specific to a lambda. ( "{(" can occur as the start of a block.)

Indeed, it can look like an expression to begin a name assignment:

var thinger = {("foo"+"bar"): ... };

No, property names in object literals are required to be a single IdentifierName, StringLiteral or NumericLiteral.

Has a syntax like this been shot down yet?:

var thinger = {{ foo, bar }: ... };

Since objects (much less literals) aren't used as keys in hashes often, this strikes me as being somewhat less ambiguous. The short (no args) version might then be:

var thinger = {: ... };

Arguments against?

What is the advantage of this syntax over ^(a, b) {a+b}, for example?

Ditto for P T Withington's proposal of ^{(a, b) a+b}.

# Yuh-Ruey Chen (15 years ago)

Breton Slivka wrote:

On Sat, Dec 6, 2008 at 9:57 AM, Michael Day <mikeday at yeslogic.com> wrote:

(1) Expression lambdas: lambdas whose body is an expression.

var x = lambda(y, z) y + z

Solves the problem with completion leakage, solves the nested return/break/continue issue. However, quite limited in usage, and makes it difficult to use lambdas to replace functions as they can't contain loop statements. (Hello, recursion! :)

[snip]

  1. It would be really nice to have a callable value that was garaunteed not to have side effects. a lambda with an expression body might not be that. Nevertheless, this would enable a parallelized array "map" function that's safe to use. In the absence of "real" multithreading, this kind of parallelism would be a boon for applications like 3d games, or image processing.

This little comment got lost in the recent deluge of emails, but I too would really like some mechanism to avoid or see if a function causes side effects (and not just mutability).

# Jon Zeppieri (15 years ago)

On Mon, Dec 8, 2008 at 4:01 AM, Yuh-Ruey Chen <maian330 at gmail.com> wrote:

Breton Slivka wrote:

On Sat, Dec 6, 2008 at 9:57 AM, Michael Day <mikeday at yeslogic.com> wrote:

(1) Expression lambdas: lambdas whose body is an expression.

var x = lambda(y, z) y + z

Solves the problem with completion leakage, solves the nested return/break/continue issue. However, quite limited in usage, and makes it difficult to use lambdas to replace functions as they can't contain loop statements. (Hello, recursion! :)

[snip]

  1. It would be really nice to have a callable value that was garaunteed not to have side effects. a lambda with an expression body might not be that. Nevertheless, this would enable a parallelized array "map" function that's safe to use. In the absence of "real" multithreading, this kind of parallelism would be a boon for applications like 3d games, or image processing.

This little comment got lost in the recent deluge of emails, but I too would really like some mechanism to avoid or see if a function causes side effects (and not just mutability).

Without a static type system, there's very little you can do. For example, let's say you wanted a guarantee that a call to some function F doesn't mutate any variables. A simple analysis can determine (conservatively) that F's own code doesn't perform any assignments, but the problem is that you also have to prove the same property for the entire possible call tree rooted at F. So, if F is passed a function and (potentially) calls it, that function has to be proven pure, as well.

Sure, you could abandon the proof requirement and turn it into an assertion, so that if F is annotated as "mutation-free" and if, at runtime, it tries to perform an assignment, an exception is thrown. But this doesn't work so well for other computational effects. Throwing an exception itself is an effect, and it would be pointless to throw an exception when your code illegally attempts... to throw an exception.

Non-termination is an effect, and it's notoriously immune to runtime checks.

I/O effects are possibly the most interesting from a practical perspective, but they're outside the scope of the ES standard.

# Mark S. Miller (15 years ago)

On Mon, Dec 8, 2008 at 7:20 AM, Jon Zeppieri <jaz at bu.edu> wrote:

On Mon, Dec 8, 2008 at 4:01 AM, Yuh-Ruey Chen <maian330 at gmail.com> wrote:

Breton Slivka wrote:

  1. It would be really nice to have a callable value that was garaunteed not to have side effects.

This little comment got lost in the recent deluge of emails, but I too would really like some mechanism to avoid or see if a function causes side effects (and not just mutability).

Without a static type system, there's very little you can do. Sure, you could abandon the proof requirement and turn it into an assertion, so that if F is annotated as "mutation-free" and if, at runtime, it tries to perform an assignment, an exception is thrown. But this doesn't work so well for other computational effects. Throwing an exception itself is an effect, and it would be pointless to throw an exception when your code illegally attempts... to throw an exception.

Non-termination is an effect, and it's notoriously immune to runtime checks.

I/O effects are possibly the most interesting from a practical perspective, but they're outside the scope of the ES standard.

E's auditors www.erights.org/elang/kernel/auditors <

wiki.erights.org/wiki/Guard-based_auditing> are a mostly non-static

approach that can verify many properties, including side effects per se including IO effects, but not including throws and non-termination. Joe-E's similar, but non-extensible and static auditor system < www.cs.berkeley.edu/~daw/papers/pure-ccs08.pdf>, has been used to

verify some nice purity properties.

I am not ready to propose any such thing for Harmony at this time.

# Brendan Eich (15 years ago)

The auditors idea is good; we've talked about it in the context of
Harmony.

Of course, ES4 had optional types, but over time ES4 lost the idea of
a normative optional static checker. It's not clear on the web when
you check. Cormac Flanagan proposed checking "when it seemed like a
good time" -- when script and page loads stabilized into some web app
cohort of sources that seemed not to be loading more code at the moment.

Optional offline static analyses can be powerful tools. They don't
even need soundness to be useful -- Mozilla's experience in C++ bears
this out (blog.mozilla.com/tglek). The same trade-offs may
apply to a Harmonious optional annotation system, if it's used well in
real code.

# P T Withington (15 years ago)

On 2008-12-08, at 00:59EST, David-Sarah Hopwood wrote:

What is the advantage of this syntax over ^(a, b) {a+b}, for example?

I prefer the above, if it were unambiguous.

Ditto for P T Withington's proposal of ^{(a, b) a+b}.

The above, I think, is. Because it is currently invalid.

# David-Sarah Hopwood (15 years ago)

Yuh-Ruey Chen wrote:

Breton Slivka wrote:

On Sat, Dec 6, 2008 at 9:57 AM, Michael Day <mikeday at yeslogic.com> wrote:

(1) Expression lambdas: lambdas whose body is an expression.

var x = lambda(y, z) y + z

Solves the problem with completion leakage, solves the nested return/break/continue issue. However, quite limited in usage, and makes it difficult to use lambdas to replace functions as they can't contain loop statements. (Hello, recursion! :) [snip]

  1. It would be really nice to have a callable value that was garaunteed not to have side effects. a lambda with an expression body might not be that. Nevertheless, this would enable a parallelized array "map" function that's safe to use. In the absence of "real" multithreading, this kind of parallelism would be a boon for applications like 3d games, or image processing.

This little comment got lost in the recent deluge of emails, but I too would really like some mechanism to avoid or see if a function causes side effects (and not just mutability).

That's unfeasibly difficult in full ECMAScript. Perhaps you could do it starting with one of the secure subsets (which have immutable globals and immutable prototypes of built-in types, for instance).

# Lex Spoon (15 years ago)

On Mon, Dec 1, 2008 at 3:19 PM, Allen Wirfs-Brock <Allen.Wirfs-Brock at microsoft.com> wrote:

Just to clarify some speculation, the syntax I proposed ({||}) was solely inspired by Smalltalk and tempered by the parsing realities of a C-like syntax. Any similarities to Ruby constructs are probably examples of parallel evolution under similar environmental pressures. I suspect that designers of other languages with C-like syntax (C# comes to mind with its () => expr "lambda" syntax) did not have the experience or goal of using closures to create control abstractions (which often requires passing multi-statement closures) and so arrived at a more function-like concise closure syntax.

I can share some history for the => form. It's disconcerting that

everyone associates it with C#, because they are open about copying the syntax from Scala. Scala's designer, Martin Odersky, most definitely had in mind that people could use functions for control flow, and in fact he treats it as the primary way to do control flow in Scala. I believe Martin got this syntax most directly from ML's "fn" expressions. He noticed that you don't really need the keyword.

The development for ML->Scala->C# actually looks a lot like is

happening in ES discussions. Once a function literal syntax is available, people really want to use it, and the syntax is pressured to get shorter and even to get its keyword dropped in favor of symbols.

On this list, the => form has so far been dismissed due to parsing

concerns. If that's the only reason, let me try and allay that worry and put that horse back in the race. Scala also has a comma operator, but it still manages to parse the => syntax. They way it does it is

to initially parse an expression and then, if it sees a =>,

reinterpret what it has seen so far as a parameter list. It's an unusual parsing strategy, but it works well and the issue is localized.

IMHO, x => x+1 really looks like a function literal, so that's the

color I'd paint the bike shed. I agree with Allen and others, though, that any version that drops the keyword will make the form more useful in practice.

# Yuh-Ruey Chen (15 years ago)

Lex Spoon wrote:

On this list, the => form has so far been dismissed due to parsing concerns. If that's the only reason, let me try and allay that worry and put that horse back in the race. Scala also has a comma operator, but it still manages to parse the => syntax. They way it does it is to initially parse an expression and then, if it sees a =>, reinterpret what it has seen so far as a parameter list. It's an unusual parsing strategy, but it works well and the issue is localized.

I don't think anyone is suggesting that it would be too difficult to parse for bottom-up parsers. It's just that it makes it difficult for a certain common class of bottom-up parsers, namely the LALR(k) for fixed k parser generators, to parse.

Personally, since I'm not responsible for any ES/JS implementation, I don't care about this difficulty, but a quick search for "ecmascript lalr" reveals that there are such existing parsers.

P.S. Take everything I say with a grain of salt, since I'm not really an expert on parsing.

# Lex Spoon (15 years ago)

On Wed, Dec 17, 2008 at 9:53 PM, Yuh-Ruey Chen <maian330 at gmail.com> wrote:

Lex Spoon wrote:

On this list, the => form has so far been dismissed due to parsing concerns. If that's the only reason, let me try and allay that worry and put that horse back in the race. Scala also has a comma operator, but it still manages to parse the => syntax. They way it does it is to initially parse an expression and then, if it sees a =>, reinterpret what it has seen so far as a parameter list. It's an unusual parsing strategy, but it works well and the issue is localized.

I don't think anyone is suggesting that it would be too difficult to parse for bottom-up parsers. It's just that it makes it difficult for a certain common class of bottom-up parsers, namely the LALR(k) for fixed k parser generators, to parse.

Good point. Actually, though, the same sort of approach should still work. The grammar for a => entry would be something like:

atomic_expression "=>" atomic_expression

Then, the rule that assembles this parse tree into a real AST would analyze the expression on the left and either convert it to a parse tree, or emit a retroactive parse error.

I know this initially violates some design sense--it does mine!--because normally a parser rule reuses subexpressions without change. However, in this case it works out well, and so I think the rule of thumb is misleading. This implementation technique should be simple, localized, easy to understand, and as a result robust.

Lex

# Brendan Eich (15 years ago)

On Dec 17, 2008, at 1:42 PM, Lex Spoon wrote:

I can share some history for the => form. It's disconcerting that everyone associates it with C#, because they are open about copying the syntax from Scala.

It's for Ecma solidarity -- we are indirectly boosting another Ecma
standard (C# is ECMA-334, IIRC), albeit at the expense of Scala :-P.

Ok, enough of that (we really do not try to align Ecma language
standards, which include Eiffel!). Thanks for the precedent correction.

On this list, the => form has so far been dismissed due to parsing concerns. If that's the only reason, let me try and allay that worry and put that horse back in the race. Scala also has a comma operator, but it still manages to parse the => syntax. They way it does it is to initially parse an expression and then, if it sees a =>, reinterpret what it has seen so far as a parameter list. It's an unusual parsing strategy, but it works well and the issue is localized.

I called the => syntax "no-go" for JS in

esdiscuss/2008-December/008352

cites two general arguments.

First, parsing top-down and then revising the AST based on right
context is do-able -- but the "This can get ugly" remark is meant to
suggest that it's costly compared to choosing a grammar that avoids
the ambiguity.

On the plus side, destructuring in JS1.7, proposed for Harmony to
broad agreement, requires similar revision:

[p, q] = [q, p];

swaps p and q, but it starts like

[p, q];

(and of course could be nested anywhere an assignment expression could
occur).

Let's say we can overcome this objection, by selling the benefit to
the users over the cost to implementors (and users, in minor code
footprint; slippery slope hazard here, otherwise it's not a big cost).
I'm not confident this assumption will hold in committee -- need to
get Waldemar's reaction, at least -- but for now let's just say :-).

The second argument is that the issue may not be localized, especially
in light of automatic semicolon insertion. A counter-example adapted
from the "The trouble with ambiguous grammars" thread:

a => a ? f : x++ (0);

A function expression in JS today is a primary expression, e.g.

var f = function (a) { return a ? f : x++; } (0);

so lambda users might expect the same precedence.

If the grammar is something like this:

AssignmentExpression: ... | identifier '=>' AssignmentExpression | '(' parameters ')' '=>' AssignmentExpression

(which AFAICT from web C# 3.0 grammars is what C# does) then we may be
ok. We'd need to check carefully.

Mono C# seems to have to bend over backwards to parse C# lambdas:

tirania.org/blog/archive/2007/Feb-15.html

but I don't see why a bottom-up parser can't decide quite late,
compared to a top-down parser, that it has a lambda parameter list and
not a parenthesized expression.

Perhaps we dismissed the => syntax too quickly, but we need a checked

bottom-up grammar. It's not enough to assert locality, unfortunately,
given the existing grammar and the complexity of automatic semicolon
insertion.

# Dave Herman (15 years ago)

"^" also has a slight resemblance to the greek lambda, which is the
reason Haskell uses "".

As an aside, the circumflex is actually the precursor to lambda:

"We end this introduction by telling what seems to be the story how
the letter 'λ' was chosen to denote function abstraction. In Principia
Mathematica the notation for the function f with f(x) = 2x + 1 is

  ^
 2x + 1.

Church originally intended to use the notation

 ^
 x.2x+1.

The typesetter could not position the hat on top of the x and placed
it in front of it, resulting in ^x.2x + 1. Then another typesetter changed it into λx.2x + 1."

-- H. Barendregt, The Impact of the Lambda Calculus In Logic and
Computer Science [1]

Dave

[1] citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.26.7908