{-# LANGUAGE Rank2Types , ConstraintKinds , KindSignatures , TypeFamilies , ScopedTypeVariables , MultiParamTypeClasses , AllowAmbiguousTypes , FlexibleContexts , FlexibleInstances , UndecidableInstances , DataKinds , TypeApplications , DeriveGeneric , DeriveDataTypeable , TypeOperators , PolyKinds #-} module Data.Generics.ClassyPlate.Core ( -- public functions and classes classyTraverse, classyTraverseM, selectiveTraverse, selectiveTraverseM, smartTraverse, smartTraverseM , ClassyPlate, SmartClassyPlate -- generator functions and datatypes , classyTraverse_, classyTraverseM_, selectiveTraverse_, selectiveTraverseM_, smartTraverse_, smartTraverseM_ , app, appM, appIf, appIfM , GoodOperationFor, GoodOperationForAuto, FlagToken -- MonoMatch , MonoMatch (..) ) where import GHC.Exts import Data.Maybe import GHC.Generics (Generic) import Data.Data (Data) import Data.Generics.ClassyPlate.TypePrune -- FIXME: when TH supports type application we can remove the token parameters type GoodOperationFor c e = (App (AppSelector c e) c e) type GoodOperationForAuto c e = (GoodOperationFor c e, Generic e) data ClsToken (c :: * -> Constraint) data FlagToken (c :: Bool) -- | A class for applying a function if the class of the functions allows the application class App (flag :: Bool) c b where app :: FlagToken flag -> ClsToken c -> (forall a . c a => a -> a) -> b -> b appM :: Monad m => FlagToken flag -> ClsToken c -> (forall a . c a => a -> m a) -> b -> m b appPred :: FlagToken flag -> ClsToken c -> (forall a . c a => a -> Bool) -> b -> b -> b appPredM :: Monad m => FlagToken flag -> ClsToken c -> (forall a . c a => a -> m Bool) -> b -> m b -> m b instance c b => App 'True c b where {-# INLINE app #-} app _ _ f a = f a {-# INLINE appM #-} appM _ _ f a = f a {-# INLINE appPred #-} appPred _ _ f th el = if f th then el else th {-# INLINE appPredM #-} appPredM _ _ f th el = f th >>= \p -> if p then el else return th instance App 'False c b where {-# INLINE app #-} app _ _ _ a = a {-# INLINE appM #-} appM _ _ _ a = return a {-# INLINE appPred #-} appPred _ _ _ _ el = el {-# INLINE appPredM #-} appPredM _ _ _ _ el = el -- | A class for traversals that use a polymorphic function to visit all applicable elements. class GoodOperationFor c b => ClassyPlate c b where classyTraverse_ :: ClsToken c -> (forall a . c a => a -> a) -> b -> b classyTraverseM_ :: Monad m => ClsToken c -> (forall a . c a => a -> m a) -> b -> m b selectiveTraverse_ :: ClsToken c -> (forall a . c a => a -> a) -> (forall a . c a => a -> Bool) -> b -> b selectiveTraverseM_ :: Monad m => ClsToken c -> (forall a . c a => a -> m a) -> (forall a . c a => a -> m Bool) -> b -> m b -- | A class for traversals that use a polymorphic function to visit all applicable elements but only visit the -- parts where the applicable elements could be found. class (GoodOperationForAuto c b) => SmartClassyPlate c (sel :: Bool) b where smartTraverse_ :: FlagToken sel -> ClsToken c -> (forall a . c a => a -> a) -> b -> b smartTraverseM_ :: Monad m => FlagToken sel -> ClsToken c -> (forall a . c a => a -> m a) -> b -> m b -- | Traverse the data structure with a polymorphic function. classyTraverse :: forall c b . ClassyPlate c b => (forall a . c a => a -> a) -> b -> b {-# INLINE classyTraverse #-} classyTraverse = classyTraverse_ (undefined :: ClsToken c) -- | Traverse the data structure with a polymorphic monadic function. classyTraverseM :: forall c b m . (ClassyPlate c b, Monad m) => (forall a . c a => a -> m a) -> b -> m b {-# INLINE classyTraverseM #-} classyTraverseM = classyTraverseM_ (undefined :: ClsToken c) -- | Traverse only those parts that are selected by the given selector function. selectiveTraverse :: forall c b . ClassyPlate c b => (forall a . c a => a -> a) -> (forall a . c a => a -> Bool) -> b -> b {-# INLINE selectiveTraverse #-} selectiveTraverse = selectiveTraverse_ (undefined :: ClsToken c) -- | Traverse only those parts that are selected by the given monadic selector function. selectiveTraverseM :: forall c b m . (ClassyPlate c b, Monad m) => (forall a . c a => a -> m a) -> (forall a . c a => a -> m Bool) -> b -> m b {-# INLINE selectiveTraverseM #-} selectiveTraverseM = selectiveTraverseM_ (undefined :: ClsToken c) -- | Traverse only those parts of the data structure that could possibly contain elements that the given function can be applied on smartTraverse :: forall c b . SmartClassyPlate c (ClassIgnoresSubtree c b) b => (forall a . c a => a -> a) -> b -> b {-# INLINE smartTraverse #-} smartTraverse = smartTraverse_ (undefined :: FlagToken (ClassIgnoresSubtree c b)) (undefined :: ClsToken c) -- | Traverse only those parts of the data structure that could possibly contain elements that the given monadic function can be applied on smartTraverseM :: forall c b m . (SmartClassyPlate c (ClassIgnoresSubtree c b) b, Monad m) => (forall a . c a => a -> m a) -> b -> m b {-# INLINE smartTraverseM #-} smartTraverseM = smartTraverseM_ (undefined :: FlagToken (ClassIgnoresSubtree c b)) (undefined :: ClsToken c) instance (GoodOperationForAuto c b) => SmartClassyPlate c True b where smartTraverse_ _ t f a = app (undefined :: FlagToken (AppSelector c b)) t f a {-# INLINE smartTraverse_ #-} smartTraverseM_ _ t f a = appM (undefined :: FlagToken (AppSelector c b)) t f a {-# INLINE smartTraverseM_ #-} {-# SPECIALIZE INLINE classyTraverse_ :: ClassyPlate (MonoMatch x) sel b => (forall a . MonoMatch x a => a -> a) -> b -> b #-} -- | A class for the simple case when the applied function is monomorphic. class MonoMatch a b where -- | Apply a monomorphic function on a polymorphic data structure. monoApp :: (a -> a) -> b -> b instance MonoMatch a a where monoApp = id {-# INLINE monoApp #-} type instance AppSelector (MonoMatch a) b = TypEq a b type family TypEq a b :: Bool where TypEq a a = 'True TypEq a b = 'False appIf :: forall c b . App (AppSelector c b) c b => ClsToken c -> (forall a . c a => a -> a) -> (forall a . c a => a -> Bool) -> b -> b -> b {-# INLINE appIf #-} appIf t f pred val combined = app flTok t f $ appPred flTok t pred val combined where flTok = undefined :: FlagToken (AppSelector c b) appIfM :: forall c b m . (App (AppSelector c b) c b, Monad m) => ClsToken c -> (forall a . c a => a -> m a) -> (forall a . c a => a -> m Bool) -> b -> m b -> m b {-# INLINE appIfM #-} appIfM t f pred val combined = appM flTok t f =<< appPredM flTok t pred val combined where flTok = undefined :: FlagToken (AppSelector c b)