# C# Language Design Meeting for December 1st, 2021

## Agenda

1. [Roles and extensions](#roles-and-extensions)

## Quote(s) of the Day

- "A certain section of the community that I suspect are [long pause] uncommonly aligned with Haskell"
- "I feel like I might be, as [redacted] said earlier, uncommonly aligned with Haskell."
- "This conference room table is too heavy to flip"

## Discussion

## Roles and extensions

https://github.com/dotnet/csharplang/issues/5497

The idea of roles and extensions has a long history in C#, under various names, throughout the years. Previous discussions/issues
on the topic include (but aren't limited to: these issues have more links themselves):

* https://github.com/MattWindsor91/roslyn/blob/master/concepts/docs/csconcepts.md - Concept C#
* https://github.com/dotnet/csharplang/issues/110 - Type classes
* https://github.com/dotnet/csharplang/discussions/164 - Shapes and extensions
* https://github.com/dotnet/csharplang/issues/1711 - Previous roles and extensions exploration post

After some more thinking on the topic, we're back with a proposal in the space. It's early days yet for this proposal: extensive
prototyping and exploration of implementation details will be needed to make forward progress. But overall, the LDM is excited
about the direction this proposal is taking, and we spent all of today's session discussing the proposal, both from a high level
and getting into some implementation ideas.

Broadly, there are 2 different features here, and the feature we choose to pursue will heavily drive our implementation concerns.
There's a smaller, simpler feature where we say that we will never allow implementing interfaces on classes the user doesn't own.
(Not to say that it's a small feature, just that it's small_er_). This feature could be implemented entirely through erasure if
desired, requiring little to no runtime changes, but that would mean it has the commensurate issues erasure brings. Overloading
could be accomplished via `modopt`s, but edges would still bleed through in reflection and generic usage.

The larger, much more complex feature is saying that we will, at some point, allow implementing interfaces on classes the user
doesn't own. Even if this feature doesn't ship initially, it will impose a much higher burden on the implementation. In such a
world, this code (or something similar) should be possible:

```cs
// Assume some pre-existing Fraction type from an older library with a constructor that takes an int numerator and an int denominator
role FractionAdditiveIdentity : Fraction, IAdditiveIdentity<Fraction>
{
    public static Fraction Zero => new(0, 1);
    public static Fraction operator+(Fraction left, Fraction right) => left + right;
}

// With the above adapter, this code should be compilable:

public T Sum<T>(IEnumerable<T> elements) where T : IAdditiveIdentity<T>
{
    T result = T.Zero;
    foreach (var el in elements)
    {
        result += el;
    }
    return result;
}

IEnumerable<FractionAdditiveIdentity> fractions = ...;
Console.WriteLine(Sum(fractions));
```

However, if that code can compile, then it means a few things:
* FractionAdditiveIdentity needs to be real in metadata. It can't just be erased: there needs to be a real runtime type that
can be used to find the implementation of `T.Zero`.
* There needs to be some sort of variance relationship between `IEnumerable<Fraction>` and `IEnumerable<FractionAdditiveIdentity>`.
Is that an identity conversion somehow? Or a role variance conversion?

Neither of these can be accomplished via erasure, and moreover would be a big breaking change if we were to add them later. It might
be possible, with the right set of restrictions, to ship an erasure-based solution now and add this later, but we'll need to fully
explore the space and have a good understanding of how the full solution would be implemented before we ship the initial feature.

When it comes to erasure, there are also different stages that types can be erased in. For example, we could have real metadata for
roles, but they could be erased in most scenarios by the JIT compiler, as opposed to being erased by Roslyn. This could potentially
simplify the language rules, at the expense of making the runtime side more complex.

We also think there are opportunities to leverage our existing variance rules here. `List<Fraction>` and
`List<FractionAdditiveIdentity>` are hard to relate to each other in a non-erasure-based system, because those are really different
types and need to have different observable results in type tests, reflection, generics, etc. But perhaps we can say that there is
a _variance_ relationship between `List<Fraction>` and `IList<FractionAdditiveIdentity>`: they are not the same object, but the class
`List` type is convertible to an interface via a variant conversion, with automatic boxing conversions inserted by some stage of the
pipeline where necessary.

Overall, the team is interested in trying to go all the way here. An erasure-based system would allow us to ship in the nearer term,
but we've been talking about type classes in the language for years. We'd really like to enable the whole deal, rather than cordoning
it off forever more. Doing that is going to take both effort and time, but we think it's worth it.

Some other miscellaneous thoughts:

* Can roles reimplement underlying behavior? IE, can they provide their own `ToString` to override the existing one? While this is
potentially an attractive feature, it would have some serious consequences on implementation strategies, and it throws some odd
questions in for the goal of having `Identity` conversions from a role to the underlying type and back.
* Is there a way we could have automapping of role properties onto an underlying dictionary, enabling an "easy" mode similar to typescript
interfaces? This might be a place where source generators would work well, but if that's the approach we would want to take we'd need
`partial` roles.
* This feature would resolve a number of alias requests we've had over the years, particularly if we disallow sideways role conversion
(or make it require an explicit cast). This would allow a user to have a `Mile` role and a `Kilometer` role, both of which extend
some numeric type. They could even potentially be a generic role on top of `INumber<T>`, allowing `Mile<decimal>` to automatically derive
all conversion information from a generic implementation.
 
#### Conclusion

The LDM is excited to continue exploring this feature, in particular the full interface implementation version. This is a long lead
proposal: it's going to take time to explore and implement. But we think we have a promising start to really start prototyping with.
