2010-10-04 37 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 2010-10-04 15:14:43

+0

@ControlFlow:啊,如果他們在一個小時內收到兩份報告,他們一定會更快地修復它:-)! – 2010-10-04 15:24:58

3

我不這麼認爲,但它不是一個非常有用的錯誤消息。見規格的細節上for ... in ... do ...表現如何評價Sequence Iteration Expressions部分。如果類型實現IEnumerable<_>,則該模式按預期工作。否則,編譯器會尋找一個公共的(規範說「可訪問」)GetEnumerator方法與正確的簽名並調用該方法。由於F#接口實現是明確的,該GetEnumerator方法不可用而不上溯造型你Foo類型IEnumerable。如果您執行upcast,您的代碼將再次按預期工作:

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 <_> 在此之後斷言失敗 - 它搜索與訪問的MoveNext()/電流成員返回類型進行訪問的GetEnumerator/0方法。似乎從明確的實施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 2010-10-04 15:22:56