1

Does anyone know why this code:

module Language.P4.UtilTest where

import Language.P4.Util (mkShow)

data Dummy = Bogus    Char
           | Nonsense Int

$(mkShow ''Dummy)

is producing this error:

Davids-Air-2:P4 dbanas$ stack ghc -- UtilTest.hs -ddump-splices
[1 of 1] Compiling Language.P4.UtilTest ( UtilTest.hs, UtilTest.o )
UtilTest.hs:24:3-16: Splicing declarations
    mkShow ''Dummy
  ======>
    instance Show Dummy where
      show (Bogus x) = show x
      show (Nonsense x) = show x

UtilTest.hs:24:3: error:
    Conflicting definitions for ‘show’
    Bound at: UtilTest.hs:24:3-16
              UtilTest.hs:24:3-16
   |
24 | $(mkShow ''Dummy)
   |   ^^^^^^^^^^^^^^

?

The TH splice expansion looks correct to me. If I comment out the second constructor (Nonsense Int), the code compiles without error. Also, if I enter the TH splice expansion shown, manually, into the code (commenting out the $(mkShow ''Dummy) line, of course), it compiles without error.

mkShow :: Name -> Q [Dec]
mkShow typName = do
  t@(TyConI (DataD _ _ _ _ constructors _)) <- reify typName  -- Get all the information on the type.
  let func_name = mkName "show"
  let var_name  = mkName "x"
  let func_decs = map ( \c@(NormalC nm _) -> FunD func_name
                                                  [ Clause [ConP (simpleName nm) [VarP var_name]]
                                                           (NormalB (AppE (VarE func_name) (VarE var_name)))
                                                           []
                                                  ]
                      )
                      constructors
  return [InstanceD Nothing [] (AppT (ConT (mkName "Show")) (ConT (simpleName typName))) func_decs]

simpleName :: Name -> Name
simpleName nm =
   let s = nameBase nm
   in case dropWhile (/=':') s of
        []          -> mkName s
        _:[]        -> mkName s
        _:t         -> mkName t
dbanas
  • 1,707
  • 14
  • 24
  • 2
    `map (\c -> FunD func_name ..) ..` produces multiple different functions which happen to have the same name (move `FunD` out of `map`). When pretty printed, this produces code which looks syntactically valid. However, you must remember that GHC is processing the TH AST *directly*, as opposed to converting it to a string and then once again parsing that code. The pretty printed representation of the TH AST should not be taken as representing the semantics of the actual generated code (it may be correct as printed, as it is here, or it may not even be syntactically well-formed in some cases). – user2407038 Aug 20 '17 at 02:30
  • Yep, that was it; thanks! Copying/pasting your comment, as the answer. – dbanas Aug 20 '17 at 11:28

1 Answers1

1

Comment from @user2407038, above, gives the answer. Here is the corrected code for mkShow():

mkShow :: Name -> Q [Dec]
mkShow typName = do
  t@(TyConI (DataD _ _ _ _ constructors _)) <- reify typName  -- Get all the information on the type.
  let func_name = mkName "show"
  let var_name  = mkName "x"
  let clause_decs = map ( \c@(NormalC nm _) ->
                            Clause [ConP (simpleName nm) [VarP var_name]]
                                   (NormalB (AppE (VarE func_name) (VarE var_name)))
                                   []
                        )
                        constructors
  return [InstanceD Nothing [] (AppT (ConT (mkName "Show")) (ConT (simpleName typName))) [FunD func_name clause_decs]]
dbanas
  • 1,707
  • 14
  • 24