Oddly enough, my posting on how to handle a mad crush has become the third most popular article I’ve written so far. Who knew there were so many 10 year old girls interested in programming language design? I may have to turn this into an agony column. I‘ve posted some more techniques for handling a mad crush, for the benefit of all you 10 year old girls reading my blog.
In other news, I was going through my email archive the other day and I found a discussion from back in the days when we were designing the VB.NET syntax that I thought might give some insight into the kinds of considerations that go into some of the smaller decisions.
A lot of people have opined that VB.NET and C# are in many ways the “same language”, just with different syntax. If you were to take a chess board and change it from black and white to red and blue and you renamed all the pieces, but didn’t change the more fundamental rules about the ways the pieces moved, the new game would in a very real sense be “the same game as chess,” right? Whether VB.NET and C# really are in that sense mere syntactic variations on each other is a debatable point — I think it is not nearly so cut-and-dried as some people think. There are a lot of reasons I think that, and I don’t want to go into all of them today. One of the most interesting and important to me though is the most ineffable — the “spirit” of a language in many ways both transcends its syntax and suffuses some small decisions in interesting ways.
Let’s consider just one small example. VB6 did not have the
+= operator familiar to all C-like-language developers. In VB6 if you wanted to add ten to a number you said
x = x + 10
Looking at that, there’s some textual redundancy there. Clearly the intention of the programmer is “increment the variable by ten”, so why do we have to state the variable twice? This is an extremely common, simple operation so we can have a short-cut syntax for it that expresses it more compactly. (Such a shortcut, whereby a relatively cumbersome but legal syntax is replaced by a simpler syntax that adds no additional real representational power to the language is called a syntactic sugar.)
x += 10
Since incrementing a variable by one is also an extremely common operation, C-like languages have an even more compact syntactic sugar for this operation:
x++ or ++x
When the decision was made to add the
+= operator to VB.NET, one of our consultants commented
I am pleased to see the
+= construct (and I assume the other similar constructs) but the increment and decrement operators ++ — are missing; this seems like an oversight. […] if the += etc. are allowed then it is natural that the ++ be allowed.
Actually, it isn’t as natural as you might think, because VB.NET and C# are NOT mere syntactic variations on each other, and nor should they be. The reason why VB.NET doesn’t have the increment operator illustrates the difference in spirit between VB.NET and C#. In fact, it is NOT the case that a C-like
k++ is syntactic sugar for a VB-like k += 1 This is because in VB there has always been a strong line drawn between statements and expressions. Not so in languages like C. This is a perfectly legal C (and, for that matter, JScript) fragment, for instance:
2 + 2;
Not a particularly useful fragment, one that would probably produce a compiler warning, but legal — because in C, a bare expression with no side effects is a legal statement.
In C it is also legal to go to the other end of the spectrum and say
x = (k += 1);
k += 1
is an expression in C which returns the assigned value and as a side effect assigns the value to k (and then to x). Most sensible people never use this fact about the += operator, but it is nonetheless true in C because C makes weak distinctions between statements and expressions.
That’s not the case in VB.NET. In VB
k += 1 is not an expression, it is a statement, and ne’er the twain shall meet. Such a bizarre construction is a syntax error in VB.NET.
k++ is an expression. You can say in C
x = (k++) * (++k);
And get both the side effects (two increments), the multiplication and the assignment to
x. This is an extremely common usage in C-like languages, so if we were going to add the ++ operator, then developers would expect that k++ is an expression in VB.NET, not a statement. (The fact that they are expressions explains why there are two forms — one form returns the value of the variable before it is incremented, the other after.)
If you make the increment operator only legal in statements
— alone on a single line — then saving that keystroke is clearly not worth the testing effort that would go into this — much less the dev effort, the documentation effort, the localization effort, the specs that would have to be written, etc.
But if you make it a legal expression in VB.NET, you run into all kinds of problems. This operator causes problems in C-like languages, and it would cause the same problems in VB.NET.
Consider a hypothetical world in which
k++ and ++k are legal expressions. We would have to come up with some definition of what this code does:
Function Foo(ByRef x, ByVal y)
Foo = x * y
x = 10
k = 100
z = Foo(k++, ++k)
First of all, what numbers get passed to
Foo? Is this the same as
z = Foo(100, 101)
— stick k on the stack, do ++k, stick k on the stack, do k++
z = Foo(100, 102)
— stick k on the stack, do k++, do ++k, stick k on the stack
z = Foo(101, 101)
— do ++k, stick k on the stack, stick k on the stack, do k++
Second, does the
k++ pass a reference to k to the byref parameter, or does it pass it by value? Suppose it passes it by reference — then what does k equal when Foo returns? Do we do it like this:
k on the stack, do ++k, stick k on the stack, do k++, call Foo, assign 10 to k
k on the stack, do ++k, stick k on the stack, call Foo, assign 10 to k, do k++, so it’s 11.
Or, if it passes by value then
k is never set to 10, so we’re fine — k is definately 102 when we’re done as it is incremented twice.
We could come up with some answers to these questions, but they would be rather arbitrary. The answers are apparently sufficiently inobvious that the C++ standard leaves several of them unanswered, and therefore such code is not portable! The question about function parameter list evaluation order is dismissed as
“The order of evaluation of arguments is undefined; take note that compilers differ. The order of evaluation of the postfix expression and the argument expression list is undefined.” The question about whether k++ runs before or after the function runs is answered: “All side effects of argument expressions take effect before the function is entered” And with regard to whether k++ returns a reference to k — in C++, the ++ and — operators take lvalues and return lvalues, so yes, if VB.NET worked like C++ in this regard, evaluating k++ as an argument would have to pass a reference to k to a function expecting a byref argument.
Clearly these problems are solvable — we solved them in C#, obviously! — but you have to ask yourself how valuable saving that keystroke is if it adds these kinds of complexities to the language semantics. Adding complexity isn’t necessarily bad, but you should get value for your additional complexity, not a single keystroke saved!
simply is not very BASIClike. Maybe that marks me as hopelessly old-fashioned that I’m saying that anything is not BASIClike when we have a BASIC that has object polymorphism! Call me old-fashioned then — but I think that VB.NET is not and should not be “C# without braces” and that there are expression idioms which don’t particularly make sense in VB. Heck, I would argue that ++ is an idiom which does not work particularly well in C, C++, Java, JScript or C#. Sure, ++ in C lets you write very dense code that can only be read if you understand the particular idiom of C — but dense code is not necessarily fast code or maintainable code or readable code or correct code. Personally I only ever use ++ in loop incrementers and when walking strings a byte at a time — I’ll gladly change ++‘s to +=1‘s.
I’m all for adding syntactic sugar that makes code less verbose.
+= is a great example of that as it adds nothing new to the language, it just makes an existing common operation lexically shorter. It’s a real sugar. But increment operators are not mere sugar; they add new functionality, opening up immense cans of worms at huge cost for small gain.