Template Haskell (TH) でデータ定義

列挙型とその文字表現を、データで定義できる TH を書いた。

-- モジュールA
defDataConstsType "T_Digit" (map (\x -> "T_" ++ [intToDigit x]) [0..10])

defShowType "T_Digit" (map (\x -> 
                             ("T_" ++ [intToDigit x],
                              "'" ++ [intToDigit x] ++ "'")) [0..10])

このように書くと、下と同じ意味になる(はず)。

data T_Digit = T_0 | T_1 ...
instance Show T_Digit where
  show T_0 = "'0'"
  show T_1 = "'1'"
  ...

TH のコード本体は利用する側と別モジュールに書かなくてはいけない(制限)。

-- モジュールB
defDataConstsType :: String -> [String] -> Q [Dec]
defDataConstsType name ns = return d
  where
    d = [DataD [] (mkName name) []
         (map (\n -> NormalC (mkName n) []) ns)
         [mkName "Eq", mkName "Enum"]]

defShowType :: String -> [(String, String)] -> Q [Dec]
defShowType name ht = return d
  where
    d = [InstanceD []
           (AppT (ConT (mkName "Show")) (ConT (mkName name))) 
           [FunD (mkName "show")
            [Clause [VarP x] 
             (NormalB (CaseE (VarE x) 
                       --
                       (map (\(n,s) ->
                              Match (ConP (mkName n) []) (NormalB (LitE (StringL s))) []) ht))) []]]]
      where x = mkName "x"