{-# LANGUAGE CPP #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
{-# LANGUAGE Trustworthy #-}
module Grisette.Internal.TH.Ctor.SmartConstructor
( makeSmartCtorWith,
makePrefixedSmartCtor,
makeNamedSmartCtor,
makeSmartCtor,
)
where
import Control.Monad (join, replicateM, when, zipWithM)
import Data.Bifunctor (Bifunctor (second))
import Grisette.Internal.Core.Data.Class.Mergeable (Mergeable)
import Grisette.Internal.Core.Data.Class.TryMerge (TryMerge, mrgSingle)
import Grisette.Internal.TH.Ctor.Common
( decapitalizeTransformer,
prefixTransformer,
withNameTransformer,
)
import Grisette.Internal.TH.Util (constructorInfoToType, putHaddock)
import Language.Haskell.TH
( Body (NormalB),
Clause (Clause),
Dec (FunD, SigD),
Exp (AppE, ConE, LamE, VarE),
Name,
Pat (VarP),
Pred,
Q,
Type (AppT, ArrowT, ForallT, VarT),
mkName,
newName,
)
import Language.Haskell.TH.Datatype
( ConstructorInfo
( constructorFields,
constructorName
),
DatatypeInfo (datatypeCons),
reifyDatatype,
)
import Language.Haskell.TH.Datatype.TyVarBndr
( Specificity (SpecifiedSpec),
TyVarBndrSpec,
plainTVFlag,
)
makeSmartCtorWith :: (String -> String) -> Name -> Q [Dec]
makeSmartCtorWith :: (String -> String) -> Name -> Q [Dec]
makeSmartCtorWith = ([String] -> Name -> Q [Dec])
-> (String -> String) -> Name -> Q [Dec]
withNameTransformer [String] -> Name -> Q [Dec]
makeNamedSmartCtor
makePrefixedSmartCtor ::
String ->
Name ->
Q [Dec]
makePrefixedSmartCtor :: String -> Name -> Q [Dec]
makePrefixedSmartCtor = (String -> String) -> Name -> Q [Dec]
makeSmartCtorWith ((String -> String) -> Name -> Q [Dec])
-> (String -> String -> String) -> String -> Name -> Q [Dec]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String -> String
prefixTransformer
makeSmartCtor ::
Name ->
Q [Dec]
makeSmartCtor :: Name -> Q [Dec]
makeSmartCtor = (String -> String) -> Name -> Q [Dec]
makeSmartCtorWith String -> String
decapitalizeTransformer
makeNamedSmartCtor ::
[String] ->
Name ->
Q [Dec]
makeNamedSmartCtor :: [String] -> Name -> Q [Dec]
makeNamedSmartCtor [String]
names Name
typName = do
d <- Name -> Q DatatypeInfo
reifyDatatype Name
typName
let constructors = DatatypeInfo -> [ConstructorInfo]
datatypeCons DatatypeInfo
d
when (length names /= length constructors) $
fail "Number of names does not match the number of constructors"
ds <- zipWithM (mkSingleWrapper d) names constructors
return $ join ds
augmentNormalCExpr :: Int -> Exp -> Q Exp
augmentNormalCExpr :: Int -> Exp -> Q Exp
augmentNormalCExpr Int
n Exp
f = do
xs <- Int -> Q Name -> Q [Name]
forall (m :: * -> *) a. Applicative m => Int -> m a -> m [a]
replicateM Int
n (String -> Q Name
forall (m :: * -> *). Quote m => String -> m Name
newName String
"x")
let args = (Name -> Pat) -> [Name] -> [Pat]
forall a b. (a -> b) -> [a] -> [b]
map Name -> Pat
VarP [Name]
xs
mrgSingleFun <- [|mrgSingle|]
return $
LamE
args
( AppE mrgSingleFun $
foldl AppE f (map VarE xs)
)
augmentFinalType :: Type -> Q (([TyVarBndrSpec], [Pred]), Type)
augmentFinalType :: Type -> Q (([TyVarBndrSpec], [Type]), Type)
augmentFinalType (AppT a :: Type
a@(AppT Type
ArrowT Type
_) Type
t) = do
tl <- Type -> Q (([TyVarBndrSpec], [Type]), Type)
augmentFinalType Type
t
return $ second (AppT a) tl
augmentFinalType Type
t = do
mName <- String -> Q Name
forall (m :: * -> *). Quote m => String -> m Name
newName String
"m"
let mTy = Name -> Type
VarT Name
mName
mergeable <- [t|Mergeable|]
applicative <- [t|Applicative|]
tryMerge <- [t|TryMerge|]
return
( ( [plainTVFlag mName SpecifiedSpec],
[AppT mergeable t, AppT applicative mTy, AppT tryMerge mTy]
),
AppT mTy t
)
augmentConstructorType :: Type -> Q Type
augmentConstructorType :: Type -> Q Type
augmentConstructorType (ForallT [TyVarBndrSpec]
tybinders [Type]
ctx Type
ty1) = do
((bndrs, preds), augmentedTyp) <- Type -> Q (([TyVarBndrSpec], [Type]), Type)
augmentFinalType Type
ty1
return $ ForallT (tybinders ++ bndrs) (preds ++ ctx) augmentedTyp
augmentConstructorType Type
t = do
((bndrs, preds), augmentedTyp) <- Type -> Q (([TyVarBndrSpec], [Type]), Type)
augmentFinalType Type
t
return $ ForallT bndrs preds augmentedTyp
mkSingleWrapper :: DatatypeInfo -> String -> ConstructorInfo -> Q [Dec]
mkSingleWrapper :: DatatypeInfo -> String -> ConstructorInfo -> Q [Dec]
mkSingleWrapper DatatypeInfo
dataType String
name ConstructorInfo
info = do
constructorTyp <- DatatypeInfo -> ConstructorInfo -> Q Type
constructorInfoToType DatatypeInfo
dataType ConstructorInfo
info
augmentedTyp <- augmentConstructorType constructorTyp
let oriName = ConstructorInfo -> Name
constructorName ConstructorInfo
info
let retName = String -> Name
mkName String
name
expr <- augmentNormalCExpr (length $ constructorFields info) (ConE oriName)
putHaddock retName $
"Smart constructor for v'"
<> show oriName
<> "' to construct values wrapped and possibly merged in a container."
return
[ SigD retName augmentedTyp,
FunD retName [Clause [] (NormalB expr) []]
]