Monthly Archives: November 2003

Default arguments are bad? I don’t buy it.

Although I greatly respect the opinons of my fellow Microsoft employees (and most other people, for that matter), there are times when we have honest differences of opinions. A recent blog entry by Brad on default parameters is a good example.

I understand the versioning argument that he and the C# team make about default arguments, but I just don’t buy it. I think that not having (or not ever using) default arguments because of versioning is throwing out the baby with the bathwater. Brad gives an example of a VB function with a default argument:

Function ComputeTotal(ByVal subTotal As Double, Optional ByVal salesTax As Double = 8.8) As Double
Return subTotal + subTotal * salesTax / 100
End Function

He suggests that the following would be better:

Function ComputeTotal(ByVal subTotal As Double, ByVal salesTax As Double) As Double
Return subTotal + subTotal * salesTax / 100
End Function

Function ComputeTotal(ByVal subTotal As Double) As Double
Return ComputeTotal(subTotal, 8.8)
End Function

He feels the latter example is better because you can change the default value of salesTax and automatically have all clients compiled against the method pick up the change. Which is true. But I have to ask: what class library designer in their right mind would change the default value in the first place?

One of the major areas of concern for class library designers inside of Microsoft is compatibility, and one of the cardinal rules of compatibility is that you don’t make this kind of behavioral change. Why not? Because you’re likely to break people who depend, rightly or wrongly, on the default behavior that you introduced. (If you have any doubts about this, go talk to Raymond.)

In the given example, yes, the state may have changed its sales tax and so the calculation function may need to change. But just changing the default may cause serious issues. What if your application needs to calculate sales tax for sales that occured before the sales tax rate changed? Yes, you should have explicitly stated the sales tax you want to use, but it’s just as likely you used the default because: a) it’s easier and b) at the time you wrote your code, you got the expected result. As a result, when the default changes you’ll end up willy-nilly calculating the sales tax with the wrong rate! I would argue that in this situation the problem has less to do with default arguments or overloading than it does with bad API design.

My point is that whether you use default arguments or overloading, it’s extremely unlikely that you’re ever going to be able to change default values once you’ve published an API. That is, unless the API is completely internal to your application. In which case, the versioning “problem” with default arguments doesn’t apply! So in this case, I think the prohibition against default arguments is worrying about what happens in a situation that shouldn’t occur in the first place.

I will hasten to add that this doesn’t mean that I don’t think that overloading isn’t a good thing or that default arguments can be abused. (Anyone with any doubt on that last point only has to look no further than the Office object model.) In practice, I think that proper API design trends in favor of overloading simply because it encourages simpler API calls and reduces the need for things like named arguments. But there are definitely places where default arguments can be used to avoid adding superfluous overloads with very little overhead.

It’s good to know we are not alone…

Stan Lippman, a C++ guru at Microsoft has started blogging. His inaugural entry discusses one of the issues that their language design team faces in moving C++ to the CLR. Near the end, in a section entitled “The C++.NET Design Challenge,“ he says:

Literally, for every aspect of the C++ extensions to support .NET the question always reduces to “How do we integrate this (or that) aspect of the Common Language Runtime (CLR) into C++ so that it (a) feels natural to the C++ programmer, and (b) is easy to use in its own right under .NET. I like to call this the Janus face dilemma. (Janus is a two-faced Roman diety, the one turned facing towards what has just been, the other towards what is to be.)

As my entry title says, it’s good to know that we (the VB team) are not alone. Moving VB to the CLR was a major undertaking involving many, many, many dilemmas along these lines. Even though C++ and VB are two very different languages, I have a lot of empathy for what the C++ team is going through as they struggle with these questions. It’s not an easy path to trod.

Publishing lag

One comment I’ve gotten several times over from family members this Thanksgiving is “I was at the bookstore and I looked for your book, but it wasn’t there.” Apparently, in all my grousing about getting the manuscript in, I gave the impression that as soon as I turned in my manuscript, the book would go out to the shelves. I’ve still got all the grunt work of reviewing copy edits and page proofs to go before I get something real in my hand.

