Why Doesn't C# Implement "Top Level" Methods?

Why Doesn't C# Implement "Top Level" Methods?

Rate This
  • Comments 58

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 just want to echo some of the sentiments here.  The benefits of scripting languages have been proven with Ericsson's derivation of Haskell and CCP's usage of Stackless Python in Eve Online.  But in both cases, however, a part of that sucess is because those languages were developed, from the get go, to be scripting languages.  C# is more along the lines of C++, Eiffel, and Java.  It's better to keep the original design that has been through 4 iterations then to try and change it.  If Microsoft wants to enter into the REPL language market, it would be better to devote reources to F# or just create .NET bindings for Haskell.

  • I guess I'm just joining the chorus, but --- please don't do this to C#.

    Strongly agree with the CPU wizzard, please don't make C# something its not, make it easier to interop with everything else.  (Just like the dynamic keyword makes it easy to call into dynamic languages without surrendering and becomming dynamic ourselves.)

    Now that there are actually several genuinely distinct .NET languages out there, the next innovation needs to be in the project system.  I would like to be able to include a F# class in my C# project, or even an F# method in my c# class.  Assemblies can't have circular dependencies, so just having two projects doesn't make it easy enough to elect a different language for a single class, I might have to re-factor my entire project structure to get all the dependencies to all the right classes with no cycles in the graph.

    I think that the pressure to make C# more like everyone else would be a lot less if I could easially decide "language X is the most natural expression of this concept" and go from there.

  • I agree with pmnieav that this is the singular distinction between static classes and namespaces, and that putting Math. in front of Sin, Cos, Abs, etc is just silly.  I don't think the answer is free functions.

    Provide a "static using" directive to introduce static members and subtypes of any class into the name resolution search space, make it be usable within methods as well as within types (don't need it file-wide), and there would no longer be any need for namespaces as a separate concept.

    Namespaces as a separate concept are dangerous anyway.  They don't respect visibility (can't be declared internal, stretch across assembly boundaries) and don't provide anything static classes don't, except at the level of syntactic sugar (the namespace name is optional when a using directive is in effect).

    The significant advantages of "static using" are:

    - it improves access to existing libraries

    - namespace pollution is explicitly opt-in, unlike free methods and to a greater degree than extension methods

    - visibility

    - generics

    Oh, I would also require a fully qualified name with "static using", no combinations with either "using" or earlier "static using" directives.

    And haven't we had this discussion before?

  • I do not see a benefit even in static using, given that we have extension methods. Forced static imports of modules is one of the reasons I do not use VB anymore.

    I think there is much more important stuff that can make people's lifes simpler, "yield foreach" is one (small) example. Allowing global level methods, on the other hand, gives nothing a tool can't give just by transparently wrapping code int o a class. For example, when you create C# extension functions in XSLT, you do not have to create a class, XSLT compiler does that for you.

  • @daniel:

    > Try thinking about it this way: a static method inside a class is really just a global method with a dot in its name.

    Except that it's not. The class provides scoping, context, and it can be imported using an alias. Utility.Foo and Utility.Bar will conflict if they are in two different classes.

    Sometimes you can find a useful class that actually provides context and makes those methods more easily discoverable. Sometimes you can work around it using extension methods (including some cases where you should not). Sometimes it's just getting in your way. At least it makes you think more about where to put it. And it provides a smell for code reviews. As long as you don't slide into procedural programming, it's only a minor annoyance.

    Given all the pro's and con's, I find it hard to justify putting effort into this. There are far more pressing features that would actually effect our daily development tasks (how about the hypothetical metaprogramming features of that hypothetical next version BTW? they are so hard to justify if you just count votes and ignore the fact that the few developers asking for them provide frameworks for so many others...)

    REPLs are going to need some additional stuff anyway, like ad-hoc variables. I don't see why any of that should be included in the "compiled edition" too.

  • It feels like you're about to Jump the Shark...

  • I like the idea of "static using" as described by Ben. I find that more desirable than having the functions themselves be free. That would solve the Maths.Sin problem without requiring any new maths API. I realise that I really don't object to the fact that the static methods live inside a static class, I just object to having maths-related code cluttered up with "Maths." all over the place.

    I don't see how extension methods reduce the marginal value of "static using". I really don't think "x.Sin()" is an improvement over "Maths.Sin(x)", and the idea of "y.Atan2(x)" is terrifying.

  • I agree with the majority of previous responders in my hope that this doesn't advance into positive points for implementation; and while I'm at it would second Stefan's interest in metaprogramming.

  • @ShadowChaser

    "Isn't that why Visual Studio contains other languages? I'd hate for the C# language to become a cluttered, horrible mess just for the sake of 'progress'.

    Strong vote against this change!"

    I completely agree with you ShadowChaser.  C# is good at being C#, there's no reason to try to make it do all of these things that other languages do.  It will only lead to the language becoming cluttered (much like VB).

    I don't want C# to become like VB or JScript or any other language.  I'd use a different language if I needed a feature that desperately, that's why other languages exist.

    I'm not saying that the C# team should stop developing new features, I'd just like them do develop features that naturally fit in and are "in keeping" with the language.  This would keep the language consistent and the features intuitive.

  • Interesting Finds: June 23, 2009

  • I really can't see this as a good thing. And I'll be very disapointed if I see this feature in a future version of C#. I don't want to have to tame my developers to don't do a completly mess again, as they did in the Delphi era. Top level functions leads to a completly messy and almost unmanageable/understandable code.

    If one wants a scripty language, choose another one, not C#.

  • While I do not strongly disagree with adding this feature to the language, I don't see a big benefit for it either.

    In my projects I would not use this feature, because I like to use (static) classes to organize my "global" functions. I usually have no problem coming up with a name for these classes, since you can organize almost any function with a simple Noun.Verb-scheme. This has the benefit, that I can see at the call site which class (and file, see below) the function belongs to and can quickly navigate there.

    It is also a matter of organizing the project. Usually I have one file per class. With global methods, would I put all methods in one big Global.cs, or even one function per file?

    So for any larger project, I don't really see how this would help me. And making the language more complex, with no (or minimal) benefit is always bad. If you need to add something to support "global" functions, maybe extending the using clause to let us import static members of static classes would be an alternative, although I wouldn't use that feature in my projects either.

    The only real use case for this feature in my eyes is for embedding C# as a scripting language in other applications. I have done this a few times and you can do a lot with a combination of sub classing and adding class constructs and using-statements automatically to the code the user entered. But this is of course fragile and I can see a real benefit here.

    However for these scenarios, it would be even more useful to me if we had an extensible compiler pipeline like boo, so that we can modify the compiler itself for the specific scenario we need to support. I believe I've read somewhere that the compiler team is already working on something like this while they're also rewriting the C# compiler in C#. Do you have any more information on this Eric?

  • I will stop programming in C# or .NET -once Microsoft tries to take this kind of direction.

    It not only sounds crazy but it is like going backwards. Saying that C# need to match

    other langugage features etc is just not right. I hope Microsoft will put people with OOP

    background in C# team -not people with scripting background - as in charge  

  • Another interesting post by the great Eric Lippert .

  • Another interesting post by the great Eric Lippert .

Page 2 of 4 (58 items) 1234