在什么条件下单位是一种类型?

时间:2016-10-27 11:11:46

标签: generics f#

在此之前被标记为重复:我知道当使用unit作为类型参数时,此问题与编译错误的各种问题有关。一些例子:

这些都遇到了与此类似的问题:

type Interface<'a> = 
    abstract member MyFunc : unit -> 'a

let implementingInstance =
  { new Interface<_> with
        member __.MyFunc () = () } // Compiler error!

根据我的理解,代码无法编译,因为单元返回函数在内部使用void返回进行编译,这是CLI的一个额外功能,而不是类型。

然而!以下似乎满足编译器:

type RecordVersion<'a> =
  { MyFunc : unit -> 'a }

let recordInstance =
  { MyFunc = ignore }

如果我用lambda或ignore绑定的模块函数替换let,这也有效。

对我而言,这只是完全相同的另一种表述。 (虽然与F#设计指南不一致,后者建议更喜欢接口而不是功能记录类型。)

我对设计用户指定所用行为和类型的API感兴趣。因此,我想避免出现意外和令人困惑的编译器错误的情况。但我不太清楚该怎么做。看起来像F#&#34;功能&#34;函数将单位视为一种类型。

单位出现这种虚假错误的确切条件是什么?我可以通过破坏设计指南并使用函数记录而不是接口来避免在我的API中使用它们吗? (我不会介意,但我不确定它是否能很好地解决问题。)

2 个答案:

答案 0 :(得分:3)

您的工作和非工作示例之间的区别在于,非工作示例是方法,在这种情况下(仅限AFAIK),F#编译器生成实际的IL代码接受或返回void。在工作案例中,它是类型为Microsoft.FSharp.Core.FSharpFunc<unit, unit>(又名unit -> unit)的属性,它无法“优化”为接受或返回void的内容。< / p>

所以是的,使用记录确实可以很好地解决问题。另一种可能性是使用属性创建接口:

type Interface<'a> = 
    // Note the parentheses to make this member a property rather than a method
    abstract member MyFunc : (unit -> 'a)

let implementingInstance =
    { new Interface<_> with
        // Must use an explicit `fun () ->` instead of a method arg list
        member __.MyFunc = fun () -> () }

如果你的问题是以单位作为参数,那么可以通过编写一个以(())作为参数的方法(即显式unit值而不是空列表来解决它。参数)。但是对于返回值,我认为没有任何方法可以使该方法有效:/

答案 1 :(得分:3)

我相信规则是静态已知具有返回类型unit的方法将被编译为.NET类型系统中返回类型为void的.NET方法(通过静态知道) ,我的意思是与使用类型参数作为返回类型的泛型类型的泛型方法或方法形成对比。在调用时,编译器会隐藏返回void的方法与在CLR级别返回true unit值的方法之间的区别。

您的示例中出现的问题是因为正确实现通用接口实际上需要CLR级别的unit返回类型(并且CLR确实关心unitvoid之间的区别)。换句话说,当且仅当您要覆盖通过静态已知返回unit的方法(基于替换unit的方法)返回泛型类的类型参数的方法时,会出现问题。那个类型参数)。通过覆盖这里,我的意思是在类或接口上实现抽象方法或在类上覆盖非密封方法。

正如泰米尔所指出的,解决此限制的一种方法是确保使用F#函数而不是方法。另一种解决方法是在层次结构中引入一个额外的具体类,该类具有一个虚拟泛型类型参数(比如额外的类是T<'unit>),并返回Unchecked.defaultof<'unit>而不是()引起问题。然后,您可以从T派生出一个额外的非泛型具体类T<unit>,一切都会正常工作。