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

Allow implementation of different generic instantiations of the same interface on the same type #856

Closed
4 tasks done
Horusiath opened this issue Mar 23, 2020 · 1 comment

Comments

@Horusiath
Copy link

Consider following example:

open System

[<Interface>] type IHave<'T> = abstract Value: 'T
[<Interface>] type ILogger = abstract Debug: string -> unit
    
module Log =
  let debug (env: #IHave<ILogger>) = Printf.kprintf env.Value.Debug
    
module Random =
  let number (env: #IHave<Random>) = env.Value.Next()

Now, what we want to is to have a function that will specialize our env parameter over different variants of IHave<> interface - .NET allows types to implement the same interface multiple times with different generic type parameters:

let rollAndPrint env =
    let num = Random.number env
    Log.debug env "Rolled %i" num // compile error: "The type `ILogger' does not match the type 'Random'"

Apparently F# compiler interface inference doesn't take generic type parameters into account. Initially I thought I may be able to trick compiler by using interfaces from IHave<T>:

type ILog = interface inherit IHave<ILogger> end
type IRandom = interface inherit IHave<Random> end
    
module Log =
    let debug (env: #ILog) = Printf.kprintf env.Value.Debug
    
module Random =
    let number (env: #IRandom) = env.Value.Next()
    
let rollAndPrint env =
    let num = Random.number env
    Log.debug env "Rolled %i" num

But this also fails with the same exact error. Specifying env type constraints ('e when 'e :> IHave<ILogger> and 'e :> IHave<Random>) explicitly also fails with message: Type constraint missmatch. The type ''e' is not compatible with IHave<Random>.

Implementing different versions of the same interface is permitted in C#, however it's not the case in F#. Example:

type Env() =
    interface IHave<ILogger> with member _.Value = { new ILogger with member _.Debug msg = Console.WriteLine(msg) }
    interface IHave<Random> with member _.Value = Random()

fails with error:

This type implements the same interface at different generic instantiations 'IHave' and 'IHave'. This is not permitted in this version of F#.

This pattern is sometimes used in for message handling of different message types, including cases in popular C# libraries like MediatR. I think it could be useful to add this feature to F# as well.

Affidavit

Please tick this by placing a cross in the box:

  • This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
  • I have searched both open and closed suggestions on this site and believe this is not a duplicate
  • [] 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:

  • This is not a breaking change to the F# language design
  • [] I or my company would be willing to help implement...
  • ...and/or test this with a help of a mentor.
@cartermp
Copy link
Member

Closing as duplicate of #545

There is also a trial implementation here that might be worth contributing a test to, if you're interested: dotnet/fsharp#2867

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

No branches or pull requests

2 participants