{-# LANGUAGE ScopedTypeVariables #-}
module Data.Singletons.Deriving.Show (
mkShowInstance
, mkShowSingContext
) where
import Language.Haskell.TH.Syntax hiding (showName)
import Language.Haskell.TH.Desugar
import Data.Singletons.Names
import Data.Singletons.Util
import Data.Singletons.Syntax
import Data.Singletons.Deriving.Infer
import Data.Singletons.Deriving.Util
import Data.Maybe (fromMaybe)
import GHC.Lexeme (startsConSym, startsVarSym)
import GHC.Show (appPrec, appPrec1)
mkShowInstance :: DsMonad q => DerivDesc q
mkShowInstance mb_ctxt ty (DataDecl _ _ cons) = do
clauses <- mk_showsPrec cons
constraints <- inferConstraintsDef mb_ctxt (DConPr showName) ty cons
return $ InstDecl { id_cxt = constraints
, id_name = showName
, id_arg_tys = [ty]
, id_sigs = mempty
, id_meths = [ (showsPrecName, UFunction clauses) ] }
mk_showsPrec :: DsMonad q => [DCon] -> q [DClause]
mk_showsPrec cons = do
p <- newUniqueName "p"
if null cons
then do v <- newUniqueName "v"
pure [DClause [DWildPa, DVarPa v] (DCaseE (DVarE v) [])]
else mapM (mk_showsPrec_clause p) cons
mk_showsPrec_clause :: forall q. DsMonad q
=> Name -> DCon
-> q DClause
mk_showsPrec_clause p (DCon _ _ con_name con_fields _) = go con_fields
where
go :: DConFields -> q DClause
go (DNormalC _ []) = return $
DClause [DWildPa, DConPa con_name []] $
DVarE showStringName `DAppE` dStringE (parenInfixConName con_name "")
go (DNormalC True [_, _]) = do
argL <- newUniqueName "argL"
argR <- newUniqueName "argR"
fi <- fromMaybe defaultFixity <$> reifyFixityWithLocals con_name
let con_prec = case fi of Fixity prec _ -> prec
op_name = nameBase con_name
infixOpE = DAppE (DVarE showStringName) . dStringE $
if isInfixDataCon op_name
then " " ++ op_name ++ " "
else " `" ++ op_name ++ "` "
return $ DClause [DVarPa p, DConPa con_name [DVarPa argL, DVarPa argR]] $
(DVarE showParenName `DAppE` (DVarE gtName `DAppE` DVarE p
`DAppE` dIntegerE con_prec))
`DAppE` (DVarE composeName
`DAppE` showsPrecE (con_prec + 1) argL
`DAppE` (DVarE composeName
`DAppE` infixOpE
`DAppE` showsPrecE (con_prec + 1) argR))
go (DNormalC _ tys) = do
args <- mapM (const $ newUniqueName "arg") tys
let show_args = map (showsPrecE appPrec1) args
composed_args = foldr1 (\v q -> DVarE composeName
`DAppE` v
`DAppE` (DVarE composeName
`DAppE` DVarE showSpaceName
`DAppE` q)) show_args
named_args = DVarE composeName
`DAppE` (DVarE showStringName
`DAppE` dStringE (parenInfixConName con_name " "))
`DAppE` composed_args
return $ DClause [DVarPa p, DConPa con_name $ map DVarPa args] $
DVarE showParenName
`DAppE` (DVarE gtName `DAppE` DVarE p `DAppE` dIntegerE appPrec)
`DAppE` named_args
go (DRecC []) = go (DNormalC False [])
go (DRecC tys) = do
args <- mapM (const $ newUniqueName "arg") tys
let show_args =
concatMap (\((arg_name, _, _), arg) ->
let arg_nameBase = nameBase arg_name
infix_rec = showParen (isSym arg_nameBase)
(showString arg_nameBase) ""
in [ DVarE showStringName `DAppE` dStringE (infix_rec ++ " = ")
, showsPrecE 0 arg
, DVarE showCommaSpaceName
])
(zip tys args)
brace_comma_args = (DVarE showCharName `DAppE` dCharE '{')
: take (length show_args - 1) show_args
composed_args = foldr (\x y -> DVarE composeName `DAppE` x `DAppE` y)
(DVarE showCharName `DAppE` dCharE '}')
brace_comma_args
named_args = DVarE composeName
`DAppE` (DVarE showStringName
`DAppE` dStringE (parenInfixConName con_name " "))
`DAppE` composed_args
return $ DClause [DVarPa p, DConPa con_name $ map DVarPa args] $
DVarE showParenName
`DAppE` (DVarE gtName `DAppE` DVarE p `DAppE` dIntegerE appPrec)
`DAppE` named_args
parenInfixConName :: Name -> ShowS
parenInfixConName conName =
let conNameBase = nameBase conName
in showParen (isInfixDataCon conNameBase) $ showString conNameBase
showsPrecE :: Int -> Name -> DExp
showsPrecE prec n = DVarE showsPrecName `DAppE` dIntegerE prec `DAppE` DVarE n
dCharE :: Char -> DExp
dCharE c = DLitE $ StringL [c]
dStringE :: String -> DExp
dStringE = DLitE . StringL
dIntegerE :: Int -> DExp
dIntegerE = DLitE . IntegerL . fromIntegral
isSym :: String -> Bool
isSym "" = False
isSym (c : _) = startsVarSym c || startsConSym c
mkShowSingContext :: DCxt -> DCxt
mkShowSingContext = map show_to_SingShow
where
show_to_SingShow :: DPred -> DPred
show_to_SingShow = modifyConNameDPred $ \n ->
if n == showName
then showSingName
else n