# C# Language Design Meeting for May 12th, 2021

## Agenda

1. [Experimental attribute](#experimental-attribute)
2. [Simple C# programs](#simple-c-programs)

## Quote of the Day

- "I've always felt the shotput with the Rubik's Cube should be part of the Olympics"

## Discussion

Today we took a look at a couple of broader .NET designs that will impact C#, to get an idea of how we feel C# fits into these designs.

### Experimental attribute

https://github.com/dotnet/designs/blob/main/accepted/2021/preview-features/preview-features.md

The first proposal we looked at is the design the runtime team has been working on around preview features. The C# compiler has shipped
preview features in the compiler ahead of language releases before, but this hasn't been very granular, and we've never shipped a version
of the runtime (particularly not an LTS!) where some parts of the runtime and language features built on top are actually still experimental.
Support stories get complicated, particularly when you start mixing installations of LTS versions of .NET 6 and .NET 7 on the same machine.
For example, we'll be shipping a preview of abstract static members in interfaces in .NET 6, but this implementation will _always_ be a
preview. It's possible that, for .NET 7, we'll move the feature out of preview, and the version of the C# compiler in VS installed at that
point would no longer consider abstract statics to be a preview language feature, even if the project itself is targetting .NET 6. To solve
this, the runtime will introduce a new attribute, detailed in the preview feature proposal, which marks APIs and elements of RuntimeFeature
that should be considered preview in this version of the runtime, and then require that the consuming library be marked preview itself.
Where this requirement comes from is one of the open questions in this meeting: should it come from an analyzer or from the compiler itself?

The analyzer approach is initially attractive because it is easier to implement. Roslyn's analyzer infrastructure, particularly with the
investments around both the symbol analyzer model and IOperation, means that it is possible to implement this analyzer almost entirely
language-independently, while a compiler feature would have be implemented in each compiler separately. It is also significantly easier to
maintain an analyzer: turning off analyzer errors is possible for false-positives while bugs are patched, while compiler errors are impossible
to disable. However, this flexibility does come with downsides: it's possible to disable the analyzer for _real_ positives and ship in an
unsupported state, and potentially cause downstream dependencies to take advantage of preview features unintentionally. There's also no
guarantees that users actually enable the analyzer, as they might just disable analyzers for any particular reason. Despite these concerns,
we think that an analyzer is a good path forward, at least initially. We can use an analyzer in .NET 6 to iron out the semantics of how the
feature works, and look at putting the logic into the compiler itself in a future version.

In particular, there are some interesting semantics that still need to be worked out. Should APIs be allowed to introduce or remove previewness
when overriding or implementing a member? For example, `System.Decimal` already has a `+` operator today, and it will be implementing the
preview numeric interfaces that define the `+` abstract static operator. The `+` on System.Decimal is not preview today, nor should it be in
.NET 6, but it _will_ be implementing the preview `+` operator. Explicit implementation is also not always possible for these operators, as
we will have asymmetric operator support in the math interfaces that get unified with a symmetric operator later in the hierarchy, so we
can't rely on enforcing explicit implementation to solve this problem. On the opposite side of this problem, allowing adding of previewness
at more derived API level is problematic, because the user could be calling a less-derived method and therefore accidentally taking advantage
of a preview feature.

Finally, there is some IDE-experience work to think through. While we do want these APIs to show up in places like completion and refactorings,
we would also like to make sure we're not accidentally opting users into preview features that then break their build totally unexpectedly. We
think there is design space similar to our handling of `Obsolete`, such as marking things preview in completion lists.

#### Conclusion

We will proceed with an analyzer for now and look to move that logic into the compiler at a later point, if we think it makes sense. More
design is needed on some parts of the feature, but it doesn't require direct involvement of the LDM.

### Simple C# programs

https://github.com/dotnet/designs/pull/213

Finally today, we looked at "simple" C# programs. We take simple here to mean programs that don't have a lot of complex build logic, and are
possibly on the smaller side code-wise. Simple does _not_ necessarily mean "Hello, World!" and nothing else. While we are interested in the
intro experience, we additionally think there is opportunity to expand to address a market that has traditionally had a bunch of friction to
use C# in today.

C# has historically had a focus on very enterprise scenarios: professional developers working on larger projects using a dedicated IDE. Our
user studies have shown that this workflow isn't what many newer users are expecting, particularly if they're coming from scripting languages
like Go, Javascript, or Python. These users instead expect to be able to simple make a `.cs` file and run it, with potentially more ceremony
as they start adding more complex dependencies or other scenarios. Other expectations exist (such as repls), but our studies have shown that
this is the most popular. An important part of our task here will be figuring out where that "more ceremony" step lies, what that additional
ceremony will look like, and how it will interact with any additions we make to the language (how project-file based projects interact with
`#r` directives, for example).

Investing in tooling that puts NuGet directives in C#, as well as potentially `#load` or other similar file-based directives, is going to
necessitate that we reconcile file structure and project structure in C#. Today, the contents of C# files are very intentionally independent
of their locations on disk: file structure is handled by the project file, and project structure is handled by `using` directives and
namespace declarations. `#r` to local NuGet packages or `#load` to add other `.cs` files would blur the line here.

Another important consideration of these directives is how complex we want to let them get. At the extreme end, these directives could be
nested inside `#if` directives, which starts to necessitate a true preprocessing step that the SDK tooling will need to understand and perform.
Today, preprocessor directives in C# don't have massive effects, and they can be processed in line with lexing. There are restrictions we can
look at for where to allow `#r` and similar directives, and deciding on those restrictions will help inform where exactly that additional
ceremony will land. For example, we could require that all of these directives must be the first things in a file, with nothing preceding
them. This would ensure that conditional NuGet references are that cliff of complexity that requires a project file.

Finally, how much of the project settings do we think is reasonable to control in a .CS file? Is it reasonable to set language version or TFM
in a file? What about output settings such as dll vs exe, or single-file and trimming settings? We don't have answers for these today, and
some of these answers will be driven by discussions with the SDK teams, but they're all part of determining where the cliff of complexity
will land.

#### Conclusion

Overall, we're extremely excited to take this challenge on, and look forward to working through this design to find the edges.
