在具有元组的已区分联合上重载相等F#运算符会产生意外结果

Dul*_*lan 3 .net f# functional-programming equality operator-overloading

好的,为了使复杂的标题更清楚:我有一个单例联合,它是一个通用元组。该类型还会使相等运算符过载,以进行类似于Edge (1, 2)等价的操作Edge (2, 1)

type Edge<'a> = Edge of 'a * 'a
    with
        static member (=) (e1: Edge<_>, e2: Edge<_>) =
            match e1, e2 with
            | Edge(a,b), Edge(c,d) ->
                (a = c && b = d) || (a = d && b = c)
Run Code Online (Sandbox Code Playgroud)

但是,当我制作两个应该相等的Edge类型的值并进行比较时,它返回false。

> let e1 = Edge (1,2);;

val e1 : Edge<int> = Edge (1,2)

> let e2 = Edge (2,1);;

val e2 : Edge<int> = Edge (2,1)

> e1 = e2;;

val it : bool = false
Run Code Online (Sandbox Code Playgroud)

我对这里实际发生的事情一无所知。关于等于(=)运算符,有什么特别的要比其他运算符更复杂的覆盖?

Joh*_*mer 5

因此,错误消息中有一个提示(您未发布)

警告FS0086:不应将名称'(=)'用作成员名称。要为类型定义相等语义,请覆盖'Object.Equals'成员。如果定义静态成员以供其他CLI语言使用,请改用名称“ op_Equality”。

您实际上可以通过printf在原始代码中添加调用来进行检查,您会发现它实际上从未被调用过。

因此,您可以尝试以下操作:

type Edge<'a> = Edge of 'a * 'a
     with
         override x.Equals (y: obj ) =
             match  y with
             | :? Edge<'a> as e ->
                 match x,e with
                 |Edge(a,b),Edge(c,d)->(a = c && b = d) || (a = d && b = c)
             | _ -> false
Run Code Online (Sandbox Code Playgroud)

这也失败了。然后,编译器错误消息将带您浏览更多错误消息,直到您了解

[<CustomEquality;NoComparison>]
type Edge<'a when 'a:equality> = Edge of 'a * 'a
    with
        override x.Equals (y: obj ) =
            match  y with
            | :? Edge<'a> as e ->
                match x,e with
                |Edge(a,b),Edge(c,d)->(a = c && b = d) || (a = d && b = c)
            | _ -> false
Run Code Online (Sandbox Code Playgroud)

效果很好。equals函数比许多其他运算符复杂一点,因此重写它需要付出更多努力。