-- | Folds over the fold family rooted at 'FoldFamily'. module Data.Origami.Internal.Fold( -- * The 'Fold' structure and useful instances Fold(..), idFold, errFold, monadicFold, -- * folding functions foldFoldFamily, foldDataTy, foldDataCase, foldDataField, foldTy ) where import Control.Monad import Data.Origami.Internal.FoldFamily import Language.Haskell.TH ------------------------------------------------------------ -- folds ------------------------------------------------------------ -- | The fold. Bundles up the functions used to replace constructors -- in the fold family. data Fold dataCase dataField dataTy foldFamily ty = Fold { mkAtomic :: ty -> dataField, mkBifunct :: Name -> dataField -> dataField -> dataField, mkDataCase :: Name -> [dataField] -> dataCase, mkDataTy :: Name -> [dataCase] -> dataTy, mkFoldFamily :: [dataTy] -> foldFamily, mkFunct :: Name -> dataField -> dataField, mkNonatomic :: ty -> dataField, mkTrifunct :: Name -> dataField -> dataField -> dataField -> dataField, mkTy :: Name -> ty } -- | The identity 'Fold'. Intended as a base to be modified. idFold :: Fold DataCase DataField DataTy FoldFamily Ty idFold = Fold { mkAtomic = Atomic, mkBifunct = Bifunct, mkDataCase = DataCase, mkDataTy = DataTy, mkFoldFamily = FoldFamily, mkFunct = Funct, mkNonatomic = Nonatomic, mkTrifunct = Trifunct, mkTy = Ty } -- | The error 'Fold'. Intended as a base to be modified. errFold :: String -> Fold dataCase dataField dataTy foldFamily ty errFold str = Fold { mkAtomic = err "mkAtomic", mkBifunct = err "mkBifunct", mkDataCase = err "mkDataCase", mkDataTy = err "mkDataTy", mkFoldFamily = err "mkFoldFamily", mkFunct = err "mkFunct", mkNonatomic = err "mkNonatomic", mkTrifunct = err "mkTrifunct", mkTy = err "mkTy" } where err tag = error (str ++ "." ++ tag) -- | Using the constructors from the base 'Fold', folds monadically in -- a bottom-up, left-to-right manner. monadicFold :: Monad m => Fold dataCase dataField dataTy foldFamily ty -> Fold (m dataCase) (m dataField) (m dataTy) (m foldFamily) (m ty) monadicFold f = Fold { mkAtomic = liftM (mkAtomic f), mkBifunct = liftM2 . mkBifunct f, mkDataCase = \ nm dfs -> do { dfs' <- sequence dfs; return $ mkDataCase f nm dfs' }, mkDataTy = \ nm dcs -> do { dcs' <- sequence dcs; return $ mkDataTy f nm dcs' }, mkFoldFamily = \ dts -> do { dts' <- sequence dts; return $ mkFoldFamily f dts' }, mkFunct = liftM . mkFunct f, mkTrifunct = liftM3 . mkTrifunct f, mkNonatomic = liftM (mkNonatomic f), mkTy = return . mkTy f } ------------------------------------------------------------ -- folds ------------------------------------------------------------ -- | Monadically folds over a 'DataCase' foldDataCase :: Fold dataCase dataField dataTy foldFamily ty -> DataCase -> dataCase foldDataCase f (DataCase nm dfs) = mkDataCase f nm (fmap (foldDataField f) dfs) -- | Monadically folds over a 'DataField' foldDataField :: Fold dataCase dataField dataTy foldFamily ty -> DataField -> dataField foldDataField f (Atomic ty) = mkAtomic f (foldTy f ty) foldDataField f (Nonatomic ty) = mkNonatomic f (foldTy f ty) foldDataField f (Funct nm df) = mkFunct f nm (foldDataField f df) foldDataField f (Bifunct nm df df') = mkBifunct f nm (foldDataField f df) (foldDataField f df') foldDataField f (Trifunct nm df df' df'') = mkTrifunct f nm (foldDataField f df) (foldDataField f df') (foldDataField f df'') -- | Monadically folds over a 'DataTy' foldDataTy :: Fold dataCase dataField dataTy foldFamily ty -> DataTy -> dataTy foldDataTy f (DataTy nm dcs) = mkDataTy f nm (fmap (foldDataCase f) dcs) -- | Monadically folds over a 'FoldFamily' foldFoldFamily :: Fold dataCase dataField dataTy foldFamily ty -> FoldFamily -> foldFamily foldFoldFamily f (FoldFamily dts) = mkFoldFamily f (fmap (foldDataTy f) dts) -- | Monadically folds over a 'FoldTy' foldTy :: Fold dataCase dataField dataTy foldFamily ty -> Ty -> ty foldTy f(Ty nm) = mkTy f nm