{-# LANGUAGE TemplateHaskell, CPP #-} -- |This module provides a simple abstract data type for -- a piece of a data stucture that can be read from and -- written to. It provides an automatic Template Haskell -- routine to scour data type definitions and generate -- accessor objects for them automatically. module Data.Accessor ( Accessor(..) , nameDeriveAccessors, deriveAccessors , getA, putA, modA , (.>), (<.), (=:) ) where import Language.Haskell.TH import Language.Haskell.TH.Syntax import Data.Maybe (catMaybes) import Control.Monad (guard) import Control.Monad.State -- |An @Accessor s a@ is an object that encodes how to -- get and put a subject of type @a@ out of/into an object -- of type @s@. -- -- In order for an instance of this data structure @a@ to be -- an "Accessor", it must obey the following laws: -- -- > getVal a (setVal a x s) = x -- > setVal a (getVal a s) s = s data Accessor s a = Accessor { getVal :: s -> a , setVal :: a -> s -> s } infixl 9 .> -- |Accessor composition. (.>) :: Accessor a b -> Accessor b c -> Accessor a c f .> g = Accessor { getVal = getVal g . getVal f , setVal = \c a -> setVal f (setVal g c (getVal f a)) a } infixr 9 <. -- |Accessor composition the other direction. -- -- > (<.) = flip (.>) (<.) :: Accessor b c -> Accessor a b -> Accessor a c (<.) = flip (.>) infix 1 =: -- |An "assignment operator" for state monads. -- -- > (=:) = putA (=:) :: MonadState s m => Accessor s a -> a -> m () (=:) = putA -- |A structural dereference function for state monads. getA :: MonadState s m => Accessor s a -> m a getA a = liftM (getVal a) get -- |A structural assignment function for state monads. putA :: MonadState s m => Accessor s a -> a -> m () putA a x = get >>= put . setVal a x -- |A structural modification function for state monads. modA :: MonadState s m => Accessor s a -> (a -> a) -> m () modA a f = liftM f (getA a) >>= putA a -- |@deriveAccessors n@ where @n@ is the name of a data type -- declared with @data@ looks through all the declared fields -- of the data type, and for each field ending in an underscore -- generates an accessor of the same name without the underscore. -- -- It is "nameDeriveAccessors" n f where @f@ satisfies -- -- > f (s ++ "_") = Just s -- > f x = x -- otherwise -- -- For example, given the data type: -- -- > data Score = Score { p1Score_ :: Int -- > , p2Score_ :: Int -- > , rounds :: Int -- > } -- -- @deriveAccessors@ will generate the following objects: -- -- > p1Score :: Accessor Score Int -- > p1Score = Accessor p1Score_ (\x s -> s { p1Score_ = x }) -- > p2Score :: Accessor Score Int -- > p2Score = Accessor p2Score_ (\x s -> s { p2Score_ = x }) -- deriveAccessors :: Name -> Q [Dec] deriveAccessors n = nameDeriveAccessors n transformName where transformName s = do guard $ not (null s) guard $ last s == '_' return $ init s -- |@nameDeriveAccessors n f@ where @n@ is the name of a data type -- declared with @data@ and @f@ is a function from names of fields -- in that data type to the name of the corresponding accessor. If -- @f@ returns @Nothing@, then no accessor is generated for that -- field. nameDeriveAccessors :: Name -> (String -> Maybe String) -> Q [Dec] nameDeriveAccessors t namer = do TyConI (DataD _ name _ cons _) <- reify t liftM concat $ mapM makeAccs cons where makeAccs :: Con -> Q [Dec] makeAccs (RecC _ vars) = liftM catMaybes $ mapM (\ (name,_,_) -> makeAccFromName name) vars makeAccs (ForallC _ _ c) = makeAccs c makeAccs _ = return [] transformName :: Name -> Maybe Name transformName (Name occ f) = do n <- namer (occString occ) return $ Name (mkOccName n) f makeAccFromName :: Name -> Q (Maybe Dec) makeAccFromName name = do case transformName name of Nothing -> return Nothing Just n -> liftM Just $ makeAcc name n -- haddock doesn't grok TH #ifndef __HADDOCK__ makeAcc :: Name -> Name -> Q Dec makeAcc name accName = do body <- [| Accessor { getVal = $( return $ VarE name ) , setVal = \x s -> $( return $ RecUpdE (VarE 's) [(name, VarE 'x)] ) } |] return $ ValD (VarP accName) (NormalB body) [] #endif