Suppose that I have some simple algebraic data (essentially enums) and another type which has these enums as fields.
data Color = Red | Green | Blue deriving (Eq, Show, Enum, Ord)
data Width = Thin | Normal | Fat deriving (Eq, Show, Enum, Ord)
data Height = Short | Medium | Tall deriving (Eq, Show, Enum, Ord)
data Object = Object { color :: Colour
, width :: Width
, height :: Height } deriving (Show)
Given a list of objects, I want to test that the attributes are all distinct. For this I have following functions (using sort
from Data.List
)
allDifferent = comparePairwise . sort
where comparePairwise xs = and $ zipWith (/=) xs (drop 1 xs)
uniqueAttributes :: [Object] -> Bool
uniqueAttributes objects = all [ allDifferent $ map color objects
, allDifferent $ map width objects
, allDifferent $ map height objects ]
This works, but is rather dissatisfying because I had to type each field (color, width, height) manually. In my actual code, there are more fields! Is there a way of 'mapping' the function
\field -> allDifferent $ map field objects
over the fields of an algebraic datatype like Object
? I want to treat Object
as a list of its fields (something that would be easy in e.g. javascript), but these fields have different types...
uniqueAttributes objects = and [go color, go width, go height] where go :: (Ord a) => (Object -> a) -> Bool; go f = allDifferent (map f objects)
– Mcmillan