Monthly Archives: August 2003

Just a few more changes

I’ve made a few more minor changes to the blog software, so let me know if things stop working for some reason. The major differences are that I replaced the calendar control with a monthly archive list, and I also added a "notable entries" sidebar so people can get answers to important questions like"who is this guy?" The software is getting pretty close to what I want, so I’m thinking this is probably the last revision for a while. (Barring some mad inspiration.)

What I’d really like to find is a nice blogging tool that gives me a good WYSIWYG editing experience with stuff like spell checking, along with a good HTML mode, a minimum of weird HTMLisms, support for some API like the Metaweblog API and extensibility. Right now, I’m using Frontpage and pasting it in by hand, but I’m sure sooner or later, I’ll go looking for something better…

The Ballad of AndAlso and OrElse

Last week, Rachel posted an entry talking about AndAlso and OrElse. Besides Integer and array bounds, this has got to be one of the most sensitive changes we made to the language in VB .NET 2002. So I thought I’d talk a little bit about it.

Prior to VB.NET, the VB language only had the And and Or operators. They were essentially bitwise operators, which means that they took their two operands and performed an AND or OR operation on each bit position to produce the resulting bit. So 3 Or 4 = 7, and 2 And 4 = 0. At the same time, the Boolean value True was considered to be equivalent to the value -1, which is just a 1 in each bit position. As a result, the bitwise operators behaved as if they were logical operators when working on Boolean values. So True And False = False, and True Or False = True. And if you mixed and matched Boolean and numeric values, things pretty much worked the way you’d expect them to. So True And 4 = 4, and 10 Or False = 10.

This all worked pretty well, and avoided the situation you have in languages like C where there are separate logical operators (|| &&) and bitwise operators (| &). However, there were some problems. The biggest was that it was not possible to support short-circuiting behaviors when doing logical AND and OR operations. Short circuiting is fantastically useful, especially when dealing with reference types. It is extremely common to want to write code along the lines of:

If (Not x Is Nothing) And (x.y = 10) Then
End If

Without short circuiting, though, this code will throw an exception if x is Nothing, because both sides of the operation are always evaluated. (Having learned about the usefulness of short circuiting from other languages, this limitation bit me all the time when I was working in VB, and I even saw quite a few bugs like this created by people who knew nothing about short circuiting but just expected it to "do the right thing.")

One of the things we wanted to do in VB.NET, then, was add short circuiting logical operations. A way of doing it would have been just to have changed the meaning of And and Or if the two operands were typed as Boolean, but this seemed a very unacceptable solution. In general, it is very bad to overload two very different behaviors on top of the same keyword. In other words, without knowing what the result type of Foo() and Bar() were, it would be hard to know what the behavior of

If Foo() And Bar() Then
End If

would be. Would Bar() always be evaluated or would it sometimes not be evaluated? Even worse, the behavior of the expression might change drastically by shifting the type of just one of the operands.

The only other alternative, then, was to introduce new operators. Our first thought was that logical operations are much more common than bitwise operations, so we should make And and Or be logical operators and add new bitwise operators named BitAnd, BitOr, BitXor and BitNot (the last two being for completeness). However, during one of the betas it became obvious that this was a pretty bad idea. A VB user who forgets that the new operators exist and uses And when he means BitAnd and Or when he means BitOr would get code that compiles but produces "bad" results. For example:

If 2 And 4 Then
End If

Assuming Option Strict was off, this would produce the value True (or -1) instead of the value False (or 0), as it did in previous versions. Given that this would be a very easy error for programmers to make and it would have an effect that could be very hard to track down and understand, it seemed clear to us that changing the meaning of these operators would be untenable.

That left the option of adding new logical operators, and we spent a lot time thinking about potential new keywords. I think I’ll do a whole entry later just on the question of choosing new keywords, but suffice it to say that AndAlso and OrElse were the best choices, in our humble opinions. Ironically, we’re not even the first language to use those names in this way – the functional language ML has andalso and orelse operators, too. (I don’t think we were aware of that at the time we were choosing the keywords, although I could be wrong about that.)

People seem to enjoy poking fun at the keywords, but after working with them for a while, I think they work pretty well and were the right way to add the new feature to the language without breaking people in bizarre and unexpected ways. Then again, as always, I’m biased…

[Correction 09/15/03: Fixed small math error pointed out by Stuart, below.]

Floating point follies followup

In a response to my previous entry, Cory asks:

