{-# LANGUAGE TemplateHaskellQuotes #-} -- | Generate 'ZipMatch' instances via @TemplateHaskell@ module AST.TH.ZipMatch ( makeZipMatch ) where import AST.Class.ZipMatch (ZipMatch(..)) import AST.TH.Internal.Utils import Control.Lens.Operators import Data.Functor.Product.PolyKinds (Product(..)) import Language.Haskell.TH import qualified Language.Haskell.TH.Datatype as D import Prelude.Compat -- | Generate a 'ZipMatch' instance makeZipMatch :: Name -> DecsQ makeZipMatch typeName = do info <- D.reifyDatatype typeName (dst, var) <- parts info let ctrs = D.datatypeCons info <&> makeZipMatchCtr var instanceD (simplifyContext (ctrs >>= ccContext)) (appT (conT ''ZipMatch) (pure dst)) [ InlineP 'zipMatch Inline FunLike AllPhases & PragmaD & pure , funD 'zipMatch ( (ctrs <&> pure . ccClause) ++ [pure tailClause] ) ] <&> (:[]) where tailClause = Clause [WildP, WildP] (NormalB (ConE 'Nothing)) [] data CtrCase = CtrCase { ccClause :: Clause , ccContext :: [Pred] } makeZipMatchCtr :: Name -> D.ConstructorInfo -> CtrCase makeZipMatchCtr var info = CtrCase { ccClause = Clause [con fst, con snd] body [] , ccContext = fieldParts >>= zmfContext } where con f = ConP (D.constructorName info) (cVars <&> f <&> VarP) cVars = [0::Int ..] <&> show <&> (\n -> (mkName ('x':n), mkName ('y':n))) & take (length (D.constructorFields info)) body | null checks = NormalB bodyExp | otherwise = GuardedB [(NormalG (foldl1 mkAnd checks), bodyExp)] checks = fieldParts >>= zmfConds mkAnd x y = InfixE (Just x) (VarE '(&&)) (Just y) fieldParts = zipWith field cVars (D.constructorFields info <&> matchType var) bodyExp = applicativeStyle (ConE (D.constructorName info)) (fieldParts <&> zmfResult) field (x, y) NodeFofX{} = ZipMatchField { zmfResult = ConE 'Just `AppE` (ConE 'Pair `AppE` VarE x `AppE` VarE y) , zmfConds = [] , zmfContext = [] } field (x, y) (XofF t) = ZipMatchField { zmfResult = VarE 'zipMatch `AppE` VarE x `AppE` VarE y , zmfConds = [] , zmfContext = [ConT ''ZipMatch `AppT` t] } field _ Tof{} = error "TODO" field (x, y) (Other t) = ZipMatchField { zmfResult = ConE 'Just `AppE` VarE x , zmfConds = [InfixE (Just (VarE x)) (VarE '(==)) (Just (VarE y))] , zmfContext = [ConT ''Eq `AppT` t] } data ZipMatchField = ZipMatchField { zmfResult :: Exp , zmfConds :: [Exp] , zmfContext :: [Pred] }