for-loopf#ienumerable

Is this an F# compiler bug? #2


open System

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

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

let xs, ys = Foo(), Bar()

for x in xs do () // <--
for y in ys do () // fine

The code above produces the following compilation error:

The type 'Foo' is not a type whose values can be enumerated with this syntax, i.e. is not compatible with either seq<_>, IEnumerable<_> or IEnumerable and does not have a GetEnumerator method.

The code looks perfectly legal and the generic version works fine. Is this an F# compiler bug?


Solution

  • I think this is a mismatch between the error message an the specification. As kvb points out, the specification allows for ... in in only two cases:

    If the type implements a non-generic IEnumerable interface then it doesn't match any of the two conditions. However, if you cast it to IEnumerable then it will actually be the IEnumerable type, which matches the second condition. Having a GetEnumerator member directly in the type (as desco suggests) is also correct, because it also matches the second case.

    So, I think that the error message is incorrect, because it says that implementing non-generic IEnumerable is sufficient, but it is actually not.

    However, there seems to be one actual compiler error related to the for loop. You get a compiler "internal error" when you write the following code (which is not correct, because the inferred generic return type doesn't implement IEnumerator):

     type Foo() =
       member x.GetEnumerator () = null
     for x in Foo() do ()  // Internal error here