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

4

编辑:从迄今为止的答案和评论来看,我似乎没有正确解释我的要求。这里有一个例子:

// 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 ....

简而言之,我希望我的自定义类型能够像上面的 map 一样行为。因此,当类型参数支持时,它应该支持相等性,否则不应该支持相等性。我还希望类型检查器在不支持相等性时阻止我使用相等性,就像对内置类型明确可以做到的那样。再次感谢。
原问题:各种内置的 F# 类型仅在某些基础类型支持时才支持相等性。例如,Map<'k,'d> 当且仅当 '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
equality约束是必要的,因为您使用的是= - Daniel
我认为使用底层相等性是拥有[<EqualityConditionalOn>]的主要目的吧?显然,我不知道如何正确地做。 - Joe Huha
Jack P.的回答完全正确。看一下Collections.Map实现中是如何完成的(搜索this.Equals)。 - Søren Debois
2个回答

6

你已经有了部分解决方案:在泛型参数上使用 [<EqualityConditionalOn>]

你需要添加的部分:在你的映射实现中(任何地方都可以检查两个'a值的相等性),需要使用Unchecked.equals而不是普通的=运算符。 Unchecked.equals在运行时检查类型是否支持通用相等性。如果支持,则像往常一样比较两个实例/值是否相等;否则,它会回退到结构相等检查或类型实现的Object.Equals(obj)方法。


2

正如Daniel所说,你的问题在于eq_implx.contenty.content上使用了=,这意味着它们必须支持相等性。也许你想使用Object.ReferenceEquals?具体取决于你试图做什么。


我现在已经添加了一个示例来更好地解释我想要的内容。非常感谢。 - Joe Huha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接