Fabulous Adventures In Coding
Eric Lippert is a principal developer on the C# compiler team. Learn more about Eric.
Before I get into the subject of today's post, thanks so much to all of you who have given us great feedback on the Roslyn CTP. Please keep it coming. I'm definitely going to do some articles on Roslyn in the future; the past few weeks I have been too busy actually implementing it to write much about it.
We introduced "named and optional arguments" to C# 4.0; that's the feature whereby you can omit some optional arguments from a method call, and specify some of them by name. This makes the code more self-documenting; it also makes it much easier to deal with methods in legacy object models that have lots and lots of parameters but only a few are relevant. The most common commentary I've seen about the "named" side of that feature is people noting that this introduces a new kind of breaking change. The commentary usually goes something like:
Suppose you are making a library for consumption by others. Of course if you change details of the public surface area of the library, you can break consumers of the library. For example, if you add a new method to a class then you might make an existing program that compiled just fine now turn into an error-producing program because a call to the old method is now ambiguous with a call to the new method. In the past, changing only the name of a formal parameter was not a "breaking change", because consumers always passed arguments positionally. Recompilation would not produce different results. In a world with named arguments though, it is now the case that changing the name of a formal parameter can cause working code to break.
Though that commentary is certainly well-intentioned and informative, it is not quite accurate. The implication is that this breaking change is something new, when in fact it has always been the case that a library provider could break consumers by changing only the name of a formal parameter. Visual Basic has always supported calling methods with named parameters. In the past, changing the name of a formal parameter was a breaking change for any consumers using Visual Basic, and that's potentially a lot of people. Now it becomes a potential breaking change for even more users. Adding a new feature to C# doesn't change the fact that the risk was always there!
Library providers should already have been cautious about changing the names of formal parameters, and they should continue to be cautious now.
Changing a parameter name can also break dynamic invocation/ late binding including data binding so it has always had the potential to break many users.
I don't agree with Eric's conclusion.
It's not at all clear to me what you disagree with. I reached two conclusions. First, that those who say that the introduction of named arguments to C# introduces a new kind of breaking change for library writers are mistaken; this is an existing and ongoing problem, not a new one. Do you disagree with that conclusion, and believe instead that this is a brand new problem that has never been seen before? Second, I concluded that library writers should continue to be careful to not introduce breaking changes. Do you disagree with that conclusion, and instead believe that library writers should be cavalier about causing breaking changes to their users?
If you disagree with neither conclusion then what is thing that I actually said that you disagree with? -- Eric
IMHO, "polluting" C# with VB features of dubious quality is not a good thing.
Whether or not the feature is a good one for C# is of course debatable. There are good reasons why the design team resisted adding named and optional arguments to the language for ten years. For me personally, the big reason to not do the feature is the amount of complexity it adds to the overload resolution logic, not because it exacerbates a long-existing fragility issue.
Customer feedback over the last ten years, coupled with the continued popularity of legacy object models that expect the feature, was enough impetus to actually do the feature. All features have costs and benefits; we were finally convinced that the benefits to customers paid for the considerable costs. It was a good, pragmatic design decision driven by real customer requests. The feature is a fait accompli and has been for some time; complaining about it now is unproductive. The point of this article is not to debate the pros and cons of the feature, but rather to describe why it does not introduce a new kind of fragility. -- Eric
If I wanted to interface with some IDispatch-based object-model, then I'd have used VB just to wrap those legacy libraries into a tiny .NET layer and then I'd have enjoyed using that layer from C#.
Though that is a possible solution, it is not a very popular one with customers, not at all. Moreover, we have a stated goal of achieving greater parity between the C# and VB languages; this feature is part of that. -- Eric
This tiny VB "bridging" layer would have exposed the old legacy interface in a more modern form, e.g. using method overloads with a reasonable number of parameters, instead of fragile named arguments.
I think Eric makes a great point. If you are exposing your dll to external users, then don't change parameter names or orders, ever! Named arguments does not introduce a new problem. This is not polluting the language.
haha.. nice try.. not it's not the same because VB doesn't matter.
I'm not following your train of thought. What is not the same as what other thing? And to whom does VB not matter? VB matters to me and to the many millions of people who write programs in it. -- Eric
In c style languages parameter names never mattered in API design and was never a breaking change.. so who cares if VB had all along or not? C# never had it until this was introduced and that's the point.
I must again apologize for not at all following your point. My article is about how library writers need to be careful to not break their users, and must not assume that their users are using C#. Do you disagree with that? If so, why? -- Eric
The point is, C# is a .NET language, and if you are writing a .NET library, you have to be prepared for consumers to be written in any .NET language, especially one as popular as Visual Basic. Therefore, library writers who are complaining that this introduces a new breaking change were really missing the fact that they had been making potentially breaking changes all along for VB consumers of their library.
> In c style languages parameter names never mattered in API design
Objective-C is one C style language that uses named parameters.
But this is not really a relevant line of argument. C# is its own language, and should be designed by thinking about what makes the most sense for C# users. If the goal was just to follow the same decisions previously made by other languages, we would all still be programming in C and would have had no reason to create C# in the first place!
> C# never had it until this was introduced
Not true. In any environment that supports type system reflection and dynamic invocation (which .NET has always provided), any change to publicly visible metadata can be breaking for programs that use these features.
Not all C# developers may have realized that altering parameter names was a breaking change, but just because some people were unaware of something does not mean it was no so!
I'm actually glad C# introduced this *because* it makes the fact this is a breaking change more obvious. (the design guidelines have always noted this as a breaking change from the start). Of course, I mostly just love the fact I can write Directory.Delete(path, recurse: true) instead of /*recurse:*/ true now.
(Programming-language syntax) haters gonna hate (changes to programming-language syntax).
While I agree that Directory.Delete(path, recurse: true) is nicer than Directory.Delete(path, true) -- mostly because the latter makes no sense to an outside reader unless they are already familiar with the overloads of the Directory.Delete method -- I would much rather have had a consistent application of enumerations for these types of parameters.
It bugs me that we have DirectoryInfo.GetFiles(pattern, SearchOption.TopDirectoryOnly) and in the same class we then have DirectoryInfo.Delete(false), where 'SearchOption.TopDirectoryOnly' and 'false' are semantically equivalent. It would be so nice if these things were just a little more consistent (or if someone would ban parameters of type bool altogether).
I also think it should generaly not be necessary to change parameter names in public calls. If you find yourself in a situation where a parameter name is not what you wanted, then you shipped a library with a poorly code reviewed and poorly tested API. Don't blame that on the .NET framework or any .NET language. You'd have messed up yourself.
Changing method parameter names has always been a breaking change, I agree. One of the main features of writing software on top of the CLR is that assemblies can be used across language boundaries. If the authors of an assembly failed to incorporate CLS compliance it is their own responsibility. If your assemblies are CLS compliant you should be aware of VB consumers and how your C# code can break theirs.
Also the other more obvious reason, as Shawn pointed out, is that changing a parameter name will break code that uses reflection on that parameter, probably not seen very often but a definite possibility.
Introducing named arguments only made more room for a breaking change, it did not introduce a completely new breaking change.
Aren't named parameters just compile-time sugar? So when the caller produces their CLR assembly, the parameter name references cease to be significant. Parameters are slotted into the correct positions at the call site. This means that if a library changes a parameter name, it won't break already-compiled caller assembles. It will only break recompiles (from source) of callers.
(This of course is not true of the reflection case, where the lookup by name happens at runtime).
I would say that if you find yourself *strongly needing* to rename a parameter, it suggests that you're modifying the meaning of it. Doesn't that suggest a possibility that existing callers will now be broken anyway? They'll still be passing the correct type, but who knows it if will contain the correct value based on the new interpretation?
Extreme example: a boolean parameter 'throwExceptionOnError' is changed to 'doNotThrowExceptionOnError'!
Other examples may not be as extreme, but that might just make the new bugs more subtle.
There are more trivial cases where you just want to rename a parameter to clarify the existing meaning. You might feel the same about a vague method name, but you're stuck with that too.
In a multi-language platform like the CLR, you have to put exactly as much care into choosing parameter names as you put into choosing method names: they're part of the method name (in fact in Smalltalk and Objective C this is literally true!)
REST urls are another case where the parameter names effectively appear embedded in the call. Before C# 4 I used to grumble to myself about a library called OpenRasta, which is a RESTful webserver framework in which method parameters have significance in how they map to parameters in the URL. It seemed wrong to me at the time - but then, I was ignorant of the VB.NET thing.
I look forward to this feature. Named arguments is a feature that I have missed since I stopped using Ada.
First: I appreciate the time you spent reading my comment and answering me.
I disagree with the first conclusion. I wrote some DLL's in C++ exposing a pure C interface, and I used them from C#. I've never thought that parameter names were important; as I've never bothered with that both in my native C++ code and in my managed C# code.
Ignorance of a problem does not make it go away. Now you are no longer ignorant of the problem, but the problem was always there. It did not just suddenly appear. -- Eric
I've never considered VB.NET clients, I don't want to waste my time with VB.NET.
So you're saying that because potential consumers of your libraries might use a language that you find distasteful for some personal aesthetic reason, it's fine with you to break them with impunity? Perhaps you are in a market position where you can afford to impose unnecessary costs on potential consumers; I'm sure that's an enviable position, but just because you have the wherewithall to offend potential customers does not mean that you should. I assure you that I, and millions of other developers, am not in that position. My VB-using customers are extremely important to me and my team works hard to ensure that they do not have a bad experience when upgrading libraries. That includes ensuring that public surface area changes do not cause breaks. -- Eric
BTW: I do consider C# the king language for managed code; after VB6 - the "COM VB" - VB programmers flew in flocks to C#; when the only "games in town" were C++/MFC and classic VB, there was a real market for RAD and VB; but with the introduction of .NET and C#, I see no market for professional VB.NET development. I believe the *professional* VB.NET programmers are a very small number these days.
You are entitled to believe whatever bizarre thing you want; however I note that in this case your belief is not justified by facts. Professional VB developers are an enormous market and have been for almost two decades now. We would not be spending millions of dollars on VB infrastructure if this were a tiny market. -- Eric
Just out of curiosity: do you use VB.NET in Microsoft? In what successful Microsoft software is VB.NET used?
The most obvious example to me, because I work with it every day, is the VB runtime library itself, and the VB portions of the Roslyn project. But we at Microsoft are not selling VB to ourselves; how we use it internally is completely irrelevant. We're selling VB to the market. VB is heavily used in both government and business applications worldwide. Those are the customers that we do not want to break. -- Eric
But now with this new C# 4 feature you introduced a breaking change: I must be bothered with parameter names.
And about this language parity with VB.NET, I don't think this is a good value. Let's get good things from other languages, but please do filter stuff. ...Are you considering making C# case-INsensitive to achieve VB language-parity ? :)
Of course we are doing so judiciously; for example, it seems unlikely that C# will ever have XML literals, and it seems unlikely that VB will ever have unmanaged pointers. But going forward we will not be, say, adding asynchrony to C# and not to VB, or vice versa. -- Eric