Search code examples
haskelltemplate-haskellderiving

Custom deriving(Read,Show) for enum type


Let's say I have this enumeration type:

data TVShow = BobsBurgers | MrRobot | BatmanTAS

and I want to define instances for Read and Show with the following behavior:

show BobsBurgers = "Bob's Burgers"
show MrRobot = "Mr. Robot"
show BatmanTAS = "Batman: The Animated Series"

read "Bob's Burgers" = BobsBurgers
read "Mr. Robot" = MrRobot
read "Batman: The Animated Series" = BatmanTAS

There is lots of repetition in these definitions, and so I'd like to associate each type constructor with a string and then generate Show and Read automatically from those associations. Is such a thing possible?


Solution

  • The paper Invertible Syntax Descriptions: Unifying Parsing and Pretty Printing describes one particularly idiomatic solution. Your example looks like this, using the invertible-syntax package based on that paper:

    import Prelude hiding (Applicative(..), print)
    import Data.Maybe (fromJust)
    import Text.Syntax
    import Text.Syntax.Parser.Naive
    import Text.Syntax.Printer.Naive
    
    data TVShow = BobsBurgers | MrRobot | BatmanTAS deriving (Eq, Ord)
    
    tvShow :: Syntax f => f TVShow
    tvShow =  pure BobsBurgers <* text "Bob's Burgers"
          <|> pure MrRobot     <* text "Mr. Robot"
          <|> pure BatmanTAS   <* text "Batman: The Animated Series"
    
    runParser (Parser p) = p
    instance Read TVShow where readsPrec _ = runParser tvShow
    instance Show TVShow where show = fromJust . print tvShow
    

    This is designed to be extensible to types more exciting than simple enumerations, as well.