【问题标题】:How to make Array Covariance work in F#如何使数组协方差在 F# 中工作
【发布时间】:2017-09-19 11:26:38
【问题描述】:

来自Array Covariance文章:

元素类型为引用类型的数组是协变的。 [...] 它被添加到 CLR 是因为 Java 需要它并且 CLR 设计者希望能够支持类似 Java 的语言。

CLR 支持数组协方差,但如何在 F# 中使用这个方便的功能?

type Foo () = class end
type Bar () = inherit Foo ()

// must have type Foo []
let foo : Foo [] = Array.create 1 (Bar ())
                                // ^^^^^^ must not cast to Foo
// must throw ArrayTypeMismatchException
foo.[0] <- Foo ()

在示例中,我想让 foo 在后台存储一个 Bar 数组,就像在 CLR 中实现的那样。

【问题讨论】:

  • 为什么要这样做?通常,F# 故意 避免了隐式类型转换的情况,数组协变就是其中之一。因此,虽然它在 C# 中可能是允许的,但在 F# 中故意困难(尽管并非不可能),并且您需要了解该语言的来龙去脉才能使用它。由于从您迄今为止提出的问题来看,您似乎对 F# 比较陌生,因此我的第一个建议是不要尝试使用数组协方差,而是找到其他解决方案(可能涉及有区别的联合)来您要解决的问题。
  • @rmunn 只要不是不可能,我想试试如何在F#中做数组协方差。

标签: c# .net f# functional-programming clr


【解决方案1】:

在 C# 允许类型之间隐式转换的许多地方,F# 需要显式转换。

F# 编译器甚至不允许从 Bar[] 直接转换为 Foo[]类型 'Bar []' 没有任何适当的子类型,不能用作类型测试的源或运行时强制。),您需要先转换为object

let foo =  Array.create 1 (Bar ()) :> obj :?> Foo[]

顺便说一句,我不会把数组协方差称为一个方便的特性,它导致的问题比它解决的要多。

【讨论】:

  • 完全同意“它带来的问题多于解决的问题”。它导致的问题之一是您可以完全绕过 F# 的类型系统安全网,编译器将无法保护您免受某些类型的非常常见错误。何时使用数组协方差的规则有点像何时优化代码的规则。规则 1:“不要这样做。”规则 2:“不要这样做”。当你知道什么时候适合打破这些规则时,你就会知道自己不会惹上麻烦。
【解决方案2】:

仅作记录,也许数组协方差有用的最值得注意的情况是当您有一个采用Foo[] 的函数并且您想使用Bar[] 参数调用它时。这是 F# 泛型所涵盖的内容,因为您可以使用 #Foo[] 语法编写适用于从给定类型继承的任何类型的函数:

type Foo () = class end
type Bar () = inherit Foo ()

let doFooThings (foos:Foo[]) = ()
let doFooThingsBetteer (foos:#Foo[]) = ()

let bars = [| Bar() |]

doFooThings bars         // error: The type 'Foo' does not match the type 'Bar'
doFooThingsBetteer bars  // no error!

【讨论】:

  • 这是正确完成的数组协方差。 doFooThingsBetter 可以从foos 读取,使用来自Array 模块等的函数,但它不能修改数组。如果添加foos.[0] &lt;- Foo(),编译器将忽略类型注释,函数类型将为Foo[] -&gt; unit。没有ArrayTypeMismatch 例外。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-10
  • 2016-05-03
  • 2014-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多