module Data.Combinators.TH (makeCombinator) where
import Language.Haskell.TH
import Control.Monad
import Data.Char
makeCombinator :: Name -> Q [Dec]
makeCombinator t = do
TyConI d <- reify t
let constructors = typeInfo d
pe <- genPE "f" (length constructors)
let combName = mkName . map toLower . nameBase $ t
clauses = map (combinClause pe) $ zip constructors [0..]
r <- funD combName clauses
return [r]
typeInfo (DataD _ _ _ _ c _) = c
typeInfo (NewtypeD _ _ _ _ c _) = [c]
combinClause :: ([PatQ], [ExpQ])
-> (Con, Int)
-> ClauseQ
combinClause (patsF, varsF) (NormalC name fields, i) = do (patsC, varsC) <- genPE "a" (length fields)
funClause patsF varsF patsC varsC name i
combinClause (patsF, varsF) (RecC name fields, i) = do (patsC, varsC) <- genPE "a" (length fields)
funClause patsF varsF patsC varsC name i
combinClause (patsF, varsF) (InfixC _ name _, i) = do (patsC, varsC) <- genPE "a" 2
funClause patsF varsF patsC varsC name i
combinClause _ (ForallC{}, _) = error "makeCombinator: GADTs are not currently supported."
combinClause _ (GadtC{}, _) = error "makeCombinator: GADTs are not currently supported."
combinClause _ (RecGadtC{}, _) = error "makeCombinator: GADTs are not currently supported."
funClause :: [PatQ] -> [ExpQ] -> [PatQ] -> [ExpQ]
-> Name
-> Int
-> ClauseQ
funClause pF vF pC vC name i =
clause (pF ++ [conP name pC])
(normalB (appE (vF !! i)
(applyConVars vC name vC 0)))
[]
applyConVars :: [ExpQ] -> t -> [a] -> Int -> ExpQ
applyConVars _ _ [] _ = conE (mkName "()")
applyConVars varsC _ [_] n = varsC !! n
applyConVars varsC name' (_:fs) n = tupE [varsC !! n, applyConVars varsC name' fs (n+1)]
genPE :: String -> Int -> Q ([PatQ], [ExpQ])
genPE x n = do
ids <- replicateM n (newName x)
return (map varP ids, map varE ids)