Fabulous Adventures In Coding
Eric Lippert is a principal developer on the C# compiler team. Learn more about Eric.
C# requires that every method be in some class, even if it is a static method in a static class in the global namespace. Other languages allow "top level" functions. A recent stackoverflow post asks why that is.
I am asked "why doesn't C# implement feature X?" all the time. The answer is always the same: because no one ever designed, specified, implemented, tested, documented and shipped that feature. All six of those things are necessary to make a feature happen. All of them cost huge amounts of time, effort and money. Features are not cheap, and we try very hard to make sure that we are only shipping those features which give the best possible benefits to our users given our constrained time, effort and money budgets.
I understand that such a general answer probably does not address the specific question.
In this particular case, the clear user benefit was in the past not large enough to justify the complications to the language which would ensue. By restricting how different language entities nest inside each other we (1) restrict legal programs to be in a common, easily understood style, and (2) make it possible to define "identifier lookup" rules which are comprehensible, specifiable, implementable, testable and documentable.
By restricting method bodies to always be inside a struct or class, we make it easier to reason about the meaning of an unqualified identifier used in an invocation context; such a thing is always an invocable member of the current type (or a base type).
Now, JScript.NET has this feature. (And in fact, JScript.NET goes even further; you can have program statements "at the top level" too.) A reasonable question is "why is this feature good for JScript but bad for C#?"
First off, I reject the premise that the feature is "bad" for C#. The feature might well be good for C#, just not good enough compared to its costs (and to the opportunity cost of doing that feature instead of a more valuable feature.) The feature might become good enough for C# if its costs are lowered, or if the compelling benefit to customers becomes higher.
Second, the question assumes that the feature is good for JScript.NET. Why is it good for JScript.NET?
It's good for JScript.NET because JScript.NET was designed to be a "scripty" language as well as a "large-scale development" language. "JScript classic"'s original design as a scripting language requires that "a one-line program actually be one line". If your intention is to make a language that allows for rapid development of short, simple scripts by novice developers then you want to minimize the amount of "ritual incantations" that must happen in every program. In JScript you do not want to have to start with a bunch of using clauses and define a class and then put stuff in the class and have a Main routine and blah blah blah, all this ritual just to get Hello World running.
C# was designed to be a large-scale application development language geared towards pro devs from day one; it was never intended to be a scripting language. It's design therefore encourages enforcing the immediate organization of even small chunks of code into components. C# is a component-oriented language. We therefore want to encourage programming in a component-based style and discourage features that work against that style.
This is changing. "REPL" languages like F#, long popular in academia, are increasing in popularity in industry. There's a renewed interest in "scripty" application programmability via tools like Visual Studio Tools for Applications. These forces cause us to re-evaluate whether "a one line program is one line" is a sensible goal for hypothetical future versions of C#. Hitherto it has been an explicit non-goal of the language design.
(As always, whenever I discuss the hypothetical "next version of C#", keep in mind that we have not announced any next version, that it might never happen, and that it is utterly premature to think about feature sets or schedules. All speculation about future versions of unannounced products should be taken as "for entertainment purposes only" musings, not as promises about future offerings.)
We are therefore considering adding this feature to a hypothetical future version of C#, in order to better support "scripty" scenarios and REPL evaluation. When the existence of powerful new tools is predicated upon the existence of language features, that is points towards getting the language features done.
UPDATE: More thoughts on considerations motivating this potential change here.
I'm sorry if anyone was offended by my "flamefest" label, but I stand by it. There were a number of comments that cannot be defined as anything but. For example:
> I'd hate for the C# language to become a cluttered, horrible mess just for the sake of "progress".
(actually, that whole comment was like that)
> I will stop programming in C# or .NET -once Microsoft tries to take this kind of direction.
> Top level functions leads to a completly messy and almost unmanageable/understandable code.
This is not to say that I consider all replies that disagree with my POV to be such. There were many reasonable replies with interesting arguments, including yours. My surprise was solely at a clearly knee-jerk, strictly emotional reaction some people here exhibited.
When all is said and done, there are definitely other features way higher on the priority list for me (and, I'm fairly certain, the C# team as well), many of which have already been named. I'm just not a great believer in OOP for OOP's sake.
+1 for doing..
It is the one thing that I would most like to be more compatible with Python and F#!
You know what I'd like to see? Easier integration of different languages in one project. .NET makes it technically possible to use (say) C# and F# in the same assembly through the use of modules (it's all bytecode, after all), but Visual Studio offers no support for this scenario. If you want to do this, you have to create one project per language, divvy up all the dependencies and tie the assemblies together, which is usually too bothersome.
Currently the framework is brimming with domain-specific languages which are implemented through designers, but I can't mix in an *actual* language for a specific purpose if my project requires it. Why can't I script the scripty parts with JScript.NET or use F# for the parts best expressed functionally and write the stuff I want component-based in C#? Then I wouldn't need a "one size fits all" C# that's chock full of things you selectively ignore (it isn't there yet, but I agree with others that it's moving in that direction with unsettling speed).
Of course, using multiple languages in one project makes it harder to maintain, and requires developers with a broader skill set. But in this case the trade-off is obvious an up-front. If on the other hand you have a language that wants to be everything to everyone (C++, the PL/I of our times), then for any project it's a crapshoot what approach it takes and what sort of developers will be proficient with it, and it also encourages idiosyncratic "geniuses" to reinvent everything at any given opportunity because the language lets them.
I think people are deceived into overlooking the cost of multiple-paradigm development by extending single languages slowly -- boiling frog and all that. There is a real need for it, but there's a cost, and having one enormous language for your development obscures that. If you could mix multiple languages more readily, you both serve the need and make the costs clear.
Revisiting this topic and the comments after your new post, I'm particularly struck by JM's comment which just precedes this one. I'd rather forgotten that it was possible to have a multi-language assembly, although at one point I was watching that development with considerable interest.
In my opinion, if you really want to take a script-like approach for some items of functionality and a more obviously strongly-typed approach for others, then supporting different languages within a single Visual Studio project would be the way to do it. Requiring the use of a different language, in a different source file, with a different icon, etc., is a type of encapsulation that would make it easier to delineate the boundaries between one approach and the other. Better doing that than blurring the lines within a language.
I take your point about considering all the possibilities, and recognise it's a good thing; but of course you're aware of the danger of diluting the things admire about C# for niche benefits. I hope the feedback you get from positing these things online is helpful; from outside Microsoft, the chance to comment is greatly appreciated.
I also hope the feeedback here reflects the bigger picture, since it agrees with my opinions ;-)
I was going to give a +1 for multilanguage assemblies, but think through what would be required for that in terms of dependency resolution. Assume you have a class F written in F#, with dependencies on a class C written in C#, in turn with dependencies on class F. With the language compilers being separate executables (hence each class would need to be compiled, incompletely, by a different program), think of what it would take to compile this. If it takes compiler changes, now realize how limited your language interop scenario becomes.
+1 for multi-language assemblies. THIS is where the future of interoperability lies.
The main scenarios for multi-language assemblies would not require circular dependencies, so leaving them out and calling it a limitation would not significantly reduce the usability of the feature.
Regarding multi-language assemblies - getting basic VS support for this would be a good start. I had a Connect ticket opened for that:
Note that this doesn't talk about adding full-fledged VS support. You'd still have to muck with .proj files directly in this scenario (though once done, VS build would happily use that).
However, for now, its resolution is rather disheartening - apparently, "the customer impact of doing this is fairly small" - which may well be the case, since I'm just one guy asking for a feature, and have no means of knowing how many more would find it useful. So - if you want it, go ahead and vote for it, send your usage scenarios, and so on.
Goodness, that's some heated debate out there!
Now, how about remembering one of the cornerstones of the modern society? Yes, I'm talking about TOLERANCE.
Will you stop programming in C# if Microsoft takes the direction you don't like? Will you stop driving if one more jerk cuts you off? Will you stop breathing if someone dares to pass winds in your presense? Be my guest!!!
Now, Microsoft has money and resources to make BOTH sides of the barricades happy: those who LOVE the top-level functions, and those who HATE them. WHAT IS WRONG WITH THAT??? What do you do whe you don't like some kind of music? You don't listen to it. What do you do when you don't like some movie genre? You don't watch it. So, if you don't like some new language feature, DON'T USE IT. That's all...
Though I certainly appreciate the sentiment, I think you might be missing the point of the people who are opposed to this change. Yes, the argument "I find this feature distasteful" is countered by "so don't use it". But the more important argument is "I think this feature will lead many other people to write bad programs that are buggy, hard to read, hard to maintain, have high cost of ownership, and trade short-term relatively unimportant benefits for long-term pain and expense." This argument is not countered by "so don't use it", because the problem is the mess made by other people.
We are well aware of this class of problems; I've blogged before about how we want C# to be a "pit of quality" language; that is a language where the constraints on the language naturally lead you to write well-factored, high-quality, robust, versionable, correct programs. A language where it is hard to write spaghetti code, memory leaks, or buffer overruns is, in our opinion, goodness. At the same time, we want to still support the features that lead to those negative outcomes, because arbitrary branching, deterministic finalization and direct access to memory are powerful and sometimes necessary. This is why we restrict the use of "goto" to never branch into a block. This is why we have the "using" statement. This is why we make you mark code that could overrun a buffer as being "unsafe".
The problem with designing a language where "a one-line program is one line" is that it makes it easy -- maybe too easy -- to write ten-thousand-line programs as though they were one-line programs. Whether the benefit of the former is worth the potential cost of the latter, and how to mitigate those costs while maintaining the benefits, are debatable points, and I welcome that debate.
Although I have known quite a few people who strife to BAN the music or the movies they don't like: they are all quite ingeniuos when it comes to finding pretexts: it's violent, it's sexually explicit, it's this, it's that... Can't we, the IT crowd, who all have the IQ above average, rise above that sort of things?
Eric, thanks very much for your thoughtful comments.
I am certainly not against a healthy debate about finding the proper balance between the language's power and safety, although personally I tend to remember that good ol' Ying-Yang symbol: both can grow or shrink, but neither can become larger than the other; a language can be either powerful, like C++ which, by the way, used to be my native language until my comparatively recent move to C#, or safe, like Java, where you cannot print anything without writing a native-code extension, but it can NEVER be both. If it's powerful, then its power, like any other power (see any fantasy novel for reference), can be used for good or evil: to create elegant solutions or to introduce awful bugs; if it's safe, it tends to put you under the glass and never let you out. The choice, then, is still yours: what do you value more, power or safety? Freedom or order?
In politics, this is the most common argument against freedom of speech: what if someone, somewhere, some day will say something that someone else does not like, and then that someone else will grab a bomb, and..! :-) It's also an agrument in favour of removing anything that can potentially cause any kind of controversy from the school program; as a result of that, I have met a few British nationals who had no idea who Rudyard Kipling was, and I also have to read Jack London's Tales of Southern Seas from the Internet because some of his passages are now considered politically incorrect and so the whole thing has been removed from the print.
But -- back to the topic. A debate about the best choice between the power and safety of C#, or any language, is welcome so long as it does not become religious, or ideological: THAT was, basically, my point. Now and again, I see people become so passionate about the "principle" of something that, in their eyes, that priciple becomes more important than the thing itself; hence, threats to ditch C# altogether is it's no longer "pure". And years before that, I knew some who used Borland C++ rather than Visual C simply because they hated, hated, HATED Microsoft (Grrr!!!) :-) By the way, even now Europe is full of people who use Linux and curse Windows for the same very reason. How childish is that??? All those operating systems, programming languages, browsers, and the rest of it, are just TOOLS that serve some PURPOSE, and, in my opinion, getting so passionate about a tool that you forget its purpose is, to put it gently, immature.
The decision between safety and power is not one or the other. Rather there is a continuum between them, the more power you have, the less safe you are (for the most part), and vice-versa, The trick of language design is to determine what level of safety you are willing to give up for what level of power. That is what the blog post and the comment debate is about, and I see nothing wrong with it. Nor do I think that the level of debate has come anywhere close to the point of invoking Godwin's Law. Nor do I see what Rudyard Kipling of Jack London have to do with any of this.
It's not the more power you HAVE the less safe you are; its' more like, the more power you USE the less safe you are. That is, in essense, my point: if someone is not comfortable with a certain level of power, he is free NOT TO USE it; it does not mean that he SHOULD NEVER HAVE AN OPTION to use it, should he change his mind.
I'm sure your car has something like 160 mph or 260 kph on its speedo; have you really gone that fast on a public road? Or should we now declare any car that POTENTIALLY CAN reach that speed dangerous and ban them all outright? It's the same with a language: it does not FORCE you to use all its powers, and there are always "some others" who may find the powers we avoid (for the safety's sake) extremely useful. So who is any one of us to decide for EVERYONE ELSE what's right and what's wrong for them? The more powerful the language is, the more of us are able to decide it FOR OURSELVES, and I see nothing wrong with that.
As for Kipling and London, they are just examples of how the fear of controversy, manifesting itself in the so-called "political correctness", criples the culture: just because they occasionnally used some words, and rised some subjects, that someone, somewhere might find offensive, their works are disappearing from people's memory. I would hate to see C#, or any other good and powerful language, cripled out of fear of its power.
It seems YOU (and not commongenius) are the one missing the point.
When dealing with a business development environment, is can be impossible to ENFORCE the useage of only "authorized" constructs. Code reviews do NOT ENSURE that they will not be used, only automated tools can hope accomplish this [and I have previously posted about the lengths some programmers will go to circumvent these, eve in the face of being fired].
Once it is POSSIBLE to use something, you have to assume that it may be used. Then you have to start figuring out how to make sure it is used corredtly.
I am NOT a ludite, and actually vote in favor of many of the features. I KNOW that the team at Microsoft (especially Eric) understands this COST.
You statement of "The more powerful the language is, the more of us are able to decide..." is the exact opposite of what heas been thre repeated experience of nearly every long term developer. The ONLY people who are truely qualified to make a decision (on any subject) are those who fully understand 100% of the alternativies and the risk/rewards of each. And the larger the required knowledge base is the smaller the percentage of people that will have the full knowledge.
As you yourself say, "the ONLY people who are truly qualified to make a decision (on any subject) are those who fully understand 100% of the alternatives and the risk/rewards of each."
Now, the question is, WHO are those people? ARE there any such people, ever???
As I move to software architecture and project management after slaving away as a developer workhorse for 19 years, I know how tempting it is to Command And Control: to Show Them All Who's The Real Boss, to finally get your vindication. I also remember how much resentmen such methods inspire: THAT is the true reason behind the ridiculous "lengths some programmers will go to circumvent these [automated tools enforcing the coding standards], even in the face of being fired". So I say: yes, work with your team on the GUIDELINES for best coding practices, but understand that life CANNOT be totally formalised and there is ALWAYS a situation in which the law is meant to be broken.
Like in a game of chess: as soon as you program your chess-playing application NEVER to place a knight in the corner, for example, it immediately runs into a situation in which such a move is the only chance to win (or not to loose).
Also, more trust in your people will not hurt: they will not push the boundaries more than necessary, UNLESS YOU LIMIT THEM more that necessary. Think Scrum: empowered, self-organizing team. I've just got my CSM certification, so I still remember...
And once again: NO ONE understands 100% of the ramification of each alternative, so it just might be convenient to keep them all handy, just in case the law we lay down proves to be wrong, and there is an urgent need for an alternative.