19

In C#, I can implement a generic interface twice on one class, using two different type-parameters:

interface IFoo<T> { void Foo(T x); }

class Bar : IFoo<int>, IFoo<float>
{
    public void Foo(int x) { }
    public void Foo(float y) { }
}

I would like to do the same thing in F#:

type IFoo<'a> = abstract member Foo : 'a -> unit

type Bar() =
    interface IFoo<int> with 
        [<OverloadID("int")>]
        member this.Foo x = ()

    interface IFoo<float> with 
        [<OverloadID("float")>]
        member this.Foo x = ()

But it gives a compiler error:

This type implements or inherits the same interface at different generic instantiations 'IFoo<float>' and 'IFoo<int>'. This is not permitted in this version of F#.

I can't find any discussion of this issue on the web. Is such use frowned upon for some reason? Are there plans to allow this in an upcoming release of F#?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Gabriel
  • 1,443
  • 15
  • 24
  • Feature planned for F# 4.0 http://fslang.uservoice.com/forums/245727-f-language/suggestions/5663504-allow-to-implement-the-same-interface-at-different – foobarcode Oct 19 '14 at 20:01
  • Pull Request can be found at: https://github.com/Microsoft/visualfsharp/pull/18 – forki23 Jan 16 '15 at 11:24

2 Answers2

13

Right now I don't know of plans to allow this.. The feature has been planned and is, at least partially (see comments) implemented in F# 4.0.

I think the only reasons its currently disallowed are that it's non-trivial to implement (especially with F# type inference), and it rarely arises in practice (I only recall one customer ever asking about this).

Given an infinite amount of time and resources, I think this would be allowed (I can imagine this being added to a future version of the language), but right now it does not seem like this is a feature worth the effort of supporting. (If you know a strong motivating case, please mail fsbugs@microsoft.com.)

EDIT

As an experiment for the curious, I wrote this C#:

public interface IG<T>
{
    void F(T x);
}
public class CIG : IG<int>, IG<string>
{
    public void F(int x) { Console.WriteLine("int"); }
    public void F(string x) { Console.WriteLine("str"); }
}

and referenced it from F# (with comments suggesting the results)

let cig = new CIG()
let idunno = cig :> IG<_>  // type IG<int>, guess just picks 'first' interface?
let ii = cig :> IG<int>    // works
ii.F(42)                   // prints "int"
let is = cig :> IG<string> // works
is.F("foo")                // prints "str"

so this is what typically happens on this 'boundary' stuff with F# - F# can consume this stuff ok, even if you can't author the same stuff from within the language.

Abel
  • 56,041
  • 24
  • 146
  • 247
Brian
  • 117,631
  • 17
  • 236
  • 300
  • 2
    Er, so how would F# type inference deal with such types written in C#, then? As for rationale, well... ECMA CLI specs defines various categories of CLS compliance; one of them is "CLS extender". One requirement for that is: "Able to... Implement any CLS-compliant interface." – Pavel Minaev Sep 23 '09 at 07:34
  • To expand on Pavel's comment, what happens if you define a non-generic interface `I` in C# which extends both `IG` and `IG`? Can this interface be implemented from within F#? – kvb Sep 23 '09 at 13:49
  • @kvb, no, that interface cannot be implemented from F#. – Brian Sep 23 '09 at 17:06
  • 1
    This is how you might handle an object-oriented flavored event handling system, e.g implement IHandle<'TEvent> for several different kinds of events. There may be other more functional methods, but there's something to be said for doing it this way. – Sebastian Good Sep 15 '11 at 18:31
  • Here's a case: I'm implementing an IDictionary<'k,'v> which also needs to be iterated on as an ICollection<'v>, as it implements NotifyCollectionChanged. Seems like a straightforward scenario. – Rei Miyasaka Dec 21 '11 at 02:21
  • It is worth noting that F# is not getting it right all the time, and even treats this kind of interfaces differently between versions of F#, see my question here: [Difference between treatment of ambiguous generic interfaces by F# between VS 2012 and VS 2015 leading to compile errors in the latter](http://stackoverflow.com/questions/33533647/vs-2015-issue-with-using-multiple-specific-interfaces-with-different-type-parame?noredirect=1#comment54850360_33533647) – Abel Nov 05 '15 at 01:02
1

There is a reasonable although not elegant way to do it, create a new type for each interface here is an example of consuming multiple events from an ESB (nSvcBus) which requires that each event corresponds to an implemented interface. The first type below contains the generic 'handler' code, the other types just implement the interface and call to the generic handler

type public nSvcBusEvents() = 

    member this.HandleEvents(msg:IEvent) = ()
        //handle messages ie: let json = JsonConvert.SerializeObject(msg)

type public ActionLoggedHandler() = 
    interface IHandleMessages<Events.ActionLoggedEvent> with
        member this.Handle(msg : ActionLoggedEvent) = 
            nSvcBusEvents().HandleEvents(msg)

type public ActionCompletedHandler() = 
    interface IHandleMessages<Events.ActionCompletedHandler> with
        member this.Handle(msg : ActionCompletedHandler) = 
            nSvcBusEvents().HandleEvents(msg)

type public ActionFailedHandler() =       
    interface IHandleMessages<Events.ActionFailedHandler> with
        member this.Handle(msg : ActionFailedHandler) = 
            nSvcBusEvents().HandleEvents(msg)
Tom DeMille
  • 3,207
  • 3
  • 23
  • 30