I have types like
data Fruit = Apple Date
| Balana Date
| Orange Date
f = [Apple 2020-01-01 ,Apple 2020-02-01
,Balana 2020-01-01 ,Balana 2020-02-01]
getDate:: Fruit -> Date
getDate (Apple x) = x
getDate (Balana x) = x
getDate (Orange x) = x
when sorting the list f
by date
field.
instance Ord Fruit where
compare f1 f2 = compare (getDate f1) (getDate f2)
But ,the question is , how to setup rule to order Apple 2020-01-01
and Balance 2020-01-01
?
what if I want to sort the date first ,if the date
are same for different types of fruits, I would order Apple
first , then Balance
then Orange
?
[Apple 2020-01-01, Balance 2020-01-01, Apple 2020-02-01 ,Balana 2020-02-01]
Thanks !
CodePudding user response:
There are some different approaches you can use:
The most straightfoward one is not what you want but usefull to know
data Fruit = Apple Date
| Balana Date
| Orange Date
deriving (Eq, Ord)
This creates automatic order for you type, but in this case it will compare the fruit first and the Date
later... which is the opposite you want.
Therefore we need to manually define an Ord
instance.
-- This get the date from fruit
getDate :: Fruit -> Date
getDate (Apple x) = x
getDate (Balana x) = x
getDate (Orange x) = x
-- This function compares the fruit with no date.
fruitOrder :: Fruit -> Fruit -> Ordering
fruitOrder (Apple _) (Apple _) = EQ
fruitOrder (Apple _) _ = LT
fruitOrder (Orange _) (Orange _) = EQ
fruitOrder (Orange _) _ = GT
fruitOrder (Balana _) (Balana _) = EQ
fruitOrder (Balana _) (Apple _) = GT
fruitOrder (Balana _) (Orange _) = LT
-- Your ordering is a combination of both
instance Ord Fruit where
compare f g = comparing getDate f g <> fruitOrder f g
-- | |- This returns the first not EQ value or EQ if both are equal
-- |- comparing func x y == compare (func y) (func y)
A different modeling
Now, probably you are modelling your types in a non idiomatic way. If all your items will have a date, then you should encode your type as pairs as shown below
-- This is an alternative representation. You have Fruit types ordered as written
data FruitType = Apple | Banana | Orange deriving (Eq, Ord)
-- A fruit is a Date and a FruitType, this is Record syntax
-- you can think of it as C's structs or Python's dataclasses.
data Fruit = Fruit {getDate :: Date, getType :: FruitType} deriving (Eq, Ord)
-- The derived order is lexicographic, meaning they are ordered by the first field, and then by the second field
As an example, this program orders your list as expected using the suggested types
import Data.List
type Date = String -- For the sake of example
-- This is an alternative representation. You have Fruit types ordered as written
data FruitType = Apple | Banana | Orange deriving (Show, Eq, Ord)
-- A fruit is a Date and a FruitType, this is Record syntax
-- you can think of it as C's structs of Python's dataclasses.
data Fruit = Fruit {getDate :: Date, getType :: FruitType} deriving (Show, Eq, Ord)
f = [ Fruit "2020-01-01" Apple
, Fruit "2020-02-01" Apple
, Fruit "2020-01-01" Banana
, Fruit "2020-02-01" Banana]
main = print $ sort f