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.

15 thoughts on “Default arguments are bad? I don’t buy it.

  1. Dave Rothgery

    I’m a long way from being a compilers expert, but couldn’t the versioning issue be fixed by changing the way the CLR (and the VB.NET compiler) handles default arguments?

    Reply
  2. James Slaughter

    In VB, it possibly makes very little difference to a good design, but there are significant problems when you translate that to another language.

    If you have any doubt, look at the rules for specifying default arguments whilst not redeclaring them (even to the same value) in C++. Also consider the type of a function with default arguments in that language: is the result more or less surprising than it would be for a set of overloads?

    I mean to pick up on some of the other .NET languages, now that they’ve become more practical (hence, I’m reading all the blogs I can find), but as of yet, I have no idea how well default arguments work with delegates or between languages. Does the compiler generate a thunk or an error to resolve this? Why?

    Reply
  3. Matthew

    >> (Anyone with any doubt on that last point only has to look no further than the Office object model.)

    What exactly are you referring to by the above statement?

    Reply
  4. russ

    Paul – why do it that way!?!

    Just have the compiler emit an overloaded call to handle the default value, which makes the whole argument moot.

    Reply
  5. Dewayne Christensen

    Matthew, I imagine he means things like this just to open an Excel file:

    Workbook _Open([in] string Filename, [in,optional] object UpdateLinks = ?, [in,optional] object ReadOnly = ?, [in,optional] object Format = ?, [in,optional] object Password = ?, [in,optional] object WriteResPassword = ?, [in,optional] object IgnoreReadOnlyRecommended = ?, [in,optional] object Origin = ?, [in,optional] object Delimiter = ?, [in,optional] object Editable = ?, [in,optional] object Notify = ?, [in,optional] object Converter = ?, [in,optional] object AddToMru = ?);

    Having to specify 500 "optional" ("required" for C# developers) arguments just to open a file sucks. And everything typed as Object?! If I was using a scripting language, I wouldn’t be using the PIA’s now, would I?

    Reply
  6. Diego Vega

    I remember Anders Hejlsberg a long time ago saying something about overloading and default arguments providing the same functionality to "client" coders. He also mentioned overloading doesn’t have the versioning problems. Maybe making default values part of the interface definition would be a good solution, but I have to agree with Anders.

    In my opiniono what really sets VB appart is support for NAMED optional arguments. It provides a superset of what overloading provides. For me, that is a wonderful construct that have saved me tons of coding both on client and library code.

    Reply
  7. Anonymous

    Since I posted this on Brad’s blog, which was in part based on yours, I’ll post it here also:

    Defaults are already used all over the place in .NET, and we should follow the pattern already established. For instance, if I write a function (say it’s a member of the string class) like so:

    public string PadLeft(int n, char c=’ ‘)

    The compiler should automatically do what .NET functions are already doing. It should compile one version of the function as written, and a second (overloaded) version of the function which just calls the function you already wrote: PadLeft(n, ‘ ‘). The JIT can optimize at runtime. Should the default change, the user code will call the function with the new changed default, which is exactly what you would expect if we changed the default value in the overloaded version. We’re already doing this, so what’s the big deal? Here’s another example::

    public string IndexOf(string value, int start, int count = value.Length – start)

    Now we get two functions for the price of writing one, and better yet, we won’t have to document the second one. When I type str.Split, I don’t want to see two overloads when one with default parameters would do. I don’t need to see two sets of documentation for identical functions.

    -Jeremy

    This is probably a bad place to post this, but since we’re talking about features of C#, I’ll add it. Declaring variables in C# is a pain. How many times must we write: type variable = new type(some parameters). Seems redundant, and is hard to read. How about this: variable := type(some parameters). Even better, variable := any_expression. Some examples, with C# equivalents:

    a: = 0; (int a = 0)
    a:=int[32]; (int []a = new int[32])
    a:="hello"; (string a = "hello")
    a:=MyClass; (MyClass a)
    a:=MyClass(); (MyClass a = new MyClass())
    a:=MyClass(3); (MyClass a = new MyClass(3))
    a:=MyClass[n]; (MyClass a = new MyClass[n])
    a:=(MyBaseClass)MyClass(); (MyBaseClass A = new MyClass())
    a:=MyClass(3)[n] …

    extend to member variables:

    private a := 0
    static a := MyClass()

    -Jeremy

    Reply
  8. Leon Bambrick

    default arguments certainly have their perils, but for people to argue against using them as a written in stone rule (in favour of overloading) is quite short sighted.

    You know the saying: ‘poor planning on your part does not consitute an emergency on mine."
    This could be extended to: "Sad limitations in C# do not constitute a reason for unnatural limitations in VB.net"

    As for this sentence of Paul’s:
    >> 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.

    Had to run that through the mental parser a few times to sort out the meaning!

    Reply
  9. Pingback: Panopticon Central

  10. Mike Schinkel

    Thank goodness for the optional parameters w/defaults! Without them, I feel I am programming with one hand tied behind my back. Why? Because they allow me to extend code w/o breaking existing code. If I realize I need another parameter, I can just add it and recompile w/o breaking other code that calls my method. Of course I could create another method that has a different prototype, but then I have to duplicate method declarations when one will do. I just wish functions in SQL supported optional parameter like stored procs do…

    Just another reason I don’t particularly like C#…

    Reply
  11. Ven0m

    Default params are something we need.

    First of all code shouldn’t be duplicated – because if we have a few parts doing virtually same thing and we change one, other won’t change.

    More, we work on code with a few persons, and someone finds bug in someone other’s code, fixes it without fixing code he hasn’t seen and which should do virtually same thing.

    We can avoid duplicates using default parameters. One of options, but you know that not too nice is making one function with all params and set of functions with different number of params calling it.

    Reply
  12. digit al

    Default values are benifical when routines have enough commonalities to share the same code. At the simplest level. Changing defaults in versionable libraries could be a bad idea.

    Reply
  13. Eric Schneider

    Bit late;

    I try to avoid them myself, but it is nice to see the default values, with overloads it is not apparent.

    There is a lot of things done best in VB, this is one of them. Another is Constructors in C# vs VB, every time I rename a class I need to rename the constructors!

    Anyone can abuse any language, the fact that we have more beginners abusing it proves VB is better…

    Eric Schneider

    Reply

Leave a Reply