a blog on programming languages and other stuff…

What should this code do?

OK, I’ve got a question for y’all. Take a look at the code sample at the end of this entry and leave me a comment on what you think the output of the program should be. Note that I’m looking for people’s opinions here and this isn’t one of those “how well do you know VB” trick questions. Once you’ve put in a comment on what you think the output should be, then feel free to run it and see what it does, but do hold off commenting on the actual behavior until people have had a chance to comment. Once we’ve got some comments, I’ll write a longer entry on why I’m asking this and why the answer matters at this particular moment in time.

I also agree with Tim… It is declared inside the loop and therefore never loses scope.

I also agree with Anon. that maybe a warning that declared variables INSIDE a loop may retain previous held values… However, most material (books) that I have read in the past has warned of this anyways.

It _should_ cause the compiler to output "are you sure you really want to do this?", because declaring a variable of a value type inside of a loop doesn’t make much sense. What it actually does, though… I’d bet on 0,0,0 but wouldn’t put money on it.

I would expect the JITer to basically allocate memory for all the variables declared within a new scope at the same time (ie upon entering the scope).

It would not make sense to reallocate memory for the same thing over an over again, if it had an initialiser i would expect the initializer to be executed each time and if the initializer was 0 then i would expect the output to be 0,0,0.

My expectation of equivilency is

Dim x as integer

For i As Integer = 0 To 2

Console.WriteLine(x)

x += 1

Next

Console.ReadLine()

with an initializer i would say it would be the equivalent of

Dim x as integer = <initializer value>

For i As Integer = 0 To 2

x= <initializer value>

Console.WriteLine(x)

x += 1

Next

Console.ReadLine()

But of course x would not be available outside the scope of the for loop.

I could go as far as saying that X is declared in the same scope as i is.

I think the answer doesn’t matter. The fact that different people come up with two different answers for a trivial coding exercise, in and of itself speaks volumes about the problem. As I read through Abrams and Cwalina’s book on designing frameworks, it strikes me that this example suffers from the same problem that the guidelines are designed to prevent from happening. While this is clearly a language/compiler issue, I think the principles involved here are the same.

I’m tempted to say that it should do what ever the language specification says it should do but I suspect that "what should the specification say it should do" is what you are actually asking.

I’ve gone back and forth with the question. Do I want the compiler to be "smart" and know that it only has to create and initialize the variable once? Do I want the compiler to be literal and initilize the variable each time through the loop? Or do I want the compiler to be brain dead and give me an error?

Having this much ambiguity in code seems like a bad idea in any case. Beginners are going to get especially confused. Maybe what I want most is that a warning is given that tells people what is going to happen here.

I would expect this to return 0, 0, 0. I agree with a few people above:

1. If we apply ExtractMethod refactoring we’d not expect any different behavior.

2. I like the fact that C# gives an error and hate the fact that VB doesn’t even provide a warning.

3. I like that C# requires you to specify the default values; however since VB was designed in such a way that it would default then in for you, it should work consistently and reinitialize that variable everytime like it would if were declared dim x as integer = 0

The output of the compiler should be a warning regarding the uninitialized variable, but the output of the program should definitely be:

0

0

0

Anything other than that would be unexpected and probably caused by some subtle detail (are there any other kinds of those?), which would contribute to buggy code, IMHO. The language should cause the code to do what it looks like it’s trying to do, as much as possible.

Code can be written in many different ways, and should always work as intended. Eric points out a very reasonable refactor that I would not expect to alter the results. And why is C# smart enough to give an error when VB doesn’t even give a warning?

I’m in the 0,0,0 camp, for the reason that Shoddy Coder put forward. I hate the idea that performing an extract method on the body might introduce a bug to the code.

In my idea world, the compiler should throw a warning saying something to the effect that incrementing x isn’t going to have any impact. This almost falls into the category of the compiler determining the coder’s intention. Which is the point of Sergio’s comment that the language should cause the code to do what it looks like it’s trying to do. The question is who’s to agree on what the code is trying to do. To me, it looks like it’s trying to output 0, 1, 2. Just failing badly 😉

