let f x = System.Object.ReferenceEquals(x,x)
f id // false
起初我认为这可能是因为一个函数可以多次转换为闭包,但以上证明了这一点。为什么最后一行返回false?
let f x = System.Object.ReferenceEquals(x,x)
f id // false
起初我认为这可能是因为一个函数可以多次转换为闭包,但以上证明了这一点。为什么最后一行返回false?
您可能已启用优化。这次是相反的问题。
如果启用内联会发生什么?
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 实现视为黑盒。