Closures and let blocks

# Igor Bukanov (18 years ago)

For functions defined with ES4 let block, does the scope of the closure includes the let block?

I.e will f in the following examples refer to the function itself or to the the name from the outer scope?

let (f = function() { ... f() ... }) { ... }

let (function f(n) { ... f() ... }) { ... }

I assume that f should refer to f from the outer scope to allow a usage like:

let (function f() { print("f is called"); f(); }) { ... }

, Igor

# Brendan Eich (18 years ago)

On Jan 3, 2008, at 8:29 AM, Igor Bukanov wrote:

For functions defined with ES4 let block, does the scope of the closure includes the let block?

The let block head scopes initialisers in the head to the outer
scope, but that has nothing to do with named function expression
scoping (not the same as function definition -- you wrote "functions
defined").

I.e will f in the following examples refer to the function itself or to the the name from the outer scope?

let (f = function() { ... f() ... }) { ... }

The call to f within itself always refers to itself. This is defined
by ES3, although ES4 changes the binding mechanism to avoid scope
object escape and Object hijacking attacks. See the compatibility
guide at www.ecmascript.org/es4/spec/incompatibilities.pdf
section 1.6.

I assume that f should refer to f from the outer scope to allow a
usage like:

let (function f() { print("f is called"); f(); }) {

This is not valid syntax. The let head should contain optionally
annotated, optionally initialised, unqualified variable names, comma- separated. A function expression as initialiser is just an
expression. If it has a name, it's a named function expression per
ES3 chapter 13, third semantic definition.

# Igor Bukanov (18 years ago)

On 03/01/2008, Brendan Eich <brendan at mozilla.org> wrote:

let (f = function() { ... f() ... }) { ... }

The call to f within itself always refers to itself.

I do not see how ES3 is relevant to this second case. So I would like to clarify if in

function f() { }

let (f = function() { ... f() ... }) { ... }

f() should refer to the outer f or let-bound f.

let (function f() { print("f is called"); f(); }) {

This is not valid syntax. The let head should contain optionally annotated, optionally initialised, unqualified variable names, comma- separated.

I thought that function definitions in the let blocks like in let (function f()) { } is supported for uniformity with let declarations that allow a usage like:

let function f() { };

, Igor

# Brendan Eich (18 years ago)

On Jan 3, 2008, at 9:36 AM, Igor Bukanov wrote:

On 03/01/2008, Brendan Eich <brendan at mozilla.org> wrote:

let (f = function() { ... f() ... }) { ... }

The call to f within itself always refers to itself.

I do not see how ES3 is relevant to this second case.

Oops, sorry -- pre-caffeine here. I misread that lambda as a named
function expression. But it's anonymous, and its scope chain (as
noted) starts with the let's outer scope, so it won't find itself
bound to the name f.

So I would like to clarify if in

function f() { }

let (f = function() { ... f() ... }) { ... }

f() should refer to the outer f or let-bound f.

Outer, no question.

I thought that function definitions in the let blocks like in let (function f()) { } is supported for uniformity with let declarations that allow a usage like:

let function f() { };

I missed that if so -- did you see this in the wiki, a trac ticket,
or another doc?

# Lars Hansen (18 years ago)

-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Brendan Eich Sent: 3. januar 2008 18:45 To: Igor Bukanov Cc: es4-discuss at mozilla.org Subject: Re: Closures and let blocks

On Jan 3, 2008, at 9:36 AM, Igor Bukanov wrote:

On 03/01/2008, Brendan Eich <brendan at mozilla.org> wrote:

let (f = function() { ... f() ... }) { ... }

The call to f within itself always refers to itself.

I do not see how ES3 is relevant to this second case.

Oops, sorry -- pre-caffeine here. I misread that lambda as a named function expression. But it's anonymous, and its scope chain (as noted) starts with the let's outer scope, so it won't find itself bound to the name f.

So I would like to clarify if in

function f() { }

let (f = function() { ... f() ... }) { ... }

f() should refer to the outer f or let-bound f.

Outer, no question.

I thought that function definitions in the let blocks like in let (function f()) { } is supported for uniformity with let declarations that allow a usage like:

let function f() { };

I missed that if so -- did you see this in the wiki, a trac ticket, or another doc?

Ditto -- news to me if "let (function f() ...) ..." has a meaning. The analogy of "let function" is to "let const" and plain let-is-the-new-var "let". I don't recall having a "let (const x...) ..." form either.

# Igor Bukanov (18 years ago)

On 03/01/2008, Brendan Eich <brendan at mozilla.org> wrote:

let function f() { };

I missed that if so -- did you see this in the wiki, a trac ticket, or another doc?

I have not seen this, I just assumed for some reasons that syntax for let blocks and declarations is shared.

, Igor

# Brendan Eich (18 years ago)

On Jan 3, 2008, at 9:58 AM, Igor Bukanov wrote:

On 03/01/2008, Brendan Eich <brendan at mozilla.org> wrote:

let function f() { };

I missed that if so -- did you see this in the wiki, a trac ticket, or another doc?

I have not seen this, I just assumed for some reasons that syntax for let blocks and declarations is shared.

Completely reasonable, both for implementations and users, IMHO.
Lars, what do you think?

# Lars T Hansen (18 years ago)

On Jan 3, 2008 7:01 PM, Brendan Eich <brendan at mozilla.org> wrote:

On Jan 3, 2008, at 9:58 AM, Igor Bukanov wrote:

On 03/01/2008, Brendan Eich <brendan at mozilla.org> wrote:

let function f() { };

I missed that if so -- did you see this in the wiki, a trac ticket, or another doc?

I have not seen this, I just assumed for some reasons that syntax for let blocks and declarations is shared.

Completely reasonable, both for implementations and users, IMHO. Lars, what do you think?

I doubt this simplifies the life for implementations even the tiniest bit, so let's talk use cases...

I'm fairly sure that in 20 years of Scheme programming I've not felt the need to use letrec as a nested expression (except in a context where we'd have a block in ES4, eg under the control of "if"). I also think the main use case for the expression form of "let" is in very simple expression functions where there won't be a credible need for nested functions. Ergo I think "let (function f() ...) ..." has doubtful utility. Same argument for "let (const ...) ..." really; the use cases for "let (...) ..." are unlikely to call for constant bindings. So my vote is "no".

# Brendan Eich (18 years ago)

On Jan 3, 2008, at 10:29 AM, Lars T Hansen wrote:

On Jan 3, 2008 7:01 PM, Brendan Eich <brendan at mozilla.org> wrote:

On Jan 3, 2008, at 9:58 AM, Igor Bukanov wrote:

On 03/01/2008, Brendan Eich <brendan at mozilla.org> wrote:

let function f() { };

I missed that if so -- did you see this in the wiki, a trac ticket, or another doc?

I have not seen this, I just assumed for some reasons that syntax
for let blocks and declarations is shared.

Completely reasonable, both for implementations and users, IMHO. Lars, what do you think?

I doubt this simplifies the life for implementations even the tiniest bit,

Didn't say that it would -- reasonable != simplify. Practical and
reference implementations have lots of common sub-parsers that can be
hooked together cheaply, was my point.

so let's talk use cases...

I'm fairly sure that in 20 years of Scheme programming I've not felt the need to use letrec as a nested expression

The context here is let blocks, not let expressions.

# Brendan Eich (18 years ago)

On Jan 3, 2008, at 11:26 AM, Brendan Eich wrote:

The context here is let blocks, not let expressions.

Agree (let (function f()...) expr-using-f) is less common, but it's
not beyond the pale. Neither (in the face of ++ and other mutating
ops) is (let (const C...) expr-using-C). Igor was applying rude
deduction of the Scheme sort: compositional primitiives, not gotchas
or asymmetric restrictions. I'm not sure, given what I maintain is
low cost to implement, of what good we do for users by breaking
symmetry here.

# Igor Bukanov (18 years ago)

On 03/01/2008, Brendan Eich <brendan at mozilla.org> wrote:

Igor was applying rude

deduction of the Scheme sort: compositional primitiives, not gotchas or asymmetric restrictions.

In fact I was wrong about the symmetry here.

Consider: { let function f() { ... f()... }; ... } versus hypothetical let (function f() { ... f()... }) { ... }

In the first case f() refers to the let-bounded f and this is clear.

In the second case if f() would also refer to the let-bounded f(), it would require an extra special scoping rule. And if f() would refer to the outer f, then it breaks the symmetry with the declaring f.

So one way or another the symmetry is broken meaning an extra complexity for the implementations and extra rules for a user to learn.

, Igor

# Brendan Eich (18 years ago)

On Jan 3, 2008, at 11:41 AM, Igor Bukanov wrote:

So one way or another the symmetry is broken meaning an extra complexity for the implementations and extra rules for a user to learn.

This asymmetry exists with let declarations vs. blocks/expressions:

// outer x may be bound here

{ ... // but cannot be used here let x = f(x); // or here as the argument to f // x's default value is used instead ... }

vs.

let (x = f(x)) { // x in f(x) finds outer x binding ... // here x is in scope throughout the // block (or expression if let expr) }

This discussion brings the existing asymmetry to light, magnifies it
via function's body, with its own scope(s). I'm not saying we should
add (let (function f()...) ...) etc. just because of the existing,
intentional asymmetry. But more asymmetry of the same kind is not
obviously fatal either.

The case for (let (const C ...) ...) seems stronger in any event.

# Nathan de Vries (18 years ago)

On Thu, 2008-01-03 at 20:41 +0100, Igor Bukanov wrote:

let (function f() { ... f()... }) { ... }

I would write that as follows:

let (f = function() { arguments.callee(); } ) {
    f();
}

Are inline named functions a necessity for let expressions? My feeling is no, and that the name bears no meaning within the scope of the let expression.

Cheers,

-- Nathan de Vries