Это ошибка компилятора F #? # 2 - PullRequest
2 голосов
/ 04 октября 2010
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 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 #?

Ответы [ 3 ]

5 голосов
/ 04 октября 2010

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

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

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

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

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

 type Foo() =
   member x.GetEnumerator () = null
 for x in Foo() do ()  // Internal error here
5 голосов
/ 04 октября 2010

Ваш пример может быть упрощен до

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
3 голосов
/ 04 октября 2010

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

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