仅在底层类型时才实现相等性的F#容器

时间:2014-03-10 14:21:45

标签: f#

编辑:从目前为止添加的答案和评论中可以看出,我没有正确解释我想要的内容。这是一个例子:

// type not supporting any type of comparison
[<NoEquality>]
[<NoComparison>]
type blah () =
    member x.huha = 0

// make a map, turns out to work whether x supports equality or not
let inline tt x =
    Map.ofList [1, x]       

let test () =
    // maps can be compared for equality if the argument can
    if (tt 1 = tt 2) then failwithf "strange"

    // maps can be made no matter if the argument supports equality
    if (tt (blah ())).Count <> 1 then failwithf "size"

    // this does not compile
    if tt (blah ()) = tt (blah ()) then ....

简而言之,我希望我自己的类型就像上面的地图一样。因此它应该在类型参数执行时支持相等,而不应该在类型参数不支持时执行。我也希望typechecker在不支持时阻止我使用相等,因为它显然可以为内置类型做到这一点。再次感谢。

原始问题:当且仅当某些基础类型具有时,各种内置F#类型才支持相等性。例如,Map<'k, 'd>将支持iff 'd所做的相等(并且在编译时检测到这一点)。是否可以在用户代码中实现此行为?这是一个失败的尝试,如果相等是无条件的,则编译好的版本。非常感谢。

[<NoComparison>]
type test_fails<[<EqualityConditionalOn>]'a> (content:'a) =

    let eq_impl (x:test_fails<'a>) (y:obj) =
        let y = y :?> test_fails<'a>
        x.content = y.content

    member x.content = content

    override x.Equals (y:obj) =
        eq_impl x y

[<NoComparison>]
type test_compiles<'a when 'a : equality> (content:'a) =

    let eq_impl (x:test_compiles<'a>) (y:obj) =
        let y = y :?> test_compiles<'a>
        x.content = y.content

    member x.content = content

    override x.Equals (y:obj) =
        eq_impl x y

2 个答案:

答案 0 :(得分:6)

您已经拥有解决方案的一部分:在通用参数上使用[<EqualityConditionalOn>]

您缺少的部分:您需要在地图实现中使用Unchecked.equals而不是普通的=运算符(在您检查两个'a值相等的任何位置)。 Unchecked.equals在运行时检查该类型是否支持泛型相等。如果是,则像往常一样比较两个实例/值的相等性;如果没有,它将回退到结构相等性检查或Object.Equals(obj)方法的类型实现。

答案 1 :(得分:2)

正如丹尼尔评论的那样,您的问题是eq_impl=x.content上使用y.content,这意味着他们必须支持平等。也许您想要使用Object.ReferenceEquals代替?这取决于你正在尝试做什么。