…how would you solve the comparison of two values if one is still on the stack and the other has been [stored in a register]? In other words, what would be the proper way to handle this type of situation (work around, etc.)?

A good question, and one that I should have addressed. You can get the compiler to insert explicit CONV.R8 opcodes by using the CDbl() conversion operator. So the code example would look like:

Dim d1, d2 As Double

d1 = Atn(-1)
d2 = Atn(-1)

If CDbl(d1) = CDbl(d2) Then
End If

If you look at the code this compiles to, you should see CONV.R8 opcodes after each local is loaded, and that should force the values to both be truncated.

Floating point follies

While talking about exception handling, Roy ran up against a change in behavior from VB6 to VB.NET. In VB6, if you evaluate the expression "x = x / 0", you’ll immediately get a divide by zero exception. In VB.NET, if you evaluate the same expression, no exception is thrown. Instead the value of x is now NaN, also known as "Not a Number." In reality, you could get this behavior in VB6 as well – on the Compile tab of the project properties, there’s an "Advanced Optimizations" button that includes an option "Remove Floating Point Error Checks." If you check that and make an exe (it doesn’t affect F5 in the IDE), then the assignment will work and x will contain the value NaN.

The reason for all this is that floating point numbers (as defined by the IEEE) support a number of special values that aren’t usually surfaced to VB programmers. In addition to NaN, a floating point variable can also contain the values positive and negative infinity. Not being a numerical expert, I won’t even pretend to explain what these values are for or why the IEEE felt they were important to include as part of the definition of floating point numbers. The bottom line is that they are there. By default in VB6, though, we inserted X86 instructions that checked for the various non-numerical states and would throw an exception if you encountered one. In VB.NET, however, things were complicated by the way that the CLR IL was designed, namely the fact that it doesn’t support a really efficient way to check for non-numerical results. In VB 2002, we tried simulating the VB6 behavior by emitting CKFINITE opcodes after each floating point operation, but this absolutely tanked all floating point performance. So we ended up dropping the VB6 behavior, and now floating point division by zero will result in NaN instead of an exception.

A related issue is that most processors’ FPU registers can handle floating point numbers with a higher precision than can be stored in the standard .NET Double datatype. As a result, depending on how the CLR JIT compiler enregisters variables and temporary values, it’s possible that floating point operations (including comparisons) can be done at differing levels of precision. For example, the following code could print "False" instead of "True":

Dim d1, d2 As Double

d1 = Atn(-1)
d2 = Atn(-1)

If d1 = d2 Then
End If

That’s because one of the variables could be left in a FPU register while the other variable is actually stored back on the stack. When you compare the two values, they aren’t equal because one of the values was truncated (so it could be stored on the stack) and the other value wasn’t. By default, VB6 suppressed this behavior by emitting X86 code to truncate values that were at higher precisions. (You can turn this off on the advanced optimizations page, too, under the option "Allow Unrounded Floating Point Operations.") But the CLR equivalent – inserting CONV.R8 opcodes everywhere a higher precision value might be used – had similar performance problems to the NaN checks.

As a result, VB.NET floating point operations are much "closer to the metal," which is either a good thing or a bad thing depending on your perspective. You definitely have to be a little more aware of what you’re doing when dealing with floating point numbers…

Ask a Language Designer

There’s one other new thing on the site that I wanted to call out on in its own entry. Akin to an idea C# has used on their GDN site, in the "Links" section to the right there is now a link to the Ask a Language Designer page. You can use this page to submit questions that you’d like answered about any particular subject, and I’ll do my best to post some answers to the best questions on this blog. The disclaimer on that page is worth quoting, though, so here it is:

Questions will be answered at my discretion. Because of limitations of time, blog space, non-disclosure of future plans or the suitability of the question, there is no guarantee that a question will be answered. This forum is intended for questions regarding the design of the language, and not as a product support forum. All responses to questions will be made on the blog.

This is going to be a bit of an experiment, so please bear with us and don’t get too upset if it doesn’t work out for some reason! But I’m thinking we’ll be able to get some interesting questions answered…

Here we go again…

