fsharp / fsharp/fslang-suggestions

Module interfaces for C# interop?

Open
#1,252 1 comment 6 reactions 0 assignees View on GitHub
Dominant language
No language data
Stars
373
Forks
21
PR merge metrics
No merged PRs in 30d

Description

This is an aggregate proposal, mostly summarising two different proposals that have recently been made for C#: [Practical existential types for interfaces](https://github.com/dotnet/csharplang/issues/5556) (issue 5556) and [static abstract interfaces and static classes](https://github.com/dotnet/csharplang/issues/5783) (issue 5783). More than proposing the details of an implementation of a new feature, I want to point out what, I think, the logical conclusion for F# would be if those C# proposals do pass.

First allow me to summarise those two proposals:

Issue 5783 proposes that static types should be allowed to implement interfaces. This was previously a non-issue but has changed with the recent addition of interfaces with static abstract members. If an interface has only static methods, why should not a static class be allowed to implement it? The restriction that static classes can not implement interfaces is particularly ad hoc because CIL does not even have an intrinsic notion of static classes (they are the same as abstract sealed classes). As a secondary change, the proposal suggests removing the restriction that generic type variables can not be instantiated by static classes, i.e., that it should be possible to have `MyClass` with `T` a static class.

Issue 5556 proposes that interfaces should be allowed to declare type names. Borrowing from the proposal:

```C#
interface Iface
{
// Existential type
type Ext;

public Ext P { get; }
}

class C : Iface
{
type Iface.Ext = int;

public int P => 0;
}
```

This may seem exotic but the author of the proposal shows that they could in principle be implemented without any special runtime support of changes to the CIL metadata format, by automatically turning the "existential types" into generics, and keeping track of constraints. The above could have the same compiled representation as

```C#
interface Iface
{
Ext P { get; }
}

class A : Iface
{
MemoryStream P => new MemoryStream();
}
```

(Compiling it like that might not be the best way, but the possibility to do it does make the proposal seem much less dramatic.)

Assume both of these C# proposals are adopted. We would then have:

* Interfaces with "existential types" and (only) static abstract methods;
* static types implementing such interfaces;
* and the possibility to have generic types be instantiated by such static classes.

Now, recall that F# modules are essentially static classes. (That is how they are consumed from the C# side; as remarked above, CIL has no notion of static classes.) Thus, by essentially just translating into F# we would have:

* "Module interfaces": Interfaces declaring type names and function signatures. This would be a notion quite close to OCaml module types. ("First class signature files" would be another way of phrasing it.)
* Modules implementing such "module interfaces".
* The possibility to have code generic in such modules. This would morally not be too far off from OCaml module functors.

In imaginary F# syntax, we could have:

```F#
module interface IM =
type T
val f : T -> string

module M =
interface IM with
type T = int
let f n = n.ToString ()
```

Do we want these things? Then go cheer for the mentioned C# proposals and try to steer the proposals in a direction that is optimal from an F# perspective! There are many details to sort out.

## Pros and Cons

The advantages of making these adjustments to F# are modules actually being modular. Consider the problem of using mocking for testing, where one typically wants to swap the actual implementation for a mock one. It could probably be accomplished by working against a "module interface", and then have different modules implementing that, switching between them.

The disadvantages of making these adjustments to F# are increased abstraction and non-productive discussions about how to best employ the shiny new toys.

## Extra information

Estimated cost (XS, S, M, L, XL, XXL):

Related suggestions: (put links to related suggestions here)

## Affidavit (please submit!)

Please tick this by placing a cross in the box:
* [x] This is not a question (e.g. like one you might ask on [stackoverflow](http://stackoverflow.com)) and I have searched stackoverflow for discussions of this issue
* [x] I have [searched both open and closed suggestions on this site](http://github.com/fsharp/fslang-suggestions/issues) and believe this is not a duplicate
* [x] This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.

Please tick all that apply:
* [x] This is not a breaking change to the F# language design. (It would be a big addition, but it would be backwards compatible.)
* [x] I or my company would be willing to help implement and/or test this

## For Readers

If you would like to see this issue implemented, please click the :+1: emoji on this issue. These counts are used to generally order the suggestions by engagement.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reading the linked C# proposals 5556 and 5783, then compare their implications with the imagined F# module-interface syntax in this issue. The proposal needs an agreed scope, syntax, and semantics for module interfaces, implementing modules, and generic use before implementation and tests can be defined.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, fsharp
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.