{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
module Polysemy.Internal.TH.Effect
( makeSem
, makeSem_
)
where
import Control.Monad (join, forM, unless)
import Data.Char (toLower)
import Data.List
import Generics.SYB
import Language.Haskell.TH
import Polysemy.Internal (send, Member, Sem)
import Polysemy.Internal.CustomErrors (DefiningModule)
makeSem :: Name -> Q [Dec]
makeSem = genFreer True
makeSem_ :: Name -> Q [Dec]
makeSem_ = genFreer False
genFreer :: Bool -> Name -> Q [Dec]
genFreer makeSigs tcName = do
isExtEnabled FlexibleContexts
>>= flip unless (fail "makeSem requires FlexibleContexts to be enabled")
hasTyFams <- isExtEnabled TypeFamilies
reify tcName >>= \case
TyConI (DataD _ _ _ _ cons _) -> do
sigs <- filter (const makeSigs) <$> mapM genSig cons
decs <- join <$> mapM genDecl cons
loc <- location
return $
[ TySynInstD ''DefiningModule
. TySynEqn [ConT tcName]
. LitT
. StrTyLit
$ loc_module loc
| hasTyFams
] ++ sigs ++ decs
_ -> fail "makeSem expects a type constructor"
getDeclName :: Name -> Name
getDeclName = mkName . overFirst toLower . nameBase
where
overFirst f (a : as) = f a : as
overFirst _ as = as
genDecl :: Con -> Q [Dec]
genDecl (ForallC _ _ con) = genDecl con
genDecl (GadtC [cName] tArgs _ ) = do
let fnName = getDeclName cName
let arity = length tArgs - 1
dTypeVars <- forM [0 .. arity] $ const $ newName "a"
pure $
[PragmaD (InlineP fnName Inlinable ConLike AllPhases)
, FunD fnName . pure $ Clause
(VarP <$> dTypeVars)
(NormalB . AppE (VarE 'send) $ foldl
(\b -> AppE b . VarE)
(ConE cName)
dTypeVars
)
[]
]
genDecl _ = fail "genDecl expects a GADT constructor"
tyVarBndrName :: TyVarBndr -> Name
tyVarBndrName (PlainTV n) = n
tyVarBndrName (KindedTV n _) = n
tyVarBndrKind :: TyVarBndr -> Maybe Type
tyVarBndrKind (PlainTV _) = Nothing
tyVarBndrKind (KindedTV _ k) = Just k
genType :: Con -> Q (Type, Maybe Name, Maybe Type)
genType (ForallC tyVarBindings conCtx con) = do
(t, mn, _) <- genType con
let k = do n <- mn
z <- find ((== n) . tyVarBndrName) tyVarBindings
tyVarBndrKind z
free = everything mappend freeVars t
pure ( ForallT (filter (flip elem free . tyVarBndrName) tyVarBindings) conCtx t
, mn
, k
)
genType (GadtC _ tArgs' (eff `AppT` m `AppT` tRet)) = do
r <- newName "r"
let
tArgs = fmap snd tArgs'
memberConstraint = ConT ''Member `AppT` eff `AppT` VarT r
resultType = ConT ''Sem `AppT` VarT r `AppT` tRet
replaceMType t | t == m = ConT ''Sem `AppT` VarT r
| otherwise = t
ts = everywhere (mkT replaceMType) tArgs
tn = case tRet of
VarT n -> Just n
_ -> Nothing
pure
. (, tn, Nothing)
. ForallT [PlainTV r] [memberConstraint]
. foldArrows
$ ts
++ [resultType]
genType _ = fail "genSig expects a GADT constructor"
simplifyBndrs :: Maybe Type -> Type -> Type
simplifyBndrs star = everywhere (mkT $ simplifyBndr star)
simplifyBndr :: Maybe Type -> TyVarBndr -> TyVarBndr
simplifyBndr (Just star) (KindedTV tv k) | star == k = PlainTV tv
simplifyBndr _ (KindedTV tv StarT) = PlainTV tv
simplifyBndr _ bndr = bndr
genSig :: Con -> Q Dec
genSig con = do
let
getConName (ForallC _ _ c) = getConName c
getConName (GadtC [n] _ _) = pure n
getConName c = fail $ "failed to get GADT name from " ++ show c
conName <- getConName con
(t, _, k) <- genType con
pure $ SigD (getDeclName conName) $ simplifyBndrs k t
foldArrows :: [Type] -> Type
foldArrows = foldr1 (AppT . AppT ArrowT)
freeVars :: Data a => a -> [Name]
freeVars = mkQ [] $ \case
VarT n -> [n]
_ -> []