2

I want to make an interface with default implementation for equality and comparison functions.

If I remove everything from the type IKeyable<'A> except the Key member, it is a valid interface, as long as I don't add a default implementation. Removing the other interface implementations from IKeyable<'A>, and leaving just default members ends in the same result.

type IKeyable<'A when 'A: equality and 'A :> IComparable> = 
    abstract member Key : 'A

    default this.Equals obj = // hidden for clarity

    default this.GetHashCode () = // hidden for clarity

    interface IEquatable<'A> with
        member this.Equals otherKey = // hidden for clarity

    interface IComparable<'A> with
        member this.CompareTo otherKey = // hidden for clarity

    interface IComparable with
        member this.CompareTo obj = // hidden for clarity

type Operation = 
    { Id: Guid }
    interface IKeyable<Guid> with // Error: The type 'IKeyable<Guid>' is not an interface type
        member this.Key = this.Id

I would like to make use of IKeyable<'A> as an interface in order to "gain" the default implementations for equality and comparison.

The error message comes up on the interface ... with under the type Operation: The type 'IKeyable<Guid>' is not an interface type

Jean Lopes
  • 33
  • 3

1 Answers1

4

An interface cannot have method implementations, and your type has five of them - Equals, GetHashCode, IEquatable<_>.Equals, IComparable<_>.CompareTo, and IComparable.CompareTo.

An interface is purely a set of method and properties. It's not like a base class, it cannot give the implementor some "default" implementations or base behavior or utility methods it anything like that.

To make your type an interface, get rid of all the implementations:

type IKeyable<'A when 'A: equality and 'A :> IComparable> = 
    inherit IEquatable<'A>
    inherit IComparable<'A>
    abstract member Key : 'A

If you really want to keep the default implementations, you have to make it a base class rather than interface, in which case Operation must become a class rather than a record:

type Operation(id: Guid)
    inherit IKeyable<Guid>
    override this.Key = id
    member val Id = id
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • Thank you, I tought it was possible to use default implementations because of this: `However, you can provide a default implementation by also including a separate definition of the member as a method together with the default keyword`, source: [dotnet docs](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/interfaces) – Jean Lopes Aug 17 '19 at 02:23
  • 1
    That statement is technically correct, in that you can do that with abstract members. But it doesn't apply to interfaces. This looks like a bug in the docs. – Fyodor Soikin Aug 17 '19 at 04:26