1
let f x = System.Object.ReferenceEquals(x,x)

f id // false

起初我认为这可能是因为一个函数可以多次转换为闭包,但以上证明了这一点。为什么最后一行返回false

4

1 回答 1

2

您可能已启用优化。这次是相反的问题

如果启用内联会发生什么?

id将被重写为idFuncClass <: FSharpFunc.

整个表达式将被重写为:

Object.ReferenceEquals(new fsharpfun1(), new fsharpfun1())

您可以关闭内联:

[<MethodImpl(MethodImplOptions.NoInlining)>]
let f x = System.Object.ReferenceEquals(x,x)

您会发现比较再次起作用。但更大的收获是——比较 F# 中的两个函数是未定义的行为。事实上,函数类型甚至没有实现相等。

let illegal = id = id //this won't compile

这是 F# 规范中的相关部分:

6.9.24 未指定对象标识和类型标识的值

CLI 和 F# 支持检测对象身份的操作,即两个对象引用是否引用同一个“物理”对象。

例如,System.Object.ReferenceEquals(obj1, obj2)如果两个对象引用指向同一个对象,则返回 true。同样,GetHashCode()返回部分基于物理对象身份的哈希码...

当与以下 F# 类型的值一起使用时,这些操作的结果未指定:

  • 函数类型
  • 元组类型
  • 不可变的记录类型
  • 联合类型
  • 盒装不可变值类型

对于此类类型的两个值,System.Object.ReferenceEquals和 的结果System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode未指定;但是,操作会终止并且不会引发异常。

不需要 F# 的实现来为这些类型的值定义这些操作的结果。

规范建议将实际函数类型及其 CLR 实现视为黑盒。

于 2020-04-08T13:05:45.583 回答