Proposal: if variable initialization

classic Classic list List threaded Threaded
45 messages Options
123
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: if variable initialization

Naveen Chawla
I don't get it. Please give an example of the per-iteration initialization in the while loop... (the for-loop version before `;` does it before iteration, not per-iteration)

On Mon, 26 Mar 2018 at 07:20 Isiah Meadows <[hidden email]> wrote:
As a counterpoint, Rust and Swift are the opposite: it's only defined
in the consequent branch, not the alternate. So it could go both ways.

But if a value is useful in both branches, I'd prefer to just define
the variable in a separate statement, to clarify that it's useful in
both branches (explicit > implicit). To take your example, I'd prefer
instead to do this:

```js
let foo = getFoo()

if (foo.isReady()) {
    foo.start()
} else {
    foo.wait()
}
```
-----

Isiah Meadows
[hidden email]

Looking for web consulting? Or a new website?
Send me an email and we can get started.
www.isiahmeadows.com


On Sun, Mar 25, 2018 at 5:42 AM, Maël Nison <[hidden email]> wrote:
> C++ defines it as being available in both branches, I think we try to share
> the same behavior. Keep in mind that it will useful when the check is on
> something different than existence :
>
> if (let foo = getFoo() ; foo.isReady())
>     foo.start();
> else
>     foo.wait();
>
> On Wed, Mar 21, 2018, 10:27 PM Isiah Meadows <[hidden email]> wrote:
>>
>> My implication was that it'd only be available in the `if` (if
>> declared with `let`/`const`).
>> -----
>>
>> Isiah Meadows
>> [hidden email]
>>
>> Looking for web consulting? Or a new website?
>> Send me an email and we can get started.
>> www.isiahmeadows.com
>>
>>
>> On Wed, Mar 21, 2018 at 6:25 PM, Sebastian Malton <[hidden email]>
>> wrote:
>> > Sorry if I missed a message but would such an initialization be only
>> > available in the first `if` block or also in the subsequent `else if` and
>> > `else` blocks?
>> >
>> > Sebastian Malton
>> >
>> >
>> >   Original Message
>> > From: [hidden email]
>> > Sent: March 21, 2018 6:18 PM
>> > To: [hidden email]
>> > Cc: [hidden email]; [hidden email]
>> > Subject: Re: Proposal: if variable initialization
>> >
>> > I'm personally very much *for* this `if (var ...; cond) { ... }`
>> > syntax. I couldn't tell you how many times I would've liked something
>> > to that effect, since that's probably one of my biggest areas of
>> > boilerplate.
>> >
>> > I would also be in favor of `if (var ...) { ... }` as a shorthand that
>> > guards `!= null` the expression result (pre-match), since that's about
>> > 90% of my use cases for it. There *is* a potential area of ambiguity
>> > in sloppy for `if ( let [ x ] = y )`, since that would be currently
>> > parsed as `var tmp = y; let[x] = tmp; if (tmp) { ... }`, but I doubt
>> > breaking that would be of much web compat risk. (A similar ambiguity
>> > existed with `for (let [`, but that break didn't cause many issues.)
>> > -----
>> >
>> > Isiah Meadows
>> > [hidden email]
>> >
>> > Looking for web consulting? Or a new website?
>> > Send me an email and we can get started.
>> > www.isiahmeadows.com
>> >
>> >
>> > On Wed, Mar 21, 2018 at 2:47 PM, Mike Samuel <[hidden email]>
>> > wrote:
>> >>
>> >>
>> >> On Wed, Mar 21, 2018 at 1:27 PM, Sebastian Malton
>> >> <[hidden email]>
>> >> wrote:
>> >>>
>> >>> Because block-level scoping is a very good way to avoid certain bugs
>> >>> and
>> >>> is easier to reason about. Especially when considering project
>> >>> successors.
>> >>
>> >>
>> >> +1.  function-scoped variables in loop bodies caused tons of bugs
>> >> before
>> >> let-scoped variables and were a main motivating case.
>> >>
>> >> var i;
>> >> for (i = 0; i < arr.length; ++i) {
>> >>   f(function () { /* Do something with */ arr[i]; });
>> >> }
>> >>
>> >> vs
>> >>
>> >> for (let i = 0; i < arr.length; ++i) {
>> >>   f(function () { /* Do something with */ arr[i]; });
>> >> }
>> >>
>> >> Yes, linters got pretty good at finding uses of closed-over variables
>> >> modified in a loop, but the workarounds were not ideal.
>> >>
>> >> var i;
>> >> for (i = 0; i < arr.length; ++i) {
>> >>   f(function (i) { return function () { /* Do something with */ arr[i];
>> >> }
>> >> }(i));
>> >> }
>> >>
>> >> Block scoping is just better for code that uses loops, variables, and
>> >> function expressions.
>> >>
>> >>
>> >> _______________________________________________
>> >> es-discuss mailing list
>> >> [hidden email]
>> >> https://mail.mozilla.org/listinfo/es-discuss
>> >>
>> _______________________________________________
>> es-discuss mailing list
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: if variable initialization

Isiah Meadows-2
Maybe this analogue to the `for (const ... of ...)`:

```js
function keyList(map) {
    const list = new Array(map.size)
    const iter = map.keys()

    while (const {done, value} = iter.next(); !done) {
        list.push(value)
    }

    return list
}
```

But in general, the more I've thought about it, the more I've noticed
it doesn't generalize well past the C-style `for` loop and I find
myself getting ready to reinvent an awkward minor variant of it
repeatedly. And without anything to the tune of pattern matching
(which Rust has) or a `loop`/`recur`-like `while`-ish loop (which is
what Clojure uses instead), it just seems pointless.

-----

Isiah Meadows
[hidden email]

Looking for web consulting? Or a new website?
Send me an email and we can get started.
www.isiahmeadows.com


On Mon, Mar 26, 2018 at 2:33 AM, Naveen Chawla <[hidden email]> wrote:

> I don't get it. Please give an example of the per-iteration initialization
> in the while loop... (the for-loop version before `;` does it before
> iteration, not per-iteration)
>
> On Mon, 26 Mar 2018 at 07:20 Isiah Meadows <[hidden email]> wrote:
>>
>> As a counterpoint, Rust and Swift are the opposite: it's only defined
>> in the consequent branch, not the alternate. So it could go both ways.
>>
>> But if a value is useful in both branches, I'd prefer to just define
>> the variable in a separate statement, to clarify that it's useful in
>> both branches (explicit > implicit). To take your example, I'd prefer
>> instead to do this:
>>
>> ```js
>> let foo = getFoo()
>>
>> if (foo.isReady()) {
>>     foo.start()
>> } else {
>>     foo.wait()
>> }
>> ```
>> -----
>>
>> Isiah Meadows
>> [hidden email]
>>
>> Looking for web consulting? Or a new website?
>> Send me an email and we can get started.
>> www.isiahmeadows.com
>>
>>
>> On Sun, Mar 25, 2018 at 5:42 AM, Maël Nison <[hidden email]> wrote:
>> > C++ defines it as being available in both branches, I think we try to
>> > share
>> > the same behavior. Keep in mind that it will useful when the check is on
>> > something different than existence :
>> >
>> > if (let foo = getFoo() ; foo.isReady())
>> >     foo.start();
>> > else
>> >     foo.wait();
>> >
>> > On Wed, Mar 21, 2018, 10:27 PM Isiah Meadows <[hidden email]>
>> > wrote:
>> >>
>> >> My implication was that it'd only be available in the `if` (if
>> >> declared with `let`/`const`).
>> >> -----
>> >>
>> >> Isiah Meadows
>> >> [hidden email]
>> >>
>> >> Looking for web consulting? Or a new website?
>> >> Send me an email and we can get started.
>> >> www.isiahmeadows.com
>> >>
>> >>
>> >> On Wed, Mar 21, 2018 at 6:25 PM, Sebastian Malton
>> >> <[hidden email]>
>> >> wrote:
>> >> > Sorry if I missed a message but would such an initialization be only
>> >> > available in the first `if` block or also in the subsequent `else if`
>> >> > and
>> >> > `else` blocks?
>> >> >
>> >> > Sebastian Malton
>> >> >
>> >> >
>> >> >   Original Message
>> >> > From: [hidden email]
>> >> > Sent: March 21, 2018 6:18 PM
>> >> > To: [hidden email]
>> >> > Cc: [hidden email]; [hidden email]
>> >> > Subject: Re: Proposal: if variable initialization
>> >> >
>> >> > I'm personally very much *for* this `if (var ...; cond) { ... }`
>> >> > syntax. I couldn't tell you how many times I would've liked something
>> >> > to that effect, since that's probably one of my biggest areas of
>> >> > boilerplate.
>> >> >
>> >> > I would also be in favor of `if (var ...) { ... }` as a shorthand
>> >> > that
>> >> > guards `!= null` the expression result (pre-match), since that's
>> >> > about
>> >> > 90% of my use cases for it. There *is* a potential area of ambiguity
>> >> > in sloppy for `if ( let [ x ] = y )`, since that would be currently
>> >> > parsed as `var tmp = y; let[x] = tmp; if (tmp) { ... }`, but I doubt
>> >> > breaking that would be of much web compat risk. (A similar ambiguity
>> >> > existed with `for (let [`, but that break didn't cause many issues.)
>> >> > -----
>> >> >
>> >> > Isiah Meadows
>> >> > [hidden email]
>> >> >
>> >> > Looking for web consulting? Or a new website?
>> >> > Send me an email and we can get started.
>> >> > www.isiahmeadows.com
>> >> >
>> >> >
>> >> > On Wed, Mar 21, 2018 at 2:47 PM, Mike Samuel <[hidden email]>
>> >> > wrote:
>> >> >>
>> >> >>
>> >> >> On Wed, Mar 21, 2018 at 1:27 PM, Sebastian Malton
>> >> >> <[hidden email]>
>> >> >> wrote:
>> >> >>>
>> >> >>> Because block-level scoping is a very good way to avoid certain
>> >> >>> bugs
>> >> >>> and
>> >> >>> is easier to reason about. Especially when considering project
>> >> >>> successors.
>> >> >>
>> >> >>
>> >> >> +1.  function-scoped variables in loop bodies caused tons of bugs
>> >> >> before
>> >> >> let-scoped variables and were a main motivating case.
>> >> >>
>> >> >> var i;
>> >> >> for (i = 0; i < arr.length; ++i) {
>> >> >>   f(function () { /* Do something with */ arr[i]; });
>> >> >> }
>> >> >>
>> >> >> vs
>> >> >>
>> >> >> for (let i = 0; i < arr.length; ++i) {
>> >> >>   f(function () { /* Do something with */ arr[i]; });
>> >> >> }
>> >> >>
>> >> >> Yes, linters got pretty good at finding uses of closed-over
>> >> >> variables
>> >> >> modified in a loop, but the workarounds were not ideal.
>> >> >>
>> >> >> var i;
>> >> >> for (i = 0; i < arr.length; ++i) {
>> >> >>   f(function (i) { return function () { /* Do something with */
>> >> >> arr[i];
>> >> >> }
>> >> >> }(i));
>> >> >> }
>> >> >>
>> >> >> Block scoping is just better for code that uses loops, variables,
>> >> >> and
>> >> >> function expressions.
>> >> >>
>> >> >>
>> >> >> _______________________________________________
>> >> >> es-discuss mailing list
>> >> >> [hidden email]
>> >> >> https://mail.mozilla.org/listinfo/es-discuss
>> >> >>
>> >> _______________________________________________
>> >> es-discuss mailing list
>> >> [hidden email]
>> >> https://mail.mozilla.org/listinfo/es-discuss
>> _______________________________________________
>> es-discuss mailing list
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: if variable initialization

Naveen Chawla
OK, but your example wouldn't be acceptable in JavaScript, because it's inconsistent with how `for(;;)` does initialization before the first `;`, which is before iteration.

That's why I was saying that initializers-as-expressions simplifies doing things like that, despite the other concerns.

On Mon, 26 Mar 2018 at 12:18 Isiah Meadows <[hidden email]> wrote:
Maybe this analogue to the `for (const ... of ...)`:

```js
function keyList(map) {
    const list = new Array(map.size)
    const iter = map.keys()

    while (const {done, value} = iter.next(); !done) {
        list.push(value)
    }

    return list
}
```

But in general, the more I've thought about it, the more I've noticed
it doesn't generalize well past the C-style `for` loop and I find
myself getting ready to reinvent an awkward minor variant of it
repeatedly. And without anything to the tune of pattern matching
(which Rust has) or a `loop`/`recur`-like `while`-ish loop (which is
what Clojure uses instead), it just seems pointless.

-----

Isiah Meadows
[hidden email]

Looking for web consulting? Or a new website?
Send me an email and we can get started.
www.isiahmeadows.com


On Mon, Mar 26, 2018 at 2:33 AM, Naveen Chawla <[hidden email]> wrote:
> I don't get it. Please give an example of the per-iteration initialization
> in the while loop... (the for-loop version before `;` does it before
> iteration, not per-iteration)
>
> On Mon, 26 Mar 2018 at 07:20 Isiah Meadows <[hidden email]> wrote:
>>
>> As a counterpoint, Rust and Swift are the opposite: it's only defined
>> in the consequent branch, not the alternate. So it could go both ways.
>>
>> But if a value is useful in both branches, I'd prefer to just define
>> the variable in a separate statement, to clarify that it's useful in
>> both branches (explicit > implicit). To take your example, I'd prefer
>> instead to do this:
>>
>> ```js
>> let foo = getFoo()
>>
>> if (foo.isReady()) {
>>     foo.start()
>> } else {
>>     foo.wait()
>> }
>> ```
>> -----
>>
>> Isiah Meadows
>> [hidden email]
>>
>> Looking for web consulting? Or a new website?
>> Send me an email and we can get started.
>> www.isiahmeadows.com
>>
>>
>> On Sun, Mar 25, 2018 at 5:42 AM, Maël Nison <[hidden email]> wrote:
>> > C++ defines it as being available in both branches, I think we try to
>> > share
>> > the same behavior. Keep in mind that it will useful when the check is on
>> > something different than existence :
>> >
>> > if (let foo = getFoo() ; foo.isReady())
>> >     foo.start();
>> > else
>> >     foo.wait();
>> >
>> > On Wed, Mar 21, 2018, 10:27 PM Isiah Meadows <[hidden email]>
>> > wrote:
>> >>
>> >> My implication was that it'd only be available in the `if` (if
>> >> declared with `let`/`const`).
>> >> -----
>> >>
>> >> Isiah Meadows
>> >> [hidden email]
>> >>
>> >> Looking for web consulting? Or a new website?
>> >> Send me an email and we can get started.
>> >> www.isiahmeadows.com
>> >>
>> >>
>> >> On Wed, Mar 21, 2018 at 6:25 PM, Sebastian Malton
>> >> <[hidden email]>
>> >> wrote:
>> >> > Sorry if I missed a message but would such an initialization be only
>> >> > available in the first `if` block or also in the subsequent `else if`
>> >> > and
>> >> > `else` blocks?
>> >> >
>> >> > Sebastian Malton
>> >> >
>> >> >
>> >> >   Original Message
>> >> > From: [hidden email]
>> >> > Sent: March 21, 2018 6:18 PM
>> >> > To: [hidden email]
>> >> > Cc: [hidden email]; [hidden email]
>> >> > Subject: Re: Proposal: if variable initialization
>> >> >
>> >> > I'm personally very much *for* this `if (var ...; cond) { ... }`
>> >> > syntax. I couldn't tell you how many times I would've liked something
>> >> > to that effect, since that's probably one of my biggest areas of
>> >> > boilerplate.
>> >> >
>> >> > I would also be in favor of `if (var ...) { ... }` as a shorthand
>> >> > that
>> >> > guards `!= null` the expression result (pre-match), since that's
>> >> > about
>> >> > 90% of my use cases for it. There *is* a potential area of ambiguity
>> >> > in sloppy for `if ( let [ x ] = y )`, since that would be currently
>> >> > parsed as `var tmp = y; let[x] = tmp; if (tmp) { ... }`, but I doubt
>> >> > breaking that would be of much web compat risk. (A similar ambiguity
>> >> > existed with `for (let [`, but that break didn't cause many issues.)
>> >> > -----
>> >> >
>> >> > Isiah Meadows
>> >> > [hidden email]
>> >> >
>> >> > Looking for web consulting? Or a new website?
>> >> > Send me an email and we can get started.
>> >> > www.isiahmeadows.com
>> >> >
>> >> >
>> >> > On Wed, Mar 21, 2018 at 2:47 PM, Mike Samuel <[hidden email]>
>> >> > wrote:
>> >> >>
>> >> >>
>> >> >> On Wed, Mar 21, 2018 at 1:27 PM, Sebastian Malton
>> >> >> <[hidden email]>
>> >> >> wrote:
>> >> >>>
>> >> >>> Because block-level scoping is a very good way to avoid certain
>> >> >>> bugs
>> >> >>> and
>> >> >>> is easier to reason about. Especially when considering project
>> >> >>> successors.
>> >> >>
>> >> >>
>> >> >> +1.  function-scoped variables in loop bodies caused tons of bugs
>> >> >> before
>> >> >> let-scoped variables and were a main motivating case.
>> >> >>
>> >> >> var i;
>> >> >> for (i = 0; i < arr.length; ++i) {
>> >> >>   f(function () { /* Do something with */ arr[i]; });
>> >> >> }
>> >> >>
>> >> >> vs
>> >> >>
>> >> >> for (let i = 0; i < arr.length; ++i) {
>> >> >>   f(function () { /* Do something with */ arr[i]; });
>> >> >> }
>> >> >>
>> >> >> Yes, linters got pretty good at finding uses of closed-over
>> >> >> variables
>> >> >> modified in a loop, but the workarounds were not ideal.
>> >> >>
>> >> >> var i;
>> >> >> for (i = 0; i < arr.length; ++i) {
>> >> >>   f(function (i) { return function () { /* Do something with */
>> >> >> arr[i];
>> >> >> }
>> >> >> }(i));
>> >> >> }
>> >> >>
>> >> >> Block scoping is just better for code that uses loops, variables,
>> >> >> and
>> >> >> function expressions.
>> >> >>
>> >> >>
>> >> >> _______________________________________________
>> >> >> es-discuss mailing list
>> >> >> [hidden email]
>> >> >> https://mail.mozilla.org/listinfo/es-discuss
>> >> >>
>> >> _______________________________________________
>> >> es-discuss mailing list
>> >> [hidden email]
>> >> https://mail.mozilla.org/listinfo/es-discuss
>> _______________________________________________
>> es-discuss mailing list
>> [hidden email]
>> https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: if variable initialization

Waldemar Horwat
In reply to this post by Naveen Chawla
On 03/22/2018 11:21 PM, Naveen Chawla wrote:
> I'm still not seeing a compelling case for not allowing `const` / `let` declarations to be evaluated as expressions. Or I've missed it.

Yes, I think there is a compelling case for not allowing `const` / `let` declarations to be evaluated as expressions.

> As was noted,
>
> `if(x = 5)` is already allowed.
>
> Is `if(const x = 5)` really that much of a stretch?

That's fine in this case, and I happily use this construct in C++.

But that's *very* different from allowing `const` / `let` declarations anyplace you allow an expression.

> To answer a concern about a function call like `myFunction(const x = 7)`, of course the scope of `x` would be where it is declared.

And here we come the problem: the scope.

> It can't be anywhere else (like inside myFunction or something).

We wouldn't want to repeat the var hoisting problems, so the scope of a general subexpression declaration would need to be the subexpression in which it's contained and not some outer context.  If you don't do that, you'll eventually run into the same problems as with var.

However, that's not what the current uses of such declarations do.  For example,

for (var i = expr; ...) {...}

scopes i to the body of the loop, and you get a new i binding for each iteration, which is important for lambda capture, even though expr is evaluated only once.  Subexpression scoping would be incompatible with that.

As such, we can reasonably allow `const` / `let` declarations in other specific contexts such as at the top level of if statement condition expressions, but not in subexpressions in general.

     Waldemar
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: Proposal: if variable initialization

Michael Theriot
In reply to this post by Rodrigo
Same sentiments, and I am pleased with how golang handles this common desire. Another idea I had is a `for` statement with only one expression of declarations, or even a new use for the dead `with` statement (conveniently named).

On Tue, Mar 20, 2018 at 3:57 PM, Rodrigo <[hidden email]> wrote:
Proposal: inline let/const statements to declare and initialize
variables within if statements, so that temporary variables exist only
within the if/else block scope.

Reason: limits variable scope to the block where really needed, in
similar fashion to variables defined in for(;;) statements. This
improves readability while reducing unnecessary variables roaming
outside their needed block.

The syntax would be very similar to the for(;;) assignment/test pair:

    if (let x = 100; x > 50) {
        console.log(x); // 100
    }
    console.log(x); // ReferenceError

    // same for const
    if( const x = foo(); typeof x === 'object' ) {
        //...
    }

    // the variable is available within any else block
    // after its declaration
    if (let x = foo(); x < 50) {
        console.log(x);  // y is not available here
    } else if (let y = bar(); y > 0) {
        console.log(x, y);
    } else {
        console.log(x, y);
    }

Right now there isn't a way to limit a variable to the if block:

    let x = 100;
    if (x > 50) {
        console.log(x);
    }
    // x is in scope, but may not be needed beyond the if statement
    console.log(x);

    // or a non-strict assignment, which also "leaks" scope
    if( (x = 100) > 50 ) {
        // ...
    }

There are many "workarounds" available, here's a few:

    // workaround 1: can be remedied with a scope block
    // but it's asymmetrical and non-idiomatic
    {
        let x = 100;
        if (x > 50) {
            console.log(x);
        }
    }

    // workaround 2: with a for statement
    // but this is non-idiomatic, hard to read and error-prone
    for (let x = 100; x > 50;) {
        console.log(x);
        break;
    }

If-initialization is available in many languages (Go, Perl and Ruby
come to mind) and are considered best practice in each one of them:

    // Golang - x is defined, assigned and conditionally tested
    if x := 100; x > 50 {
        // x is in scope here
    } else {
        // and in here
    }
    // x is not available here

    ###### Perl
    if( my $x = 100 ) {
        print $x;
    }
    print $x; # an error

    if ( ( my $x = myfoo() ) > 50 ) {  # also ok in Perl
        print $x;
    }

    ###### Ruby
    if ( x = 100 )  # parens required per style guide
        puts(x)
    end
    puts(x) # unfortunately Ruby does not limit scope to if, so x "leaks"

I think this would be a great and important addition to the language.

-Rodrigo

PS: Just for the sake of comparison, Perl-style if-assignments could also be an
option, albeit a very bad one IMO:

    if( ( let x = 100 ) > 50 ) {
    }

A Perl-style, value-returning let/const has readability issues, opens
quite a few fronts and sort of implies that let/const can return
values anywhere in the code outside if/else. On the other hand it
would fit with the currently if assignment if( x = y ). Definitely not
recommended.
_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
123