{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
module Data.OpenADT.TH
( mkVarPattern
)
where
import Control.Monad ( replicateM )
import Data.Functor.Foldable ( Fix(..) )
import Data.List ( foldl'
, init
)
import Language.Haskell.TH
import Data.Row ( Label(..) )
import Data.Row.Variants ( pattern IsJust
, view
)
import Data.OpenADT.VarF ( OpenAlg
, VarF(..)
)
mkVarPattern :: Name
-> String
-> String
-> String
-> Q [Dec]
mkVarPattern tyName rowLabel pName pfName = do
let patName = mkName pName
let patFName = mkName pfName
let rowLabelT = return $ LitT (StrTyLit rowLabel)
TyConI dec <- reify tyName
let (conBndrs, conArgTs, conName) = case dec of
DataD _ _ tvs _ [NormalC n argTs] _ ->
(tvs, fmap (return . snd) argTs, n)
NewtypeD _ _ tvs _ (NormalC n argTs) _ ->
(tvs, fmap (return . snd) argTs, n)
_ -> error "Expected newtype or data declaration with one constructor."
args <- replicateM (length conArgTs) (newName "a")
let conTvs = fmap bndrToVar conBndrs
let appliedTyCon = return $ foldl' AppT (ConT tyName) (init conTvs)
let argsP = fmap VarP args
let appliedConExp = return $ foldl' AppE (ConE conName) (fmap VarE args)
let appliedPatF = return $ ConP patFName (fmap VarP args)
let appliedConPat = return $ ConP conName (fmap VarP args)
r <- newName "r"
let tvV = return $ bndrToVar (last conBndrs)
let tvR = varT r
let adtR = [t| Fix (VarF $tvR) |]
let patBndrsF = PlainTV r : conBndrs
let patBndrs = PlainTV r : conBndrs
let patTypeCtxF = [t| ( OpenAlg $tvR $rowLabelT $appliedTyCon $tvV ) |]
let patTypeCtx = [t| ( OpenAlg $tvR $rowLabelT $appliedTyCon $adtR
, $tvV ~ $adtR ) |]
let patRetTypeF = [t| VarF $tvR $tvV |]
let patTypeTypeF = foldr funApp patRetTypeF conArgTs
let patTypeType = foldr (\x a -> do
x' <- x
v' <- tvV
if x' == v' then funApp adtR a else funApp x a
) adtR conArgTs
patTypeF <- forallT patBndrsF ((: []) <$> patTypeCtxF) patTypeTypeF
patType <- forallT patBndrs ((: []) <$> patTypeCtx) patTypeType
patBody <-
[p| VarF (view (Label :: Label $rowLabelT) -> Just $appliedConPat) |]
patClause <- [| VarF (IsJust (Label :: Label $rowLabelT) $appliedConExp) |]
fixedPatF <- [p| Fix $appliedPatF |]
return
[ PatSynSigD patFName patTypeF
, PatSynD patFName
(PrefixPatSyn args)
(ExplBidir [Clause argsP (NormalB patClause) []])
patBody
, PatSynSigD patName patType
, PatSynD patName (PrefixPatSyn args) ImplBidir fixedPatF
]
bndrName :: TyVarBndr -> Name
bndrName (PlainTV n ) = n
bndrName (KindedTV n _) = n
bndrToVar :: TyVarBndr -> Type
bndrToVar = VarT . bndrName
funApp :: Q Type -> Q Type -> Q Type
funApp a b = appT (appT arrowT a) b