csharplang/meetings/2020/LDM-2020-04-15.md
2020-04-28 13:10:24 -07:00

5.2 KiB

C# Language Design Notes for Apr 15, 2020

Agenda

  1. Non-void and non-private partial methods
  2. Top-level programs

Non-void, non-private partial methods

Proposal: https://github.com/dotnet/csharplang/issues/3301.

Currently, partial methods are required to return void. They are implicitly private, and cannot have an explicit accessibility. In return for that, calls to a partial method that has a declaration but no definition can be safely elided by the compiler. Thus, partial methods serve as optional "hooks" or points of extension for generated code, code that is conditionally included based on compilation target, etc.

With the expected advent of source generators in the C# 9.0 timeframe, there are likely to many scenarios where these restrictions are too limiting. The proposal suggests a different trade-off for partial methods, where they can have return values, and can have broader accessibility, but in exchange the definition is mandatory: An implementation must be provided, since calls can't be elided.

The mandatory aspect can in fact be viewed as a feature. It is a way for one part of the code to require another part to be specified, even as they are separated across files and authorship.

Main concern is that we would need to preserve the "old" semantics for compatibility in cases that are already allowed in C#, and developers may accidentally fall into that case, failing to compel another part to produce an implementation, and having calls elided without wanting to.

One mitigating factor is that the existing feature doesn't allow you to explicitly say private - it has to be implied. So we could say that if private is explicitly supplied we are in the new semantics, and the method implementation is required. It's a subtle an non-obvious distinction, but at least it is there.

Another question is whether we would allow other members to be partial. We would need to work out the syntax in each case: E.g. how do you distinguish a partial property definition from a declaration that implements it as an auto-property?

Decision

Despite the weirdness of distinguishing between implicit and explicit private (the latter requires an implementation, the former does not), we are ok with accepting this wart in the language. The feature extension is valuable, and alternative solutions are distinctly less appetizing.

On the other hand we are not ready to allow partial on other kinds of members. If future scenario bear out a strong need, we will do the design work to hash it out, but we think methods are able to address the vast majority of what's needed.

Top-level statements

Proposal: https://github.com/dotnet/csharplang/blob/master/proposals/Simple-programs.md

We took a look at the currently implemented semantics to make sure we are happy with them. A couple of questions came up:

Expressions at the end

Part of the motivation for the feature was to decrease the syntactic distance between C# (.cs) and its scripting dialect (.csx). However, unlike script we still don't allow expressions at the end. For the scripting dialect this is mostly for producing a result in an interactive setting.

Decision

We are ok with this remaining distance, and would prefer not to have a notion of "expression at the end produces result" in C#.

Shadow and error

The proposal puts top-level local variables and functions in scope inside type declarations in the program. However, if they are are used in those places, and error is given.

Decision

This is deliberately there to allow us to do a more general form of top-level functions in the future. We do believe that it protects likely future designs for this.

Args

Currently there is no way to access the args array optionally given as input to an explicit Main method. Instead you have to make use of existing APIs that have a slightly different behavior (they include the name of the program as the first element), and certainly look different.

For anyone who uses the APIs in a top level program, they can still trivially move it into an explicit Main method at a later point, but going the other way with a Main body that uses args is not so easy.

There are ideas to:

  • add a new API that looks more like args (e.g. Something.Args) and behaves the same way
  • add args as a magic variable in top level programs (similar to value in property setters), on the assumption that 99.9% of Main methods use args as the parameter name.

Decision

We think this is important to pursue further, but aren't going to hold up the feature for it.

Await triggers a different signature

In the current implementation, the signature of the Main-like method generated from the top level program will be different, depending on whether await is used or not. If it is, then the signature will include async Task<...>, otherwise it won't.

An alternative would be to always generate a Task-based signature, and just suppress the usual warning when no awaits occur in the body. The choice doesn't affect the user much. The main difference is that with the current design there is no need to reference the Task types, and any limitations imposed by the language inside async methods are not in force, unless await is used.

Decision

We stick with the current design.