2014-04-06 10 views
6

dire che ho un tipo di dati come il seguente:modo elegante di scrivere confronto ordinato su più proprietà

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

E mi piacerebbe farlo un'istanza di Ord confrontando field1, field2 e field3 in un ordine particolare

trovo molto fastidioso per scrivere:

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

Monadi come Maybe e Either hanno qualche davvero bello supporto per questo genere di cose, e mi ritrovo a desiderare che Ordering avevano qualcosa di simile, per esempio

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

... o qualcosa del genere.

Ho dovuto eseguire questa operazione per tipi di dati complessi, in cui i campi di riordino nella definizione e in base alle definizioni predefinite per deriving (Eq, Ord) non erano possibili, quindi non sono interessato alle soluzioni che riproducono le dichiarazioni di istanza predefinite.

C'è un modo più elegante, o almeno più terso, per definire questo tipo di ordinamento?

Grazie!

+2

Ecco un suggerimento non correlato: 'all id' è uguale a' e :: [Bool] -> Bool' – cdk

risposta

13

È possibile utilizzare l'istanza Monoid per Ordering e funzioni con buoni risultati qui:

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

Un altro trucco è possibile utilizzare che generalizza più facilmente all'istanza Eq è quello di utilizzare le istanze per le tuple:

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

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

Beautiful. Grazie! – koschei