这是 F# 编译器错误吗? #2

问题描述 投票:0回答:3
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# 编译器错误吗?

for-loop f# ienumerable
3个回答
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

5
投票

您的样本可以简化为

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

for x in Foo() do ()

最初,F# 编译器尝试断言源类型正在实现 IEnumerable <_> 此断言失败后 - 它会搜索可访问的 GetEnumerator/0 方法,该方法返回具有可访问的 MoveNext()/Current 成员的类型。似乎 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
投票

我不这么认为,但这不是一个非常有用的错误消息。 有关如何计算 for ... in ... do ... 表达式的详细信息,请参阅规范的

Sequence Iteration Expressions
部分。 如果该类型实现了
IEnumerable<_>
,则该模式将按预期工作。 否则,编译器会查找具有正确签名的公共(规范说“可访问”)
GetEnumerator
方法并调用它。 由于 F# 接口实现是显式的,因此如果不将
GetEnumerator
类型向上转换为
Foo
,则
IEnumerable
方法不可用。 如果您执行向上转换,您的代码将再次按预期工作:

for x in (xs :> Collections.IEnumerable) do () // fine
© www.soinside.com 2019 - 2024. All rights reserved.