2010-10-04 1 views
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 

Код выше выдает следующую ошибку компиляции:Является ли это ошибкой компилятора F #? # 2

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.

Код выглядит совершенно законно и общая версия работает отлично. Является ли это ошибкой компилятора F #?

ответ

5

Я думаю, что это несоответствие между сообщением об ошибке спецификации. Как KVB указывает, спецификация позволяет for ... in только в двух случаях:

  • Когда тип реализует общий IEnumerable<_> интерфейс (так называемый seq<_>)
  • Когда тип имеет GetEnumerator метод, который возвращает тип с определенными свойствами

Если тип реализует не-родовое IEnumerable интерфейс, то он не соответствует ни одному из двух условий. Однако, если вы нажмете его на IEnumerable, то это будет фактически тип IEnumerable, который соответствует второму условию. Наличие члена GetEnumerator непосредственно в типе (как предполагает desco) также является правильным, так как оно также соответствует второму случаю.

Так, я думаю, что сообщение об ошибке неверно, потому что в нем сказано, что реализация не общего IEnumerable достаточна, но на самом деле это не так.

Однако, похоже, существует одна фактическая ошибка компилятора, связанная с циклом for. Вы получаете компилятор «Внутренняя ошибка», когда вы пишете следующий код (который не является правильным, так как выведенный общий тип возвращаемого значения не реализует IEnumerator):

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

Спасибо за ответ, Томас! на самом деле я нашел эту ошибку и отправлю ее на [email protected] час назад ... – ControlFlow

+0

@ControlFlow: Ах, конечно, они исправят это быстрее, если получат два отчета за один час :-)! –

3

Я так не думаю, но это не очень полезное сообщение об ошибке. См. Раздел спецификаций Sequence Iteration Expressions для получения подробной информации о том, как вычисляются выражения for ... in ... do .... Если тип реализует IEnumerable<_>, шаблон работает так, как ожидалось. В противном случае компилятор ищет публикацию (spec говорит «доступно») GetEnumerator метод с правой подписью и вызывает это. Поскольку реализации интерфейса F # являются явными, метод GetEnumerator недоступен без повышения вашего типа Foo до IEnumerable. При выполнении вентиляционного, ваш код снова работает, как ожидалось:

for x in (xs :> Collections.IEnumerable) do() // fine 
5

ваш образец может быть упрощена до

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

for x in Foo() do() 

Изначально F # компилятор пытается утверждать, что тип источника является реализующим IEnumerable <_> После этого утверждение не удалось - он ищет доступные GetEnumerator/0 метод, который возвращает тип с доступной MoveNext()/Текущими членами.Кажется, что методы из явного реализации IEnumerable являются не видны типа Foo, потому что ниже код действителен:

open System 
open System.Collections 

type Foo() = 
    member x.GetEnumerator() : IEnumerator = null 

for x in Foo() do() // GetEnumerator is accessible in Foo 

или

open System 
open System.Collections 

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

for x in (Foo() :> IEnumerable) do() // IEnumerable has accessible GetEnumerator 
+0

Благодарим за объяснение! – ControlFlow