{-# LANGUAGE CPP, TemplateHaskell #-} module Data.GADT.Show.TH ( DeriveGShow(..) ) where import Control.Applicative import Control.Monad import Control.Monad.Writer import Data.GADT.TH.Internal import Data.Functor.Identity import Data.GADT.Show import Data.Traversable (for) import Data.List import Data.Set (Set) import qualified Data.Set as Set import Language.Haskell.TH import Language.Haskell.TH.Datatype class DeriveGShow t where deriveGShow :: t -> Q [Dec] instance DeriveGShow Name where deriveGShow typeName = do typeInfo <- reifyDatatype typeName let instTypes = datatypeInstTypes typeInfo paramVars = Set.unions [freeTypeVariables t | t <- instTypes] instTypes' = case reverse instTypes of [] -> fail "deriveGEq: Not enough type parameters" (_:xs) -> reverse xs instanceHead = AppT (ConT ''GShow) (foldl AppT (ConT typeName) instTypes') (clauses, cxt) <- runWriterT (mapM (gshowClause typeName paramVars) (datatypeCons typeInfo)) return [InstanceD Nothing (datatypeContext typeInfo ++ cxt) instanceHead [gshowFunction clauses]] instance DeriveGShow Dec where deriveGShow = deriveForDec ''GShow $ \typeInfo -> do let instTypes = datatypeInstTypes typeInfo paramVars = Set.unions [freeTypeVariables t | t <- instTypes] clauses <- mapM (gshowClause (datatypeName typeInfo) paramVars) (datatypeCons typeInfo) return $ gshowFunction clauses instance DeriveGShow t => DeriveGShow [t] where deriveGShow [it] = deriveGShow it deriveGShow _ = fail "deriveGShow: [] instance only applies to single-element lists" instance DeriveGShow t => DeriveGShow (Q t) where deriveGShow = (>>= deriveGShow) gshowFunction :: [Clause] -> Dec gshowFunction clauses = FunD 'gshowsPrec clauses isApplicationOf :: Type -> Type -> Bool isApplicationOf t t' = t == t' || case t' of AppT u _ -> isApplicationOf t u _ -> False gshowClause :: Name -> Set Name -> ConstructorInfo -> WriterT [Type] Q Clause gshowClause typeName paramVars con = do let conName = constructorName con argTypes = constructorFields con conTyVars = Set.fromList (map tvName (constructorVars con)) precName <- lift $ newName "prec" argNames <- forM argTypes $ \_ -> lift $ newName "x" argShowExprs <- forM (zip argNames argTypes) $ \(n,t) -> do let useShow = do u <- lift $ reifyInstancesWithRigids paramVars ''Show [t] case u of (_:_) -> return () _ -> tell [AppT (ConT ''Show) t] return [| showsPrec 11 $(varE n) |] case t of AppT tyFun tyArg -> do if isApplicationOf (ConT typeName) tyFun then return [| gshowsPrec 11 $(varE n) |] else useShow _ -> useShow let precPat = if null argNames then wildP else varP precName lift $ clause [precPat, conP conName (map varP argNames)] (normalB (gshowBody (varE precName) conName argShowExprs)) [] showsName name = [| showString $(litE . stringL $ nameBase name) |] gshowBody :: Q Exp -> Name -> [Q Exp] -> Q Exp gshowBody prec conName [] = showsName conName gshowBody prec conName argShowExprs = let body = foldr (\e es -> [| $e . $es |]) [| id |] . intersperse [| showChar ' ' |] $ showsName conName : argShowExprs in [| showParen ($prec > 10) $body |]