Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add description of extension interface implementation #7928

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

agocke
Copy link
Member

@agocke agocke commented Feb 10, 2024

There are a lot of unknowns and difficult semantic constraints with bringing extension interface implementation to C#. This documents many of the challenges and outlines some of the most desirable semantics. When a particular semantic is suggested, example code is provided to show why that semantic is valuable.

There are a lot of unknowns and difficult semantic constraints with bringing extension interface implementation to C#. This documents many of the challenges and outlines some of the most desirable semantics. When a particular semantic is suggested, example code is provided to show why that semantic is valuable.
@agocke agocke requested a review from a team as a code owner February 10, 2024 00:07
string M2<T>(T p) where T : IPrettyPrint { ... }
```
Unlike in the Interface parameter example, no conversion is performed. However, in the above example, both `string` and `int` should be considered valid substitutions for the type parameter `T`, even though they would not have originally satisfied the `IPrettyPrint` constraint. Once again, the interface members on `IPrettyPrint` should be invocable on `p` and they should use the implementation provided in the extension implementations.
The main differences in this example and Interface parameter are around boxing. While a value type would have been copied and boxed with an `IPrettyPrint` parameter type, a generic substitution would normally not box the parameter during the invocation of `M2` or during a call to `PrettyPrint` if the input argument directly implemented `IPrettyPrint`. The same should be true of an extension implementation, and side-effects of `M2` and `PrettyPrint` should be propagated back to the `i` parameter in the call `M2(i)`.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same should be true of an extension implementation, and side-effects of M2 and PrettyPrint should be propagated back to the i parameter in the call M2(i).

I don't understand this.
In today's C#, a value type is copied in a method call, even in a generic one.
What do you mean by: should be propagated back to the i parameter.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a constrained instance invocation on a generic type parameter, the receiver is a ref variable, so the interface invocation is side-effect-preserving.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a constrained instance invocation on a generic type parameter

Can you give an example please, I'm still not sure to fully understand this line :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, so if you have a call like

interface IFace {
    void Mutate();
}
struct S : IFace {
  public int F;
  public void Mutate() {
    F++;
  }
}
void M<T>(T t) where T : IFace {
  Console.WriteLine(t.F);
  t.Mutate();
  Console.WriteLine(t.F);
}

You can see that the call to the interface Method Mutate does not copy the struct t. Instead, the function mutates the struct variable directly. If you look underneath, you'll see the reason why is that the this parameter in the S.Mutate method is actually ref S this, that is it's passing the struct by-ref.

This is a very high-level explanation. The exact semantics and requirements of the feature will be detailed below.

## Why
The basic idea and inspiration traces back to Wadler’s paper [1] on a feature called “type classes.” The basic goal of the feature is to allow the description of arbitrary sets of functions (interfaces) and arbitrary implementations of those sets (interface implementations). C# already allows some of those features, but the current design has severe limitations in two areas:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a link to the paper [1]?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, endnotes got lost somehow. It's https://dl.acm.org/doi/pdf/10.1145/75277.75283

### Interface Parameter

First, let’s consider one of the simplest possible examples: extending an existing primitive type with a new interface, and passing an instance of this type to a parameter of the interface type.
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(applies to other code snippets below)

Suggested change
```
```C#

Add reference to paper
@jcouv jcouv self-assigned this May 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants