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?
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.