-- | -- -- GHC has a phase restriction which prevents code generated by Template Haskell -- being referred to by Template Haskell in the same file. Thus, when using this -- library, you will need to spread invocations out over several files. -- -- We will refer to the following example in the documentation: -- -- @ -- module Foo where -- data Arith = Add Atom Atom -- data Atom = Var String | Const Lit -- data Lit = Lit Int -- @ module Data.Comp.Trans ( deriveMultiComp , generateNameLists , makeSumType , getLabels , T.deriveTrans , U.deriveUntrans ) where import Control.Monad ( liftM, mapM ) import Data.Comp.Multi ( (:+:) ) import Data.Data ( Data ) import Language.Haskell.TH.Quote ( dataToExpQ ) import Language.Haskell.TH import qualified Data.Comp.Trans.DeriveTrans as T import qualified Data.Comp.Trans.DeriveUntrans as U import Data.Comp.Trans.DeriveMulti import Data.Comp.Trans.Collect import Data.Comp.Trans.Names -- | -- Declares a multi-sorted compositional datatype isomorphic to the -- given ADT. -- -- /e.g./ -- -- @ -- import qualified Foo as F -- deriveMultiComp ''F.Arith -- @ -- -- will create -- -- @ -- data ArithL -- data AtomL -- data LitL -- -- data Arith e l where -- Add :: e AtomL -> e AtomL -> Arith e ArithL -- -- data Atom e l where -- Var :: String -> Atom e AtomL -- Const :: e LitL -> Atom e AtomL -- -- data Lit (e :: * -> *) l where -- Lit :: Int -> Lit e LitL -- @ deriveMultiComp :: Name -> Q [Dec] deriveMultiComp root = do descs <- collectTypes root liftM concat $ mapM deriveMulti descs -- | -- -- /e.g./ -- -- @ -- generateNameLists ''Arith -- @ -- -- will create -- -- @ -- origASTTypes = [mkName "Foo.Arith", mkName "Foo.Atom", mkName "Foo.Lit"] -- newASTTypes = [mkName "Arith", mkName "Atom", mkName "Lit"] -- newASTLabels = map ConT [mkName "ArithL", mkName "AtomL', mkName "LitL"] -- @ generateNameLists :: Name -> Q [Dec] generateNameLists root = do descs <- collectTypes root nameList1 <- mkList ''Name (mkName "origASTTypes") descs nameList2 <- mkList ''Name (mkName "newASTTypes") (map transName descs) return $ nameList1 ++ nameList2 where mkList :: Data t => Name -> Name -> [t] -> Q [Dec] mkList tNm name contents = sequence [ sigD name (appT listT (conT tNm)) , valD (varP name) (normalB namesExp) [] ] where namesExp = dataToExpQ (const Nothing) contents getLabels :: [Name] -> Q [Type] getLabels nms = mapM toLabel nms where toLabel n = do TyConI (DataD _ n' _ _ _) <- reify $ nameLab n return $ ConT n' -- | -- Folds together names with @(`:+:`)@. -- -- /e.g./ -- -- @ -- import qualified Foo as F -- deriveMult ''F.Arith -- makeSumType \"ArithSig\" [''Arith, ''Atom, ''Lit] -- @ -- -- will create -- -- @ -- type ArithSig = Arith :+: Atom :+: Lit -- @ -- -- You can use `generateNameLists` to avoid spelling out the names manually makeSumType :: String -> [Name] -> Q [Dec] makeSumType nm types = sequence $ [tySynD (mkName nm) [] $ sumType types] where sumType [] = fail "Attempting to make empty sum type" sumType [t] = conT t sumType (t:ts) = appT (appT (conT ''(:+:)) (conT t)) (sumType ts)