Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage News C# 9: towards First Class Support for Scripting

C# 9: towards First Class Support for Scripting

This item in japanese

One of the defining characteristics of “scripting” languages is they don’t need any boilerplate. The very first line of a file can be the declarations and statements as you would see inside a function. By contrast, a language like VB, C#, or Java requires a ‘main’ method of some sort contained within a class.

Mads Torgersen of Microsoft is proposing C# 9 adopt this capability. Proposal 3117, Top-level statements and functions would allow statements and functions to be declared in a file without the need for encompassing class. His justification from the previous incarnation of this proposal is,

The C# compiler currently understands a dialect of the language used for various scripting and interactive purposes. In this dialect, statements can be written at the top level (without enclosing member bodies) and non-virtual members (methods etc) can be written at the top level (without enclosing type declarations).

Usage of the scripting dialect has been relatively minor, and in some ways it hasn't kept up with the "proper" C# language. However, usage is picking up through Try.NET and other technologies, and I am concerned with being stuck with two incompatible dialects of C#.

Currently the scripting version of C# isn’t heavily used, but Torgersen predicts that will change in the future,

Not only is there Try.NET, but data-science and machine learning scenarios are also growing, and those tend to benefit from a much more direct - interactive mode of working with live data.

Why then not just keep interactive/scripting C# separate? Because I think it is going to be useful to be able to roundtrip code between "experimentation" and "software development".

Scenario 1

If scenario 1 of this proposal is adopted, executable statements would be allowed to appear before a namespace declaration. Any such statements would be compiled into a Main function in a class named Program. This function would support asynchronous operations.

If multiple files have executable statements outside of a namespace, then a compiler error will occur. Specifically, you would get multiple classes named Program with Main functions.

Scenario 2

Top-level functions would also be allowed under scenario 2. The functions can be declared within a namespace or globally. They would default to internal, though public would also be allowed. Consumers would see the function as belonging directly to the namespace. (This is already how functions work in Visual Basic modules.)

The implementation would be that a partial class is generated to wrap the members as static members. The class name would probably be unspeakable, and would be chosen in such a way that the same name wouldn't be used twice in the same namespace across different assemblies. If any of the top-level members are public, the class would be public, and marked in such a way that a consuming assembly would know to expose the members directly.

Scenario 3

C# already has a scripting dialect, though it does differ from C# itself. The goal in this scenario isn’t to eliminate the scripting dialect, but rather move the two languages closer together. Torgersen elaborates,

If we want to add top level statements and functions to C#, we don't want them to conflict with how those work in scripting. Rather we want to compile them in the requisite manner when necessary, but unify on the semantics of the features. This doesn't fully eliminate the scripting dialect, as we would still need to deal with the special directives and "magic commands" that it requires, but at the minimum we do need to avoid the same syntax to not mean materially different things.

At this time, Mads is proposing that C# only focus on scenario 1,

You can squint and imagine a merged feature that serves all the scenarios. It would require a lot of design work, and some corners cut. I do not propose that. Instead I suggest that we fully embrace scenario 1, essentially fleshing out and slightly generalizing the feature sketched out for that scenario above.

He goes on to explain any functionality introduced by scenario 1 needs to be done in such a way that it will not prevent scenarios 2 and 3 from being implemented in the future.

Design Details

The first rule of Top-level Statements is only one file may contain them in a given project. Just as you can only have one “Main” function, having more than one file that contains naked statements will result in a compiler error.

The contents of the statements will dictate what the generated code looks like. There are four possibilities depending on whether or not await is used and if there is a return statement with an expression (e.g. return 5).

static void Main(string[] args)
static int Main(string[] args)
static Task Main(string[] args)
static Task<int> Main(string[] args)

Local functions are permitted using the same syntax one would use for local functions in normal methods.

Rate this Article