F# 比较 lambda 是否相等

F# comparing lambdas for equality

我想尝试比较 F# lambda 的相等性。乍一看,这是不可能的。

let foo = 10
let la = (fun x y -> x + y + foo)
let lb = (fun x y -> x + y + foo)
printfn "lambda equals %b" (la = lb)

产生错误

The type '('a -> 'b -> int)' does not support the 'equality' constraint because it is a function typeF# Compiler(1)

然而,令人惊讶的是,可以序列化 lambda 函数。

open System.Runtime.Serialization.Formatters.Binary
open System.IO
let serialize o =
    let bf = BinaryFormatter()
    use ms = new MemoryStream()
    bf.Serialize(ms,o)
    ms.ToArray()

let ByteToHex bytes = 
    bytes 
    |> Array.map (fun (x : byte) -> System.String.Format("{0:X2}", x))
    |> String.concat System.String.Empty

let foo = 10
let la = (fun x y -> x + y + foo)
let lb = (fun x y -> x + y + foo)

let a = serialize la 
let b = serialize lb

printfn "%s" (ByteToHex a)
printfn "%s" (ByteToHex b)
printfn "lambda equals %b" (a = b)

这表明如果它们可以序列化,它们就可以进行比较。但是,检查此示例的字节流显示存在差异的两个字节。

是否有可能通过智能比较字节数组来解决这个问题的策略?

从等价的角度来看,函数没有进行有意义的序列化。

F# 中的 Curryable 函数实现为派生自 FSharpFunc.

let la = (fun x y -> x + y + foo)

将作为以下 class 的实例实现(在等效的 C# 中):

[Serializable] class Impl : FSharpFunc<int, int, int>
{
    public int foo;
    Impl(int foo_) => foo = foo_;

    public override int Invoke(int x, int y) =>
        x + y + _foo;
}

二进制序列化捕获的是完整的类型名和 foo 的值。 事实上,如果我们查看字节流中的字符串,我们会看到:

test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Program+la@28
foo

...其中 la@28 是我们派生的名称 class.

lalb 的字节流不同之处在于实现 class 的名称。 lalb 的实现可能完全不同。

例如,您可以将 lb 更改为 let lb = (fun x y -> x * y + foo),两次运行的结果将相同。


但是,您可以使用 Code Quotations:

let foo = 10
let la = <@ fun x y -> x + y + foo @>
let lb = <@ fun x y -> x + y + foo @>

printfn "Is same: %b" (la.ToString() = lb.ToString()) //true

F# 还支持 Expression<Func<>>(C# 的表达式树)——这也是比较的有效途径。