Not content to leave well enough alone, I’ve continued to hack away at the OpEd codebase to try and add some features that I thought would be useful. I’m now trying them out live, so if there are any problems, do let me know. Major changes are:

  • The backend storage is now a SQL Server database instead of an XML file. No one should really notice this, I hope.
  • I merged "comment view" into "permalink view" and added a permanent redirect from the former to the latter. I didn’t see much point in not showing comments in permalink view.
  • Every item now has a Trackback URL. I hope it works – I tried it out locally, but the world seems to be a variable place. Autodiscovery of TrackBack from RSS and embedded RDF in the page should work too.
  • I now generate dc:creator tags in the RSS, so names should start showing up in SharpReader.

I think that’s the major stuff. I’ve already had to fix a number of glitches since going live, so I won’t be surprised if there aren’t a few more disasters lurking around the corner…

Accessing shared members from instances

OK, quick quiz. And, yes, this is a trick question. Without looking at VS, what do you expect the following code to print?

Public Sub Foo()
Dim s1, s2 As String

s1 = "abc"
End Sub

Yes, that’s right, it prints nothing. Wha….? Because String.Copy is a shared function that takes a string and returns a copy of it. It has no effect on the instance that it’s being called on.

Another quick quiz. And, yes, this is also a trick question. Without looking at VS, what do you expect the following code to print?

Public Class Foo
Public Shared Baz As Integer = 10
End Class

Module Test
Public Function Bar() As Foo
Return New Foo()
End Function

Sub Main()
Dim y As Integer

y = Bar().Baz
End Sub
End Module

Yes, that’s right, it prints nothing. Wha….? Since Baz is a shared member, we do not need to evaluate the qualifying instance expression, so we don’t. So Bar is never called.

In general, it is a bad idea to call shared members off of instance expressions for these two reasons. Shared members cannot affect instances directly, so calling shared methods or changing shared properties/fields off of an instance may give surprising results. And because we do not evaluate expressions that qualify shared members, you won’t get side effects where you might expect it. (This has bitten even a few members of the VB team themselves.)

A legitimate question then is, if it’s a bad idea, why do we allow it in the first place? Unfortunately, the original logic has been lost to history – my grey matter no longer contains the memory of when and why we decided to allow this. One possible reason is that disallowing shared member access off of an instance expression causes problems in a common situation:

Enum Color
End Enum

Class Control
Public Color As Color = Color.Red
End Control

With just the simple rule that shared members cannot be accessed off of instances, the field declaration in Control would be in error. This is because the “Color” in “Color.Red” will bind first to the field Control.Color, which is an instance field. Which means you
can’t access shared members off of it, and enum members are shared. C# fixes this problem by introducing a special rule: in a context where the name of a variable is the same as the name of its type, you can access shared instances off of an instance. As it turns out, we had to introduce this rule anyway late in VS 2002 for the following related situation:

Class Control
Public Color As Color

Public Shared Sub Bar(ByVal SomeControl As Control)
SomeControl.Color = Color.Red
End Sub
End Class

In this case, the “Color” in “Color.Red” still binds to the instance field, but you run afoul of the rule that says that you can’t access
instance members in a shared context.

Whatever the original logic, it’s become clear that this is not a good design and that we made a mistake in allowing it. (Yes, Virginia, we do make occasionally make mistakes around here.) I encourage people to avoid using it whenever possible. Unfortunately, the compiler doesn’t really help you on this today, but it’s something we’re be helping you more with in the future.

“The only emperor is the emperor of ice-cream.”

[Ed note: Warning. No technology content ahead. May not be suitable for all readers.]

Well, I’ve finally broken down and installed the RealPlayer so that I can listen to The Writer’s Almanac on my computer every day instead of mostly missing it on our local NPR station. What I find fascinating about it is that it manages to bring poetry back from the obscure backwater that it inhabits in my high school English classes of the past. What’s even more fascinating is that normally I absolutely cannot stand to listen to Garrison Keillor do a reading – his “News from Lake Wobegon” segment from A Prairie Home Companion is like nails on a chalkboard to me. But for some reason I think his poetry readings are just absolutely superb. It’s probably because he lacks that self-conscious “poetry cadence” that most people (including poets themselves) use when reading poetry. I think the poetry choices are also very good and I have found the poems he reads to be very personally affecting, something that I had really forgotten that poetry can do. Many kudos to a fine and worthy effort.

And for those who wonder, the title of this entry comes from one of my favorite poems (do people still have those these days?), “The Emperor of Ice Cream” by Wallace Stevens. Why it is a favorite, I have no idea. Something about the line “Let be be finale of seem” just does it for me, I guess…

[We now return you to your regularly scheduled technology blog.]