-- | -- Module : Data.OpenADT.TH -- Copyright : Copyright (c) Jordan Woehr, 2018 -- License : BSD -- Maintainer : Jordan Woehr -- Stability : experimental -- -- This module exports template haskell functions for generating tedious -- boilerplate. {-# 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(..) ) -- | Create patterns for a variant constructor. -- -- For example, for the type FooF with the constructor FooF': -- -- > data FooF a x = FooF' a x -- > $(mkVarPattern ''FooF "foo" "Foo" "FooF") -- -- A pattern similar to the following will be generated: -- -- > pattern FooF :: (OpenAlg r "foo" (FooF a) v) => a -> v -> VarF r v -- > pattern FooF a v <- VarF (view (Label :: Label "foo") -> Just (FooF' a v)) -- > -- > pattern Foo :: (OpenAlg r "foo" (FooF a) (OpenADT r)) -- > => a -> OpenADT r -> OpenADT r -- > where FooF a v = VarF (IsJust (Label :: Label "foo") (FooF' a v)) -- > pattern Foo a v = Fix (FooF a v) mkVarPattern :: Name -- ^ The 'Name' of the type to create patterns for. -> String -- ^ The label in the variant the constructor will have. -> String -- ^ The name of the fixed pattern. -> String -- ^ The name of the unfixed pattern. -> 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 -- Init should not fail because the types should be functors, thus -- always have > 0 variables 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" -- row type variable let tvV = return $ bndrToVar (last conBndrs) -- variant type variable 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 -- a -> b -> (a -> b) funApp :: Q Type -> Q Type -> Q Type funApp a b = appT (appT arrowT a) b