What Does This Mean? Variable Declared Followed by a Block Without Assignment

Why variable that declared in a using statement treated as readonly?

I'm looking at an (outdated?) spec [1] right now.

15.13 says that variables that you declare in the resource acquisition part are readonly. That is:

var form = new Form1();
using (form) {
form = null;
}

works, but

using (var form = new Form1()) {
form = null;
}

doesn't.
This answers part of the question (i.e. Why? Because it is part of the spec..), but I understand that this is not really satisfying. But why would you even want to do that?


Edit: After thinking about this, let me offer a possible explanation for this rule:

You have

using (var something = new Foo()) {
something = /* whatever */
}

and the compiler allows this. Now what if Foo needs a lot of unmanaged resources (maybe that's the reason you wanted to use using in the first place)? After the using block you have no way to access this reference anymore. It wasn't disposed, because you reassigned something and forgot to handle it yourself. You don't have a guarantee that the GC runs, at all. Or when. You just created a resource leak that is obscured and hidden.


A final one, inspired by Henk's link to Eric Lippert's blog, which again just ends up throwing the spec at us:

A using statement of the form

using (expression) statement

has the same two possible expansions, but in this case ResourceType is
implicitly the compile-time type of the expression, and the resource variable
is inaccessible in, and invisible to, the embedded statement.

In other words:

var form = new Form1();
using (form) {
form = null;
}

works, because this is expanded to

var form = new Form1();
var invisibleThing = form;
try {
form = null;
} finally {
if (invisibleThing != null) ((IDisposable)invisibleThing).Dispose();
}

So in this case the fact that you have no influence over the using reference is just hidden from you and is exactly like in the previous case.

1:http://www.ecma-international.org/publications/standards/Ecma-334.htm

Why does writing to a variable change its scope?

Python defaults to implicit variable declaration via assignment, in order to remove the need for additional explicit declarations. Just "implicit declaration" leaves several options what assignment in nested scopes means, most prominently:

  • Assignment always declares a variable in the inner-most scope.
  • Assignment always declares a variable in the outer-most scope.
  • Assignment declares a variable in the inner-most scope, unless declared in any outer scope.
  • Assignment declares a variable in the inner-most scope, readable only after assignment.

The latter two options mean that a variable does not have a scope well-defined just by the assignment itself. They are "declaration via assignment + X" which can lead to unintended interactions between unrelated code.

That leaves the decision of whether "writing to a variable" should preferably happen to isolated local or shared global variables.


The Python designers consider it more important to explicitly mark writing to a global variable.

Python FAQ: Why am I getting an UnboundLocalError when the variable has a value?

[...]

This explicit declaration is required in order to remind you that (...) you are actually modifying the value of the variable in the outer scope

This is an intentional asymmetry towards purely reading globals, which is considered proper practice.

Python FAQ: What are the rules for local and global variables in Python?

[...]

On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you’d be using global all the time.

What is the specific difference between block variable and block-local variable in Ruby?

Unfortunately there are no suitable keywords in ruby which might explain it beyond the doubt. So, let me translate it to javascript! (Javascript local variables are practically identical to ruby variables - with an exception for explicit creation keywords)

But before, few notes about scopes in javascript. Even though I'll be writing using JS, all the notes are also correct for ruby - except for a lack of the explicit variable keyword.

So: let keyword - let creates a new variable on the current scope. Once created, given variable can be read only when we are inside the same lexical scope or its child scopes:

let x = 0;

x #=> 0;

function() { # function creates a new child scope
return x; # access variable of the parent scope
}() #=> 0

It is important to understand that the scope is lexical, not dynamic - this means that variables resolves in a static context, which depends on the structure of the code rather than how the code is being called. This creates so-called closures (again - closures also exists in Ruby, however, unlike in JS, it's best to avoid them).

When searching for a variable, we always look at the current scope first and, if we have nothing defined in current scope, move to the parent scope. If no parent scope is found, exception is thrown.

So, let's translate your code into javascript:

let x = 0, y = 0, z = 0        
let ary = [1, 2, 3]

ary.forEach(function(x) { # x is now function argument
let y; # This is because of `|...; y|` in your block

# In this scope we have 3 variables:
# x - is an argument of a function
# y - is locally scoped variable
# z - is undefined in this scope, so it'll reference z from the parent scope
y = x
z = x
console.log(x, y, z)
})

console.log(x, y, z)

Let's analyze. In the above code, there are two scopes - top scope and child scope. There are 4 variables defined in your top scope (x, y, z and ary) and 2 variables defined in the child scope (x, y).

When you do y = x you first read the value of x - since such a variable exists in the current scope (and it is passed as an argument), we take that. In first iteration the value of x is 1, so expression evaluates to:

y = 1

Now we need to find variable y we want to assign to - there is variable y in the current scope so we assign to that one. The y variable in the main scope is not affected by this assignment

Next expression: z = x - x resolves the same way as before, so in first iteration we have:

z = 1

But now, there is no z variable in the current scope, so we look for a variable in the parent scope. In result, this assignment modifies the variable in the main scope

Next, we're printing x, y and z resolved in the current scope - there's no surprise here, they are all 1s.

Second iteration, everything repeats, but this time x resolves to 2. Again, we set the value of a y in a local, child scope and z of the parent scope. And then, third iteration.

In last expression we print the values of the variables, but in the parent scope. x is 0 as we have never done a single assignment to it, similarly y is 0 - because all the assignments were done against the variable defined in the child scope so, in fact, we didn't make a single assignment to it. z on the other hand was assigned to on each iteration. On the last iteration it received value 3 which is what it currently holds.

Hoisting of JS variables declared without 'var'

First off, ONLY variables defined with var are hoisted.

Assigning to a variable that has not been previously declared creates a global of that name at the moment that the assignment occurs.

Trying to read a variable that has not been previously declared causes a reference error unless you prefix it with a containing object such as window.global in which case it would return the same as any other property of an object that doesn't yet exist, undefined.

Your code is basically equivalent to this (with one added console.log() statement):

var outside;      // declared as a global
(function() {
var local; // the declaration of this variable is hoisted to here
i = 2;
if (i == 1) {
local = 'local';
global = 'global'; // creates a global variable only when this line is executed
}
console.log(outside); // undefined
console.log(local); // undefined
console.log(window.global); // undefined
console.log(global); // Uncaught ReferenceError, no symbol of this name is found
})();

That means both outside and local are defined at the time you try to use them so there is no reference error. Neither was intialized so their value is undefined. global is not defined when you try to reference it because your assignment to it was not executed so it does not exist. There is no hoisting for the creation of global variables with the use of var. Those variables are only created when and if the code that assigns to them is actually executed.

Why do some variables declared using let inside a function become available in another function, while others result in a reference error?

It's because you're actually saying:

c = 10;
b = c;
let a = b;

And not what you think you are saying, which is:

let a = 10;
let b = 10;
let c = 10;

You'll notice that no matter how many variables you add to your chain, it will only be the first (a) that causes the error.

This is because "let" scopes your variable to the block (or, "locally", more or less meaning "in the brackets") in which you declare it.

If you declare a variable without "let", it scopes the variable globally.

So, in the function where you set your variables, everything gets the value 10 (you can see this in the debugger if you put a breakpoint). If you put a console log for a,b,c in that first function, all is well.

But as soon as you leave that function, the first one (a)--and again, keep in mind, technically in the order of assignment, it is the last one-- "disappears" (again, you can see this in the debugger if you set a breakpoint in the second function), but the other two (or however many you add) are still available.

This is because, "let" ONLY APPLIES TO (so only locally scopes) THE FIRST VARIABLE--again, which is technically the last to be declared and assigned a value--in the chain. The rest technically do not have "let" in front of them. So those are technically declared globally (that is, on the global object), which is why they appear in your second function.

Try it: remove the "let" keyword. All your vars will now be available.

"var" has a similar local-scope effect, but differs in how the variable is "hoisted", which is something you should definitely understand, but which is not directly involved with your question.

(BTW, this question would stump enough pro JS devs to make it a good one).

Strongly suggest you spend time with the differences in how variables can be declared in JS: without a keyword, with "let", and with "var".



Related Topics



Leave a reply



Submit