假设我的数据类型如下:

data Foo = Foo { field1, field2, field3 :: Int }

我想通过按特定顺序比较Ordfield1field2使其成为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

诸如MaybeEither之类的Monad对这种事情有一些非常好的支持,我发现自己希望Ordering具有类似的东西,例如
instance Ord Foo where
    compare == comparing field1 >>= comparing field2 >>= comparing field3

...或类似的东西。

我需要对复杂的数据类型执行此操作,在这些数据类型中,无法对定义中的字段进行重新排序,并且无法根据deriving (Eq, Ord)的默认定义进行排序,因此,我对使用默认实例声明的解决方案不感兴趣。

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

谢谢!

最佳答案

您可以将 Monoid 实例用于 Ordering 和函数,以在此处获得良好效果:

instance Ord Foo where
    compare = comparing field1 <> comparing field2 <> comparing field3

您可以使用的另一个更容易推广到 Eq 实例的技巧是将实例用于元组:
equating = on (==)
reorder v = (field1 v, field2 v, field3 v)

instance Eq  Foo where (==)    = equating  reorder
instance Ord Foo where compare = comparing reorder

10-08 00:39