The book’s going to be part of the Microsoft .NET Development Series published by Addison Wesley that John Montgomery’s been talking about. The series includes books by Don Box and Chris Sells, which is a bit scary company to be keeping. On the other hand, I made a small contribution to one of the books in the series already, and they haven’t laughed me out of the club yet…

Gobble gobble gobble

It should come as no suprise to anyone, but entries this week are going to be slow given that I am visiting family for Thanksgiving. The feed, at least, is fixed thanks to a prod by Darrell Norton. I’ve had a lot of problems with FreeTextBox not translating my particular style of writing into valid XHTML, so my feed keeps getting broken by me incorrectly fixing up the HTML. Anyway, when I get back and get some time, I’ll look into solutions, but for now…

Another interesting use of When

Ok, I think I may have done the “When” thing to death, but Sam Spencer, a PM who used to work on the compiler, pointed out another interesting use of it. Often times, applications will have a global exception handler in the Main method that catches any exceptions that weren’t handled elsewhere. Something like:

Sub Main()
Try
RunApplication()
Catch ex As Exception
HandleGlobalException(ex)
End Try
End Sub

The compiler itself uses a strategy similar to this for out of memory situations — once we’ve run out of memory, the compiler’s toast, so we throw an exception indicating “out of memory” and then catch it at the top level, giving a nice “out of memory, please restart the compiler” error. (Hopefully none of you have ever seen it.)

Anyway, when you’re debugging the application it can be convenient to not catch exceptions at the top level so that you immediately break into the debugger when you get a global exception. A simple way to do this is to use When to not catch the exception when a debugger is present:

Sub Main()
Try
RunApplication()
Catch ex As Exception When Not System.Diagnostics.Debugger.IsAttached
HandleGlobalException(ex)
End Try
End Sub

Kind of cute.

Is too! IsNot!

I got a question from “Russ” who asks whether there’s something that can be done to avoid the annoyance of having to remember to start with Not when you are testing if something is not Nothing:

If Not x Is Nothing Then Console.WriteLine(”Has a value.”)

This is something I forget to do all the time, breaking the flow of my thinking. We’ve had requests for this in the past, so for Whidbey we added an inverse operator to Is called (can you guess?) IsNot. So you can write:

If x IsNot Nothing Then Console.WriteLine(”Has a value.”)

Just one of those little things.

Flow of control analysis

Esmond Hart asks:

I code a lot of VB.NET and the bug I make most often is forgetting to return the result of a function call. Daft but true. How I would welcome an option that flagged each End Function statement without a Return statement immediately preceding it. Option EvenStricter maybe.

Well, we’ll actually be doing better than this in Whidbey! One of the major features that we’re implementing in the compiler for Whidbey is “flow of control” analysis. This allows us to track at compile-time the ways in which a method can execute and then give the user warnings based on that analysis. For example, take the following code snippet: (I realize it’s a nonsense function, but my brain is working slowly this morning and I can’t think of anything better.)

Function Test(ByVal Value As Integer) As Integer
Dim c,d As Collection

If Value > 0 Then
c = New Collection()
End If

c.Add(Value)

If Value < 0 Then
Return Value
End If
End Function

In Whidbey the compiler will be able to walk through the function and figure out the ways in which it might be executed, determining the following things:

  • It’s possible that the variable c will be used without having assigned it a value.
  • It’s possible that the function will not explicitly return a value.
  • The local variable d was never used.

Once we’ve determined these things, we’ll give warnings on them. Pretty cool, huh? (Yes, I know, compilers for the C family of languages [and many, many others, I’m sure] have had this for forever, but it’s nice to join the party.)

Interface reimplementation

Frans points out that in the current version of VB.NET you can’t re-implement an interface that your base class implements. Although reimplementing a base interface isn’t a common scenario, there are some times (such as the one that Frans outlines) where it is needed. Although I don’t know whether this is in the PDC build or not, I don’t think it’s much of a revelation to say that the Whidbey version of VB will allow interface reimplementation. Doesn’t help now, but…