I can’t agree with 0, 1, 2 … if the intent of the code was that, then th Dim would have been outside the For/Next block. We should not need to remember special rules, or trip-wires… I think the intent is clear: 0 0 0, even in C# if I ignore the compiler warning.

The problem is whether each iteration of the loop should create new variable. What it’s initialized to is irrelevant, and I suspect Paul now wishes he’d specifically initialized it to keep the discussion on topic.

I think that whatever VB does now is what it should continue to do in the future.

However, I hope that it prints zeros, because otherwise I don’t see a convenient way to get this behavior. If I want to create a new variable within each loop iteration then how am I going to do it unless it’s automatic? If I don’t want the variable created each iteration, then I can simply move it to the containing scope.

Btw, I hope VB never warns about default initialization, or at least allows me to disable the warning. Rather than add this feature, it would be nicer to have the IDE automatically add the explicit initialization if that’s what the user chooses.

The intention isn’t completely clear. The fact that x is incremented at the bottom of the loop and then never used is the complicating factor. Either the Dim at the top or the increment at the bottom is misplaced. So it’s not obvious (at least to me) whether the coder wants a new x with each iteration or not.

X shouldn’t get reinitialized every time, unless an initialization value is specified, as in:

Dim X as Integer = 0

This would give you the flexibility to declare loop vars that don’t re-init every time through the loop, or declare loop vars that do re-init. Otherwise, X would need to be declared outside of the loop even though it’s only used inside of the loop.

