views:

154

answers:

2
+2  Q: 

Is this an F# bug?

I have an type that is implementing IEnumerable<T> interface, all is ok:

open System

type Bar() =

     interface Collections.IEnumerable with
         member x.GetEnumerator () = null

     interface Collections.Generic.IEnumerable<int> with
         member x.GetEnumerator () = null

But things goes wrong if type inherits IEnumerable interface implementation via the base type:

open System

type Foo() =
     interface Collections.IEnumerable with
         member x.GetEnumerator () = null

type Bar() =
     inherit Foo()

     interface Collections.Generic.IEnumerable<int> with
         member x.GetEnumerator () = null

Code above produces the type inference errors:

  • The member 'GetEnumerator<'a0 when 'a0 : null> : unit -> 'a0 when 'a0 : null' does not have the correct type to override any given virtual method

  • The member 'GetEnumerator<'a0 when 'a0 : null> : unit -> 'a0 when 'a0 : null' does not have the correct number of method type parameters. The required signature is 'GetEnumerator : unit -> Collections.Generic.IEnumerator<int>'.

Am I doing something wrong or this is an F# compiler bug?

Microsoft (R) F# 2.0 Interactive build 4.0.30319.1


Update more canonical example:

type IFoo     = abstract Bar : obj list
type IFoo<'a> = abstract Bar : 'a  list
                inherit IFoo
/* ok */
type Foo        = interface IFoo      with member x.Bar = []
                  interface IFoo<Foo> with member x.Bar = []
/* fail */
type FooBase    = interface IFoo      with member x.Bar = []
type FooDerived = interface IFoo<Foo> with member x.Bar = [] // <---
                  inherit FooBase
/*
 error FS0017: The member 'get_Bar : unit -> 'a list' does not
    have the correct type to override any given virtual method.
*/
+3  A: 

The compiler cannot infer the correct type from your "null"-implementation. Try

open System

type Foo() =
     interface Collections.IEnumerable with
         member x.GetEnumerator () = null

type Bar() =
     inherit Foo()

     interface Collections.Generic.IEnumerable<int> with
         member x.GetEnumerator () : Collections.Generic.IEnumerator<int> = null

UPDATE:
The reason is, that the type of the GetEnumerator method implemented by the Bar type is ambigous as IEnumerable<'a> implements/inherits the non-generic IEnumerable which also specifies a (non-generic) GetEnumerator method. So, how should the compiler infer, which method exactly you are trying to implement if all he gets is null? Therefore we need a type annotation in this case.

Novox
ok, lets take an interface like this: `type Foo<'a> = abstract M : unit -> 'a` and try to implement it without specifying any types except required in declaration: `type Bar() = interface Foo<string> with member x.M() = null` - it works perfectly well!
ControlFlow
Sure, but that's not the same. An equivalent sample would rather read `type Boo<'a> = abstract M : 'a` and `type Foo<'a> = abstract M : unit -> Boo<'a>` and finally `type Bar() = interface Foo<string> with member x.M() = null`. Again, the compiler cannot infer Boo<_> (like IEnumerator<_>).
Novox
Hm, you're right, but what about first example? How the correct type is inferred there?
ControlFlow
Sorry, I take back what I said in my last comment. The sample actually works if you allow `Boo<'a>` to be null using the `[<AllowNullLiteral>]` attribute. So, what does this mean? My answer somehow solves the problem, but I am not sure about the reason anymore...
Novox
The problem seems to be related to the fact, that `IEnumerable<_>` implements `IEnumerable` (non-generic). If you implement both interfaces explicitly for the `Bar` type, it also works without any type annotations in the case of inheriting from `Foo`.
Novox
+1  A: 

This is not a bug, this is just an type inference fail because of F# may implement inherited interface members in the derived interface implementation declaration:

type IA = abstract A : int
type IB = inherit IA
type IC = inherit IB

type Baz =
     interface IC with
       member x.A = 1

So in my example I should specify the correct return type explicitly because member x.GetEnumerator() in derived Bar type may match both IEnumerable.GetEnumerator() and IEnumerable<T>.GetEnumerator().

ControlFlow
Just figured that out, too ;) Yes, the problem is that the type of the GetEnumerable is ambigous if not specified explicitly as both IEnumerable and IEnumerable<'a> specifiy such a method.
Novox