比较F#中的函数类型

时间:2014-09-10 15:16:01

标签: f#

下一次测试失败。我直接将GetType调用到函数定义,然后我也在内联函数中调用GetType。生成的类型不相等。

namespace PocTests

open FsUnit
open NUnit.Framework

module Helpers = 
    let balance ing gas = ing - gas

[<TestFixture>] 
type ``Reflected types`` ()=

    [<Test>] member x.
        ``test type equality with inline use`` () =
            let inline (=>) f = f.GetType().FullName, f in
            let fullName, fval = (=>) Helpers.balance in
            Helpers.balance.GetType().FullName |> should equal (fullName)

我怎么能得到相同的类型才能“可比”。

2 个答案:

答案 0 :(得分:5)

当您将函数用作值时,F#不会保证两个创建的对象将是“相同”。在封面下,编译器为每个实例创建一个新的闭包对象,因此即使你尝试这样的事情,你实际上也会获得false

balance.GetType().FullName = balance.GetType().FullName

这是预期的行为 - 当您尝试直接比较函数时,编译器会告诉您函数不满足等式约束并且无法进行比较:

> let balance ing gas = ing - gas;;
val balance : ing:int -> gas:int -> int

> balance = balance;;
error FS0001: The type '(int -> int -> int)' does not support the 
'equality' constraint because it is a function type

这意味着您问题的最佳答案是您要求的内容无法完成。我认为比较函数值很可能不是一个好主意,但如果您提供更多详细信息,为什么要这样做,或许可以为您的特定问题找到更好的答案。

如果你真的想对函数值进行相等测试,那么最干净的方法可能是定义一个接口并测试普通对象的相等性:

type IFunction = 
  abstract Invoke : int * int -> int
let wrap f = 
  { new IFunction with
      member x.Invoke(a, b) = f a b }

现在,您可以将balance函数包装在可以比较的接口实现中:

let balance ing gas = ing - gas
let f1 = wrap balance
let f2 = f1
let f3 = wrap balance
f1 = f2 // These two are the same object and are equal
f1 = f3 // These two are different instances and are not equal

答案 1 :(得分:1)

每次调用Helpers.balance时都会创建一个新的闭包,所以

Helpers.balance.GetType().FullName |> printfn "%A" //output: "Program+main@22-1" 
Helpers.balance.GetType().FullName |> printfn "%A" //output: "Program+main@23-2" 

与类相似(从c#中编译的exe反编译)

[Serializable]
internal class main@22-1 : OptimizedClosures.FSharpFunc<int, int, int>
{
    internal main@22-1()
    {
      base..ctor();
    }

    public override int Invoke(int ing, int gas)
    {
      return Program.Helpers.balance(ing, gas);
    }
}