local

Jon Zeppieri jaz at bu.edu
Thu Aug 21 19:28:41 PDT 2008


On Thu, Aug 21, 2008 at 7:19 PM, Ingvar von Schoultz
<ingvar-v-s at comhem.se> wrote:
> Jon Zeppieri wrote:
>>
>>   let x = 2, y = x * 2;
>>
>> ... the let-bound x would have to be visible to y's initializer.

(Actually, I left out the '*' part of 'letrec*' here, so:  the
let-bound variables would also have to be initialized left to right,
just as with a var declaration.  I also didn't mention hoisting, which
is relevant below.)

[snip]

>>
>> ... the 'fn' on the right hand side would refer to the let-bound value
>> (and not to something in the enclosing scope).
>
> Yes indeed. If instead left and right side are in different scopes,
> it seems the only "useful" thing that this adds is the ability to
> give the same name different meanings in adjacent code. But that's
> error-prone. Suppose you're reading someone else's code:
>
>    var a = 0; // Say you have noticed this declaration.
>    // Several lines of code.
>    for (let a = a; a < 10; a++)
>    {   // Several lines of code.
>        let x = a; // Have you noticed the change in meaning?
>    }
>    let y = a; // Are you aware that a is unaffected by the loop?

I don't find this confusing, at all.  I think it could become
confusing if there were redundant declarations of the same variable,
as allowed by var.


>
> Having left and right in the same scope is simpler, more intuitive
> and more useful, in my view.

For a moment I thought you might be right about the "more useful"
part.  It would be a problem if the let form didn't allow the binding
of recursive functions (assuming that we do not want separate 'let'
and 'letrec' forms in the language). But I think function expressions
can supply their own names, right?  So, even a non-recursive let form
would allow:

let (fn = function fact(n) { ... fact(n - 1); }) {
  ...
}

(Also the spec-formerly-known-as-ES4 had a 'this function' expression
that could be used to achieve the same effect.)


Non-recursive bindings are sometimes useful (particularly for simple
code generation).
Left-to-right evaluation of the bindings is often useful...

>
>> So, here is a suggestion that, I am sure, no one but me will like:
>> drop let declarations and *only* include let statements and
>> expressions.  Give them let semantics.  Maybe this is only true of me,
>> but if let statements were in the language, I would never use let
>> declarations, anyway.  The advantage of the statement and expression
>> forms is that they make the scope of the let-bound variables explicit.

After thinking about this a bit more, I don't know what single binding
semantics to prefer:

let: closest to the immediate function expression + application
pattern in common use
let*: like let, but with left-to-right evaluation
letrec*: closest to current 'var' behavior


>
> For very small and simple projects it's great that JavaScript lets
> you sprinkle your declarations all over the code, with redundant
> repetitions if you like. Block scope should be available also with
> this usage pattern, in my view.

I *do* know, however, that I don't like this.

I think you, Ingvar, are the lone champion of redundant variable declarations.

I'm not sure who the champions of var hoisting are.  (In another
thread, Brendan referred to hoisting as a wart.)

So, the only 'let' form that the working group has, in principle,
agreed to add to the language is the one that allows redundant
declarations and hoists?  Consistency is not an absolute virtue.

[snip]

>
> Let statements might also enable throwing an error on undeclared
> names.

I can't imagine what you have in mind here.

>
> While we're speaking of radical proposals, here's another one: If
> let is used anywhere in a block (any type of block), then forbid
> var in that block and all its nested blocks. And if var is used,
> forbid let. Allow both only in the global scope.

This complicates, rather than simplifies, the language.
Compositionality is good.

-Jon


More information about the Es-discuss mailing list