packages feed

cgen-0.0.1: src/DeriveMod.hs

module DeriveMod(deriveMod, deriveSMod, deriveMods, modify)
where

import Language.Haskell.TH
import Data.Char
import Control.Monad.State.Class()
import Control.Monad.State(modify) -- for exporting

{-
 example:

data DataType = Constructor {
    field1 :: String
  , field2 :: Int
  }
$(deriveMods ''DataType)

=>

-- from mkModN'
modifyField1 :: (String -> String) -> DataType -> DataType
modifyField1 f c = c{field1 = f (field1 c)}

-- from toState
sModifyField1 :: (MonadState m) => (String -> String) -> m DataType ()
sModifyField1 f = modify (modifyField1 f)

-- from mkSet
setField1 :: String -> DataType -> DataType
setField1 v c = c{field1 = v}

-- from toState
sSetField1 :: (MonadState m) => String -> m DataType ()
sSetField1 v = modify (setField1 v)

and repeat for field2.

-}

deriveMod :: String -> [Dec]
deriveMod = mkModN' . mkName

-- mkModN' abc => modAbc f c = c{abc = f(abc c)}
mkModN' :: Name -> [Dec]
mkModN' n = 
  let f = mkName "f"
      c = mkName "c"
      m = mkName ("mod" ++ (capitalize (nameBase n)))
  in [FunD m 
         [Clause [VarP f,VarP c] 
                 (NormalB 
                    (RecUpdE (VarE c) 
                             [(n, AppE (VarE f) (AppE (VarE n) (VarE c)))])) []]]

-- mkModM mkModN' abc creates all modFoo functions for datatype abc.
mkModM :: (Name -> [Dec]) -> Name -> Q [Dec]
mkModM crf d = do
  fs <- dToFs d
  let exps = concatMap crf fs
  return $ exps

-- get the fields of a data type.
dToFs d = do
  TyConI (DataD _ _ _ cons _) <- reify d
  return $ concatMap getF cons

deriveMods :: Name -> Q [Dec]
deriveMods d = do
  fs1 <- mkModM mkModN' d
  let fs2 = deriveSMod fs1
  fs3 <- mkModM mkSet d
  let fs4 = concatMap toState fs3
  return (fs1 ++ fs2 ++ fs3 ++ fs4)

deriveSMod :: [Dec] -> [Dec]
deriveSMod =
  concatMap toState

-- turns a -> b -> b into (MonadState m) => a -> m b ().
toState (FunD fn _) =
  let nn = mkName ('s' : capitalize (nameBase fn))
      f  = mkName "f"
  in [FunD nn
         [Clause [VarP f] 
            (NormalB 
                (AppE (VarE (mkName "modify"))
                      (AppE (VarE fn) (VarE f)))) []]]
toState _ = []

-- get a list of all fields of a constructor.
getF :: Con -> [Name]
getF (RecC _ vars) = let (names, _, _) = unzip3 vars
                     in names
getF _             = []

capitalize []     = []
capitalize (h:hs) = toUpper h : hs

mkSet :: Name -> [Dec]
mkSet n = 
  let v = mkName "v"
      c = mkName "c"
      s = mkName ("set" ++ (capitalize (nameBase n)))
  in [FunD s
        [Clause [VarP v, VarP c]
                 (NormalB
                    (RecUpdE (VarE c)
                             [(n, VarE v)])) []]]