{-# LANGUAGE TypeSynonymInstances #-} -- | These small short-named functions are intended to make the -- construction of abstranct syntax trees less tedious. module Language.Haskell.TH.Helper where import Data.Char import Language.Haskell.TH.Syntax import Language.Haskell.TH.Data import Language.Haskell.TH.Compat -- * Special folds for the guessing applyWith, foldl1With, foldr1With :: Exp -> [Exp] -> Exp applyWith join xs = foldl AppE join xs foldl1With join xs = foldr1 (\y x -> AppE (AppE join y) x) xs foldr1With join xs = foldr1 (\y x -> AppE (AppE join x) y) xs -- * Syntax elements -- -- | A simple clause, without where or guards. sclause :: [Pat] -> Exp -> Clause sclause pats body = Clause pats (NormalB body) [] -- | A default clause with N arguments. defclause :: Int -> Exp -> Clause defclause num = sclause (replicate num WildP) -- | A simple Val clause sval :: Pat -> Exp -> Dec sval pat body = ValD pat (NormalB body) [] case' :: Exp -> [(Pat, Exp)] -> Exp case' exp alts = CaseE exp [ Match x (NormalB y) [] | (x,y) <- alts ] (->:) :: String -> Exp -> Exp (->:) nm bdy = LamE [vr nm] bdy -- | We provide 3 standard instance constructors -- instance_default requires C for each free type variable -- instance_none requires no context -- instance_context requires a given context instance_none :: String -> DataDef -> [Dec] -> Dec instance_none = instance_context [] instance_default :: String -> DataDef -> [Dec] -> Dec instance_default n = instance_context [n] n instance_context :: [String] -> String -> DataDef -> [Dec] -> Dec instance_context req cls dat defs = InstanceD ctx hed defs where vrs = vars 't' (dataArity dat) hed = l1 cls (lK (dataName dat) vrs) ctx = [typeToPred $ l1 r v | r <- req, v <- vrs] -- | Build an instance of a class for a data type, using the heuristic -- that the type is itself required on all type arguments. simple_instance :: String -> DataDef -> [Dec] -> [Dec] simple_instance cls dat defs = [instance_default cls dat defs] -- | Build an instance of a class for a data type, using the class at the given types generic_instance :: String -> DataDef -> [Type] -> [Dec] -> [Dec] generic_instance cls dat ctxTypes defs = [InstanceD ctx hed defs] where vrs = vars 't' (dataArity dat) hed = l1 cls (lK (dataName dat) vrs) ctx = map (typeToPred . l1 cls) ctxTypes -- | Build a type signature declaration with a string name sigN :: String -> Type -> Dec sigN nam ty = SigD (mkName nam) ty -- | Build a fundecl with a string name funN :: String -> [Clause] -> Dec funN nam claus = FunD (mkName nam) claus -- * Pattern vs Value abstraction class Eq nm => NameLike nm where toName :: nm -> Name instance NameLike Name where toName = id instance NameLike String where toName = mkName -- | The class used to overload lifting operations. To reduce code -- duplication, we overload the wrapped constructors (and everything -- else, but that's irrelevant) to work in patterns, expressions, and -- types. class Valcon a where -- | Build an application node, with a name for a head and a -- provided list of arguments. lK :: NameLike nm => nm -> [a] -> a -- | Reference a named variable. vr :: NameLike nm => nm -> a -- | Lift a TH 'Lit' raw_lit :: Lit -> a -- | Tupling tup :: [a] -> a -- | Listing lst :: [a] -> a instance Valcon Exp where lK nm ys = let name = toName nm in case (nameBase name, ys) of ("[]", []) -> ConE name ("[]", xs) -> lst xs ((x:_), args) | isUpper x || x == ':' -> foldl AppE (ConE name) args ((x:_), [a,b]) | isOper x -> InfixE (Just a) (VarE name) (Just b) where isOper x = not (isAlpha x || x == '_') (nm, args) -> foldl AppE (VarE name) args vr = VarE . toName raw_lit = LitE tup = TupE lst = ListE instance Valcon Pat where lK = ConP . toName vr = VarP . toName raw_lit = LitP tup = TupP lst = ListP instance Valcon Type where lK nm = foldl AppT (if bNm == "[]" then ListT else ConT (mkName bNm)) where bNm = nameBase (toName nm) vr = VarT . toName raw_lit = error "raw_lit @ Type" -- XXX work around bug in GHC < 6.10 -- (see http://hackage.haskell.org/trac/ghc/ticket/2358 for details) tup [t] = t tup ts = foldl AppT (TupleT (length ts)) ts lst = error "lst @ Type" -- | Build an application node without a given head app :: Exp -> [Exp] -> Exp app root args = foldl AppE root args -- | This class is used to overload literal construction based on the -- type of the literal. class LitC a where lit :: Valcon p => a -> p instance LitC Integer where lit = raw_lit . IntegerL instance LitC Char where lit = raw_lit . CharL instance LitC a => LitC [a] where lit = lst . map lit instance (LitC a, LitC b) => LitC (a,b) where lit (x,y) = tup [lit x, lit y] instance (LitC a, LitC b, LitC c) => LitC (a,b,c) where lit (x,y,z) = tup [lit x, lit y, lit z] instance LitC () where lit () = tup [] -- * Constructor abstraction dataVars :: DataDef -> [Type] dataVars dat = take (dataArity dat) $ map (VarT . mkName . return) ['a'..] -- | Common pattern: list of a familiy of variables vars :: Valcon a => Char -> Int -> [a] vars c n = map (vrn c) [1 .. n] -- | Variable based on a letter + number vrn :: Valcon a => Char -> Int -> a vrn c n = vr (c : show n) -- | Make a list of variables, one for each argument to a constructor ctv :: Valcon a => CtorDef -> Char -> [a] ctv ctor c = vars c (ctorArity ctor) -- | Make a simple pattern to bind a constructor ctp :: Valcon a => CtorDef -> Char -> a ctp ctor c = lK (ctorName ctor) (ctv ctor c) -- | Reference the constructor itself ctc :: Valcon a => CtorDef -> a ctc = l0 . ctorName -- * Lift a constructor over a fixed number of arguments. l0 :: (NameLike nm, Valcon a) => nm -> a l1 :: (NameLike nm, Valcon a) => nm -> a -> a l2 :: (NameLike nm, Valcon a) => nm -> a -> a -> a l0 s = lK s [] l1 s a = lK s [a] l2 s a b = lK s [a,b] -- * Pre-lifted versions of common operations true, false, nil :: Valcon a => a hNil', hZero' :: Type true = l0 "True" false = l0 "False" nil = l0 "[]" unit = lit () hNil' = l0 "HNil" hZero' = l0 "HZero" id' = l0 "id" cons :: Valcon a => a -> a -> a cons = l2 ":" box :: Valcon a => a -> a return', const' :: Exp -> Exp hSucc' :: Type -> Type box x = cons x nil return' = l1 "return" const' = l1 "const" hSucc' = l1 "HSucc" (==:), (&&:), (++:), (>>=:), (>>:), (.:), ap', (>:) :: Exp -> Exp -> Exp hCons' :: Type -> Type -> Type (==:) = l2 "==" (&&:) = l2 "&&" (++:) = l2 "++" (>>=:) = l2 ">>=" (>>:) = l2 ">>" (.:) = l2 "." (>:) = l2 ">" ap' = l2 "ap" hCons' = l2 "HCons" -- | Build a chain of expressions, with an appropriate terminal -- sequence__ does not require a unit at the end (all others are optimised automatically) (&&::), (++::), (>>::), sequence__, (.::) :: [Exp] -> Exp (&&::) = foldr (&&:) true (++::) = foldr (++:) nil (>>::) = foldr (>>:) (return' unit) (.::) = foldr (.:) id' sequence__ [] = return' unit sequence__ xs = foldr1 (>>:) xs -- | K-way liftM liftmk :: Exp -> [Exp] -> Exp liftmk hd args = foldl ap' (return' hd) args