Yeah, I know this sounds unreasonable. Guess there are better approaches than simply hiding an instance.
I'm working on a library about images. Inside one module, there is an enum type for the sizes of the images:
module Sizes (Sizes(..)) where
data Size = Original
| Regular
| Small
| Thumb
| Mini
Now I need to parse sizes from strings, particularly in a case-insensitive way (e.g. "original"
, "Original"
, "ORIGINAL"
should all be parsed to Original
). I planned the following:
Read
instance for Size
readSize
to parse from string, making use of read
and readMaybe
Read
instantiation from users so they do not mistakenly use read
or whatsoever when, say, parsing "ORIGINAL"
Dirty enough. Anyway, here are the first two steps, and I have no idea how to conduct step 3.
data Size = ...
deriving (Read)
readSize :: String -> Maybe Size
readSize s
| (toLower <$> s) `elem` ["original", "regular", "small", "thumb", "mini"] = (read . onlyCapitaliseFirst) s
| otherwise = readMaybe s
where onlyCapitaliseFirst "" = ""
onlyCapitaliseFirst (c:xs) = toUpper c : (toLower <$> xs)
Why don't I just write the instance myself, you may ask?
Well, because it's too inelegant and tiresome to implement such readsPrec
,
which would be much better if the compiler just use some magic to complete it for me.
instance Read Size where
readsPrec _ = \case ('o' : 'r' : 'i' : 'g' : 'i' : 'n' : 'a' : 'l' : xs) -> [(Original, XS)]
... -- it's not even correct, for only considering all lowercase case
In your case, I prefer to do this way:
Deriving Enum and Show (not Read) instances
data Size = Original | ...
deriving (Enum, Show)
Make parse function
readSize :: String -> Maybe Size
readSize str = lookup (toLower <$> str) size_map
where
size_map = [(toLower <$> show size, size) | size <- [Original ..]]