haskelltypeclass

Can you not to export an instance in Haskell?


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:

  1. Derive the Read instance for Size
  2. Create another function readSize to parse from string, making use of read and readMaybe
  3. Hide the 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

Solution

  • In your case, I prefer to do this way:

    1. Deriving Enum and Show (not Read) instances

      data Size = Original | ...
          deriving (Enum, Show)
      
    2. Make parse function

      readSize :: String -> Maybe Size
      readSize str = lookup (toLower <$> str) size_map
        where
          size_map = [(toLower <$> show size, size) | size <- [Original ..]]