fsharp / fsharp/fslang-suggestions

Support explicit generic constraints of multiple specializations of the same generic interface

Open
#1,036 10 comments 7 reactions 0 assignees View on GitHub
area: type-inference
Dominant language
No language data
Stars
373
Forks
21
PR merge metrics
No merged PRs in 30d

Description

# Support explicit generic constraints of multiple specializations of the same generic interface

I propose we allow:
```fs
type I<'T> = abstract U : unit
let f1 (_: I) = ()
let f2 (_: I) = ()
let f3<'T when 'T :> I and 'T :> I>(x: 'T) = f1 x; f2 x
```

You currently cannot do this in F#. The reason is given here:
https://github.com/dotnet/fsharp/issues/10432#issuecomment-738324344
> There is also a relevant F# type inference limitation that may be coming into play - I haven't looked closely at the repo above but it is definitely relevant to #10516
>
> The important thing to know is this:
>
> > **When F# sees `'a :> IA` and `'a :> IA` constraints for a type inference variable `'a` then it unifies `ty1` and `ty2`.**
>
> This is by design, and one of the reasons why multiple instantiations of generic interfaces was not supported in F#.
>
> The reason this is done is that it is very, very common to have `'a :> seq` and `'a :> seq` for partially inferred types `ty1` and `ty2`. This happens everywhere in F# code and in this situation it's crucial to flow type equalities between `ty1` and `ty2`.
>
> That is, the F# type inference system simply assumes that generic code is not constrained over multiple different interface instantiations.
>
> Type definitions that implement multiple instantiations can be *used* but you can't write F# generic code over this code or structure.
>
> It would be a separate RFC to enable this in some limited cases (e.g. when full type annotations are given), though I'd also recommend just not trying to do this
>
> This may or may not be the cause of the above. On a quick glance it is the cause of #10516

> When you do `dostuff mything` or `mything |> dostuffrev` the constraint of `IA = IA` is encountered before the constraint `IA = IA` and `int` is chosen to solve the inference type `?T`.
>
> When you do `5 |> dostuff mything` or `"blah" |> dostuff mything` the solution `?T = int` or `?T = string` is pre-chosen and it is obvious (I believe via the feasibly-equivalent relationship in the compiler and language spec) that the compiler can apply the appropriate `C :> IA` or `C :> IA`
>
> Given the constraint `C :> IA` the compiler will not continue with `?T` unresolved.
>
> So this is just how it works. If you're going to use this feature you'll need type annotations and/or flowed type information.

## Pros and Cons

The advantages of making this adjustment to F# are
1. Consistency with C#
2. Convenience
3. Correctness

The disadvantage of making this adjustment to F# is that this cannot be used together effectively with type inference for the reasons as mentioned.

## Extra information

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

Related suggestions:
https://github.com/dotnet/fsharp/issues/10432
https://github.com/dotnet/fsharp/issues/10516
https://github.com/dotnet/fsharp/issues/11659

## 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
* [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

No source files, tests, or compiler entry points are named. Start by reviewing related issues #10432, #10516, and #11659 alongside the type-inference rationale; done means agreeing on an approach that supports multiple specialized interface constraints without disrupting existing inference behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
fsharp
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.