2014-04-06 7 views
6

Say mam typ danych jak następuje:elegancki sposób pisać uporządkowaną porównania na wielu właściwości

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

I chciałbym, aby to wystąpienie Ord porównując field1, field2 i field3 w konkretne zamówienie.

Uważam to za bardzo irytujące napisać:

-- (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 

Monady jak Maybe i Either jakieś naprawdę piękny poparcie dla tego rodzaju rzeczy, a ja znajduję się chce, że Ordering miał coś podobnego, np

... lub coś w tym stylu.

Musiałem to zrobić dla złożonych typów danych, w których ponowne zamówienie pól w definicji i w zależności od domyślnych definicji dla deriving (Eq, Ord) nie było możliwe, więc nie interesują mnie rozwiązania, które grają domyślne deklaracje instancji.

Czy istnieje bardziej elegancki, a przynajmniej bardziej zwięzły sposób definiowania tego rodzaju zamawiania?

Dzięki!

+2

Oto niepowiązana wskazówka: 'all id' jest takie samo jak' i :: [Bool] -> Bool' – cdk

Odpowiedz

13

Można użyć instancji Monoid dla Ordering i funkcje z dobrym skutkiem tutaj:

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

Kolejny trik można użyć że uogólnia łatwiej do instancji Eq jest użycie instancji za krotki:

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

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

Piękny. Dziękuję Ci! – koschei

Powiązane problemy