2014-04-06 36 views
6

说我有一个像数据类型如下:写对多个属性排序比较优雅的方式

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

而且我想通过比较field1field2,并field3使它的Ord一个实例一个特定的顺序。

我觉得很讨厌写:

-- (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有这种事情一些非常不错的支持,我发现自己希望的是Ordering有类似的东西,例如

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

...或类似的东西。

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

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

谢谢!

+2

这里是一个无关的提示:'所有ID'和'和:: [Bool] - > Bool'一样 – cdk

回答

13

可以使用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 
+0

美丽。谢谢! – koschei