On one side i think the benefits of comforming with other general purpose languages in mass-use (c,++, #, java) has tremendous advantages. You’re compiler works the way a majority of developers expect it to.

On the other side, I think VB.NET has different priorities and pressures than those other languages. The team works incredibly hard to make VB.NET more natural than any other. If, in their expert opinions, they think that the 0,1,2 behaviour is more natural and as such fits in with that VB.NET priority, so be it.

I don’t think 0,1,2 is at all more intuiative or natural. I think it’s incredibly subtle. Maybe that’s the C bias talking though

FYI, for those who haven’t run it, the code *does* print out 012, not 000. My refactoring prints out 000 on the other hand, which I think is a huge disconnect–it means that in at least in some cases in VB, an Extract Method refactoring may *not* be guaranteed safe, where the identical code and refactoring in C# *is* guaranteed not to change behavior.

However, if you do initialize the variable at declaration (i.e. Dim x as Integer = 0) it will print out 000.

I think the inconsistency is bad, myself, but it may be the only way to get a persistent-across-loops variable that’s scope-limited to the loop body, for whatever that’s worth.

As to what the code should do it depends on your upbringing. Either way, if refactoring doesn’t preserve the functionality, then it is a bug with Refactoring and not the compiler.

My personal preference would be to print 0 0 0. Then we have a consistent story that not initialising a variable is identical in *behaviour* to initialising it to the default value.

If we suddenly want to introduce the concept of variables that exist within the scope of a loop block then use a different notation e.g.

Dim x As Integer = x //you realise this works today, right?

So if you did what I suggest above, then you could convince the c# team to accept the same (with the same semantics of course) instead of asking “Assignment made to same variable; did you mean to assign something else?”

Naturally this would promote consistency between the two languages which, as I understand it, goes against the goals of both teams 🙂

Now in this case it does not matter if the variable is allocated, it may make sense to reuse it. However either way I believe that there is an implicit contract that on the line following, the varible will be initialised to its default value.

Now it would make sense for me to explicitly initialise variables in all cases that matter, and this is what I try to do. However for better or worse we might come to rely on the default initialisation. In this case a programmer could take a long time to realise what is causing these unexpected results.

I like to keep my variables as local as possible, and I am attracted to the idea that the scope of x could be equivalent to the scope of i, but it makes my code less clear and so I think that I would declare x outside the loop any way if I wanted that result.

okay, so now people have tested it, I s’pose we can discuss the "why" ?

Lets say we have the code call a method in the loop, passing the variable x out by reference ot another method. Suddenly the game changes dramatically. You now need the compiler to allocate an array (or risk overflowing the stack allocation). If we use an array though, I don’t believe you are meant to pass out references to elements…. it’s going to get real messy real soon.

So it is probably more logical in some ways to not allow that ever expaning list of local varaibles. But the question then becomes is this behaviour obvious, and I think the answer is obviously not. So perhaps a non obtrusive warning is in order (along with a declerative syntax for turnign that eror off if you are sure this is what you want your code to do)

But this leads to a bigger question. Here we have an artifical scoping, promoting the variable declaration up one level so as it stays alive on each iteration. Yet when we have a Try Catch block we don’t promote the variable or do we ? Consider this variation of Paul’s code:

Module Module1

Sub Main()

For i As Int32 = 0 To 2

Try

Dim x As Int32

Console.WriteLine(x)

x += 1

Catch ex As Exception

End Try

Next

Console.ReadLine()

End Sub

End Module

Now igiven the behaviour of x, we would expect we could also access x in the catch block, right ? After all x is actually promoted to outside the For block. yet we cannot.

For me it is quite simple, the variable is declared once, so it is initialized once. (The Dim statement is not an executable statement, if I’m not very wrong you cannot set a breakpoint on it in the debugger). If you want an intialization of the variable, code it!

I have been using this myself several times when I want a variable with scope only within the current block.

you can set a breakpoint on a Dim statement, and it is an executable statement. It allocates space for the object and sets the object to it’s initial state which in VB is Nothing( null or default in C#)

"Variable initializers on local declaration statements are equivalent to assignment statements placed at the textual location of the declaration. Thus, if execution branches over the local declaration statement, the variable initializer is not executed. If the local declaration statement is executed more than once, the variable initializer is executed an equal number of times. "

That’s because the Dim statement has no initialization indication and therefore the variable will be initialized to its default value when stack space is allocated to it. It seems to me that Dim is an execution statement only when some sort of initialization is explicitly indicated. Therefore, the Dim in the snippet is just a variable declaration (which implicitly clears the area that the variable will occupy, and thus, "initializes" it).

I can’t aggree with others that say that the compiler should warn about a non-initalized variable. VB’s tradition has allways been to auto-initialize variables with their default value upon declaration, it’s part of its semantic. If other languages require you to manually assign a value to a fresh variable, so be it, but that’s not how VB works. I frawn upon code that says something like "Dim B as Boolean = False". It seems to me as an overstatement.

Nevertheless, because there’s no explicit inicialization, the variable begins its life with the default value and keeps its last assigned value at each cycle, which is just as it should be, IMHO.

3 years ago, when I first approached VB.NET, I would expect it to be 0,0,0. Now I know that the result is 0,1,2.

I stumbled over this exact problem in production code countless times already, until I learned the behaviour to ALWAYS initialise the value inline, such as Dim a As Integer = 0.

What makes things worse: When you test the code, it often happens that you test the loop only ONCE (because of time constaints) – and the first interation is ok. You only notice it later, in production environment, that actually, the second and third interations don’t work anymore 🙁

It’s gonna print 012, which irritates me. I don’t see any real value in VB Block scope variables. Their lifetime is the same as other variables local to the procedure, but their scope is just within the block. I think their life should be block scope as well.

At least we can work around this by initializing the variable on every loop.

Bill McCarthy: Had to test it – VS2005 allows you to put on a breakpoint on a Dim statement (without initializers), but when the code is run the breakpoint is moved to the next (executable) statement.

Regarding the following:

******

page 180 of the VB langauge specification says:

"Variable initializers on local declaration statements are equivalent to assignment statements placed at the textual location of the declaration. Thus, if execution branches over the local declaration statement, the variable initializer is not executed. If the local declaration statement is executed more than once, the variable initializer is executed an equal number of times. "

this contradicts the behaviour.

*****

the first line says "Variable initializers…". There are no variable initializer in the given code…

Two paragraphs above the specification says:

"local variables are initialized to their type’s default value upon each entry into the method"

which is exactly what is happening.

I personally prefer the current behaviour, but that might be because I’m used to how vb does it. Though a warning might be a good idea.

Rolf: The document probably does mean explicit variable initializers, but it does not distinguish between explict and implicit initializers.

I think the current behaviour is undesirable, as the different opinions here lay testimony to. Ideally the language should be clearer on issues like this. Having the IDE or compiler warn on such constructs is really a second best option as that can just lead to excessive noise on a compile, but given the current situation the IDE interacting on such constructs is probably the best we can now hope for.

I do understand why the compiler does this for a number of reasons including backward compatibility, and the difficulty//almost impossiblity of implementing the variable as a true new allocation on each iteration. At best, it’d probably only be practical to implement it as the one slot that gets re-allocated. Given that constraint, the current implementation does offer some extra functionality, albeit via smoke and mirrors. In doing so I think it confuses some, and does not conform to the block scopign rules, rather the variable is scoped with the outer block (althoguh that is unlikely to bother people as to object lifetime except in some extreme cases)

So anyway, yes a rule such as "Warn on no explicit variable initialisers inside loop constructs" would possibly be helpful, as well as a <nowarn> attribute that cold be applied. If people do use this syntax, I want it obvious in their code they deliberately are doing so. Honestly, I don’t think there is any real scoping benifit, and in fact it is more prone to errors if you refactor etc. so I owuld be jsut as happy to see the IDE not allow that code, requiring the variable to be declared before the For Next block. doing that would make the code very clear to everyone and we wouldn’t have different interpretations. I think that is the ultimate goal.

So perhaps the best path forward is a compiler warnign in the next version, or two, warnign that this feature will be made obsolete, and this actual feature be totally deprecated for the sake of code clarity and developer sanity 🙂

I assumed it would have output 000. If I had wanted the 012 result, I would have Dimmed it immediately before the For line. For no particular reason other than "It just feels right", I see Dim statements as the birth of the variable, not a continuation.

I disagree with the claims that the variable’s Lifetime Scope is different from its accessibility. What we’re arguing over here is the very *definition* of scope.

If you rewrite the code removing the for loop (unrolling) then you could get :

Begin Scope of unrolled For loop

Dim x As Integer

‘ x = 0 ‘ If the var has explicit initializer

Console.WriteLine(x)

x += 1

‘ x = 0

Console.WriteLine(x)

x += 1

‘ x = 0

Console.WriteLine(x)

x += 1

End Scope

There’s nothing *wrong* with that per se. It’s just weird to anyone used to the behavior of C++, C#, etc, which is:

Begin Scope of unrolled For loop

Begin Scope of For loop contents

Dim x As Integer

Console.WriteLine(x)

x += 1

End Scope

Begin Scope of For loop contents

Dim x As Integer

Console.WriteLine(x)

x += 1

End Scope

Begin Scope of For loop contents

Dim x As Integer

Console.WriteLine(x)

x += 1

End Scope

End Scope

(Note, I’m not saying that loops are always unrolled by the compiler. I’m just trying to clarify the behavior.)

However, note that this problematic behavior does not exist for objects or valuetypes. Interestingly this means that the seemingly built-in type Date behaves differently.

For X As Integer = 0 To 2

Dim d As Date

Console.WriteLine(d.ToString)

d.Add(TimeSpan.FromTicks(1))

Next

Prints the same value three times, as do any user defined types.

So it seems you have a bug somewhere, because the behavior should at least be consistent. From an outsider’s perspective it would seem to be easiest to treat loops the same as in other commonly used languages, but I wouldn’t be too upset if you instead went the other way. Just make it consistent for all types.

If you *are* going to keep the current behavior then please, just for me, add an option to enable a warning whenever variables within loops are not explicitly initialized.

It should be 0, 1, 2 because when you define the variable you are then setting the for loop to run until the result reads 2. After the result is two the for loop closes because the result no longer reads the criteria set forth before the for loop.

Well i for one cant wait to see what Paul has to say, about the behaviour (especially if its a recomened practice or not, im starting to think not since C# doesnt do the same, and converting from VB to C# could lead to problems).

I noticed that looking at the IL of the decompiled method. The declarations for both variables (i and x) are made upon entry to the method and hence any more granular scope than the method level seems to only be an enforced rule for clarity rather than technical reasons.

That i did not expect (i expected the variables to be declared upon entry to the scope that contains it). So i was right but the reason was slightly off.

JustinM: Re the datetime code. The Add method does not change the intital variable, you’d have to assign it to the variable. So the behaviour you are seeign has nothign to do with loop scope. also, it is unlikely that adding 1 tick would change the date string in general format.

As ot the comment about objects, no, the behaviour is actually the same for objects, valuetypes and intrinsic types in regardss to scope and loop constructs. Tyr this simple code as an example:

A poll with some radiobuttons might have been better, I’d really like to see a running tally.

For the time being I think it should output what it currently does.

If the code didn’t output 0, 1, 2 – if x were reinitialized each time then it would completely nullify the whole block level scoping. A For loop isn’t repeatedly leaving its own scope and reentering it. All the iterations are in a continuous scope.

The variable i is also within the scope of the For loop, it doesn’t reinitialize to 0 every iteration.

Block level scoping is good for reusing the For loop’s imfamous i, j, x, and y variables multipletimes in a procedure and it does save you variable initialization if you have lots of variables and complicated control structures (if ginormous procedures are the kinda thing you’re into).

As for matching up with C# behavior, I think we’ve burned that bridge a while ago. I resent the implication that C# sets the gold standard for what is and isn’t good practice. While I like them matching each other in capability I also like room for innovation and personality especially where and alternative is provided – that is to say, C# for loops don’t cache their upperbound – can you cache it? yes. Can you make it so that x reinitializes every iteration? yes.

And as for new programmers being confused? It’s called reading. If they can’t read a paragraph about block level scoping then they really aren’t at the level to be formulating assumptions about programming language behavior.

Lastly (and this may just be my browser) for the love of Jesus, Mary, Joseph, and all the Saints find a way to make this feedback box wider, PLEASE.

Although Java on an MS VB thread will probably be about as popular as a New York strip steak at a vegetarian buffet, I’d like to share a perspective from another programming language.

0, 0, 0

is what makes sense to me, and here’s why. It does seem to me that the for loop’s scope is being entered afresh each time around the loop. Consider the following Java program:

public class Sevens {

public static void main(String[] args) {

for (String s : args) {

final int x = Integer.parseInt(s) * 7;

System.out.println(s + ": " + x);

}

}

}

We iterate over the command line parameters, converting each one to an int and multiplying by seven. Notice that the variable x’s scope is confined to the loop, and that it is declared "final", meaning that it cannot be changed once it has been set.

At first blush you might think this is not a legal Java program, but in fact it is, and does just what you would expect. Of course, one big difference may be that it is the BLOCK of the for loop (the curly braces) that introduces a new scope. If the understanding is that in VB it is the FOR keyword itself introducing the scope, I can see why one would argue that the scope is not being exited and entered each time around the loop.

I hope this outsider’s perspective is of some benefit. Consider it grist for the mill.

>>Do I want the compiler to be "smart" and know that it only has to create and initialize the variable once? Do I want the compiler to be literal and initilize the variable each time through the loop? Or do I want the compiler to be brain dead and give me an error?

I would prefer the compiler be brain dead. If the code is ambiguous, then warn me and don’t compile. That way I have to write what I want it do do… clearly and concisely.

Since it is declared within the body of the For…Next statement, the scope of x should be within the body of the loop and therefore initialized for each pass through the loop. x should not be in scope when the For…Next statement counter is evaluated.