packages feed

minioperational-0.4.1: Control/Monad/Operational/TH.hs

{-# LANGUAGE TemplateHaskell #-}
module Control.Monad.Operational.TH (makeSingletons) where
import Prelude hiding (mapM)
import Language.Haskell.TH
import Control.Monad.Operational.Class
import Data.Char

import Control.Monad.Trans.State
import Control.Applicative
import qualified Data.Map as Map
import Control.Lens
import Data.List (nub)
import Data.Traversable

renameType :: Map.Map Name Type -> Type -> Type
renameType m (VarT n) = case n `Map.lookup` m of
    Just t -> t
    Nothing -> VarT n
renameType m (SigT t k)          = SigT (renameType m t) k
renameType m (AppT l r)          = AppT (renameType m l) (renameType m r)
renameType _ t                   = t

tyVars :: Type -> [Name]
tyVars (VarT n) = [n]
tyVars (AppT l r) = tyVars l ++ tyVars r
tyVars _ = []

makeSingletons :: Name -> Q [Dec]
makeSingletons name = do
    TyConI dec <- reify name
    case dec of
        DataD _ _ vs cs _ -> fmap concat $ mapM (fromCon (map fromTyVarBndr vs)) cs
        _ -> fail "Expecting a type construcor"
    where
        gen vs_ eqs_ conName argTypes_ resultType_ = do
            let bodyName = let (b:bs) = nameBase conName in mkName (toLower b : bs)
            
            let refresh1 m i = case Map.lookup i m of
                    Just (VarT v) -> v
                    _ -> i

            let ref = Map.fromList [(v, VarT $ mkName $ "v" ++ show i) | (i, v) <- zip [0..] vs_]
            let vs = map (refresh1 ref) vs_
            let resultType = renameType ref resultType_
            let argTypes = map (renameType ref) argTypes_
            let eqs = [EqualP (renameType ref s) (renameType ref t) | EqualP s t <- eqs_]

            let eqm = Map.fromList [(v, t) | EqualP (VarT v) t <- eqs]
            let vs' = map (refresh1 eqm) vs
            let resultType' = renameType eqm resultType
            let argTypes' = map (renameType eqm) argTypes

            let instr = renameType eqm $ foldl AppT (ConT name) $ map VarT (init vs')
            let m = mkName "m"

            let vars = map PlainTV $ (m :) $ nub
                    $ tyVars instr ++ tyVars resultType' ++ concatMap tyVars argTypes'

            let sig = SigD bodyName $ ForallT vars [ClassP ''(:!) [instr, VarT m]]
                    $ foldr (\x y -> AppT ArrowT x `AppT` y) (AppT (VarT m) resultType') argTypes'
            
            ps <- mapM (newName . ("p"++) . show) [0..length argTypes - 1]

            let body = AppE (VarE 'singleton) $ foldl AppE (ConE conName) (map VarE ps)

            return [sig, FunD bodyName [Clause (map VarP ps) (NormalB body) []]]  

        fromCon vs (ForallC _ eqs (NormalC conName ts)) = gen vs eqs conName (map snd ts) (VarT $ last vs)
        fromCon vs (NormalC conName ts) = gen vs [] conName (map snd ts) (VarT $ last vs)
        fromCon _ _ = fail "Unsupported data constructor"

        fromTyVarBndr :: TyVarBndr -> Name
        fromTyVarBndr (PlainTV n) = n
        fromTyVarBndr (KindedTV n _) = n