优雅的方式来编写多个属性的有序比较

kos*_*hei 7 monads comparison haskell functional-programming

假设我有以下数据类型:

data Foo = Foo { field1, field2, field3 :: Int }
Run Code Online (Sandbox Code Playgroud)

我想使它的一个实例Ord,通过比较field1,field2以及field3以特定的顺序.

我觉得写起来很烦人:

-- (we need Eq Foo to define Ord Foo)
instance Eq Foo where
    x == y = all id [ f x == f y
                    | f <- [field1, field2, field3] ]

instance Ord Foo where
    compare x y = case (comparing field1) x y of
        EQ -> case (comparing field2) x y of
            EQ -> (comparing field3) x y
            ord -> ord
        ord -> ord
Run Code Online (Sandbox Code Playgroud)

Monads喜欢Maybe并且Either对这种事情有一些非常好的支持,我发现自己希望Ordering有类似的东西,例如

instance Ord Foo where
    compare == comparing field1 >>= comparing field2 >>= comparing field3
Run Code Online (Sandbox Code Playgroud)

...或类似的东西.

我需要为复杂的数据类型执行此操作,其中定义中的字段重新排序并且根据默认定义deriving (Eq, Ord)是不可能的,因此我对游戏默认实例声明的解决方案不感兴趣.

是否有更优雅,或至少更简洁的方式来定义这种排序?

谢谢!

Dan*_*ner 17

你可以在这里使用Monoid实例Ordering和函数效果良好:

instance Ord Foo where
    compare = comparing field1 <> comparing field2 <> comparing field3
Run Code Online (Sandbox Code Playgroud)

您可以使用的另一个技巧更容易推广到Eq实例是使用元组的实例:

equating = on (==)
reorder v = (field1 v, field2 v, field3 v)

instance Eq  Foo where (==)    = equating  reorder
instance Ord Foo where compare = comparing reorder
Run Code Online (Sandbox Code Playgroud)