uniplate 1.6 → 1.6.1
raw patch · 11 files changed
+129/−210 lines, 11 filesdep ~base
Dependency ranges changed: base
Files
- Data/Generics/Biplate.hs +4/−2
- Data/Generics/PlateData.hs +8/−4
- Data/Generics/PlateDirect.hs +4/−2
- Data/Generics/PlateTypeable.hs +4/−2
- Data/Generics/Uniplate.hs +2/−2
- Data/Generics/Uniplate/Internal/Data.hs +70/−173
- Data/Generics/Uniplate/Internal/Utils.hs +4/−0
- Data/Generics/UniplateOn.hs +2/−2
- Data/Generics/UniplateStr.hs +4/−2
- Data/Generics/UniplateStrOn.hs +4/−2
- uniplate.cabal +23/−19
Data/Generics/Biplate.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {- |- /RECOMMENDATION/: Use "Data.Generics.Uniplate.Operations" instead.+ /DEPRECATED/: Use "Data.Generics.Uniplate.Operations" instead. Requires multi-parameter type classes, so is no longer Haskell 98. These operations are easier to use and construct than the equivalent "Data.Generics.UniplateStrOn" methods,@@ -19,7 +19,9 @@ available on GHC, but automatically infers instances. -} -module Data.Generics.Biplate(+module Data.Generics.Biplate+ {- DEPRECATED "Use Data.Generics.Uniplate.Operations instead" -}+ ( module Data.Generics.UniplateStrOn, module Data.Generics.Biplate ) where
Data/Generics/PlateData.hs view
@@ -1,22 +1,26 @@ {-# LANGUAGE ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances, ExistentialQuantification, Rank2Types, CPP #-} {- |- /RECOMMENDATION/: Use "Data.Generics.Uniplate.Data" instead.+ /DEPRECATED/: Use "Data.Generics.Uniplate.Data" instead. This module exports 'Biplate' instances for everything with 'Data' defined. Using GHC the 'Data' instances can be constructed with @deriving Data@. -}-module Data.Generics.PlateData(+module Data.Generics.PlateData+ {-# DEPRECATED "Use Data.Generics.Uniplate.Data instead" #-}+ ( module Data.Generics.Biplate ) where import Data.Generics.Biplate import Data.Generics.PlateInternal import Data.Generics++#if !(__GLASGOW_HASKELL__ < 606 || __GLASGOW_HASKELL__ >= 702) import Data.List import qualified Data.IntSet as IntSet import Data.Ratio-+#endif -- | An existential box representing a type which supports SYB@@ -35,7 +39,7 @@ start -> find -> Box find -#if __GLASGOW_HASKELL__ < 606+#if __GLASGOW_HASKELL__ < 606 || __GLASGOW_HASKELL__ >= 702 -- GHC 6.4.2 does not export typeRepKey, so we can't do the trick -- as efficiently, so we just give up and revert to always following
Data/Generics/PlateDirect.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {- |- /RECOMMENDATION/: Use "Data.Generics.Uniplate.Direct" instead.+ /DEPRECATED/: Use "Data.Generics.Uniplate.Direct" instead. This module supplies a method for writing 'Biplate' instances more easily. This module requires fewest extensions, highest performance, and most instance@@ -36,7 +36,9 @@ -} -module Data.Generics.PlateDirect(+module Data.Generics.PlateDirect+ {-# DEPRECATED "Use Data.Generics.Uniplate.Direct instead" #-}+ ( module Data.Generics.Biplate, -- * The Combinators plate, plateSelf,
Data/Generics/PlateTypeable.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances #-} {- |- /RECOMMENDATION/: Use "Data.Generics.Uniplate.Typeable" instead.+ /DEPRECATED/: Use "Data.Generics.Uniplate.Typeable" instead. This module supplies a method for writing 'Biplate' instances more easily. @@ -20,7 +20,9 @@ > uniplate = uniplateAll -} -module Data.Generics.PlateTypeable(+module Data.Generics.PlateTypeable+ {-# DEPRECATED "Use Data.Generics.Uniplate.Typeable instead" #-}+ ( module Data.Generics.Biplate, module Data.Typeable, -- * The Class
Data/Generics/Uniplate.hs view
@@ -1,5 +1,5 @@ {- |-/RECOMMENDATION/ Use "Data.Generics.UniplateStr" instead.+/DEPRECATED/ Use "Data.Generics.Uniplate.Operations" instead. This is the main Uniplate module, which defines all the essential operations in a Haskell 98 compatible manner.@@ -14,7 +14,7 @@ module Data.Generics.Uniplate- {- DEPRECATED "Use Data.Generics.UniplateStr instead" -}+ {- DEPRECATED "Use Data.Generics.Uniplate.Operations instead" -} where import Control.Monad
Data/Generics/Uniplate/Internal/Data.hs view
@@ -12,146 +12,48 @@ import Data.Generics import Data.Maybe import Data.List-import qualified Data.IntSet as IntSet-import Data.IntSet(IntSet)-import qualified Data.IntMap as IntMap-import Data.IntMap(IntMap) import Data.IORef import Control.Exception--------------------------------------------------------------------------- HIT TEST---data Answer a = Hit {fromHit :: a} -- you just hit the element you were after (here is a cast)- | Follow -- go forward, you will find something- | Miss -- you failed to sink my battleship!--data Oracle to = Oracle {fromOracle :: forall on . Typeable on => on -> Answer to}--{-# INLINE hitTest #-}-hitTest :: (Data from, Data to) => from -> to -> Oracle to---#if __GLASGOW_HASKELL__ < 606--- GHC 6.4.2 does not export typeRepKey, so we can't do the trick--- as efficiently, so we just give up and revert to always following--hitTest _ _ = Oracle . maybe Follow Hit . cast---#elif 0---hitTest from to =- let kto = typeKey to- in case hitTestQuery (dataBox from) kto of- Nothing -> Oracle $ \on -> if typeKey on == kto then Hit $ unsafeCoerce on else Follow- Just cache -> let test = cacheHitTest cache in- Oracle $ \on -> let kon = typeKey on in- if kon == kto then Hit $ unsafeCoerce on- else if test kon then Follow- else Miss----- A cache hit test, but partially evaluated-{-# INLINE cacheHitTest #-}-cacheHitTest :: Cache -> TypeKey -> Bool-cacheHitTest (Cache hit miss)- | IntSet.null hit = const False- | IntSet.null miss = const True- | otherwise = \x -> x `IntSet.member` hit----- hit means that this value may result in a hit--- miss means that this value will never result in a hit-data Cache = Cache {hit :: IntSet, miss :: IntSet} deriving Show-newCache = Cache IntSet.empty IntSet.empty----- Indexed by the @from@ type, then the @to@ type--- Nothing means that we can't perform the trick on the set-{-# NOINLINE hitTestCache #-}-hitTestCache :: IORef (IntMap (IntMap (Maybe Cache)))-hitTestCache = unsafePerformIO $ newIORef IntMap.empty+import qualified Data.IntMap as IntMap; import Data.IntMap(IntMap) -hitTestQuery :: DataBox -> TypeKey -> Maybe Cache-hitTestQuery from@(DataBox kfrom vfrom) kto = inlinePerformIO $ do- mp <- readIORef hitTestCache- let res = IntMap.lookup kfrom mp >>= IntMap.lookup kto- case res of- Just ans -> return ans- Nothing -> do- let res = toCache $ hitTestAdd from kto IntMap.empty- res2 <- Control.Exception.catch (return $! res) (\(_ :: SomeException) -> return Nothing)- -- -- uncomment these lines to see where type search fails- -- if isNothing res2 then print ("failure",show (typeOf vfrom),kfrom,kto) else return ()-- atomicModifyIORef hitTestCache $ \mp -> flip (,) () $- IntMap.insertWith (const $ IntMap.insert kto res2) kfrom (IntMap.singleton kto res2) mp- return res2-+#if __GLASGOW_HASKELL__ >= 606 && __GLASGOW_HASKELL__ < 702+import qualified Data.IntSet as Set+import qualified Data.IntMap as Map --- need to classify each item as one of the following-data Res = RHit | RMiss | RFollow | RBad deriving (Show,Eq)+type TypeKey = Int+type TypeSet = Set.IntSet+type TypeMap = Map.IntMap +typeKey :: Typeable a => a -> TypeKey+typeKey x = inlinePerformIO $ typeRepKey $ typeOf x -toCache :: IntMap Res -> Maybe Cache-toCache res | not $ IntSet.null $ f RBad = Nothing- | otherwise = Just $ Cache (f RFollow) (f RMiss)- where f x = IntMap.keysSet $ IntMap.filter (== x) res+#else+import qualified Data.Set as Set+import qualified Data.Map as Map -hitTestAdd :: DataBox -> TypeKey -> IntMap Res -> IntMap Res-hitTestAdd from@(DataBox kfrom _) kto res = case sybChildren from of- _ | kfrom `IntMap.member` res -> res- Nothing -> IntMap.insert kfrom RBad res+type TypeKey = TypeRep+type TypeSet = Set.Set TypeKey+type TypeMap = Map.Map TypeKey - -- make an inductive hypothesis that this value is a miss- -- if it turns out you were wrong, start again- -- uses backtracking, so could be expensive- Just xs | kto == kfrom -> hitTestAdds xs kto $ IntMap.insert kfrom RHit res- | correct -> res2- | otherwise -> hitTestAdds xs kto $ IntMap.insert kfrom RFollow res- where res2 = hitTestAdds xs kto $ IntMap.insert kfrom RMiss res- correct = all ((==) RMiss . (res2 IntMap.!) . dataBoxKey) xs+typeKey :: Typeable a => a -> TypeKey+typeKey = typeOf -hitTestAdds :: [DataBox] -> TypeKey -> IntMap Res -> IntMap Res-hitTestAdds [] kto res = res-hitTestAdds (x:xs) kto res = hitTestAdds xs kto $ hitTestAdd x kto res+#endif -type TypeKey = Int--typeKey :: Typeable a => a -> Int-typeKey x = inlinePerformIO $ typeRepKey $ typeOf x+---------------------------------------------------------------------+-- HIT TEST --- | An existential box representing a type which supports SYB--- operations.-data DataBox = forall a . (Data a) => DataBox {dataBoxKey :: TypeKey, dataBoxVal :: a}--dataBox :: Data a => a -> DataBox-dataBox x = DataBox (typeKey x) x---- return all the possible children of a node--- if you can't do so, just return Nothing-sybChildren :: DataBox -> Maybe [DataBox]-sybChildren (DataBox k x)- | k == typeRational = Just [dataBox (0 :: Integer)]- | isAlgType dtyp = Just $ concatMap f ctrs- | isNorepType dtyp = Nothing- | otherwise = Just []- where- f ctr = gmapQ dataBox (asTypeOf (fromConstr ctr) x)- ctrs = dataTypeConstrs dtyp- dtyp = dataTypeOf x--typeRational = typeKey (undefined :: Rational)+data Answer a = Hit {fromHit :: a} -- you just hit the element you were after (here is a cast)+ | Follow -- go forward, you will find something+ | Miss -- you failed to sink my battleship! -#else+data Oracle to = Oracle {fromOracle :: forall on . Typeable on => on -> Answer to} +{-# INLINE hitTest #-}+hitTest :: (Data from, Data to) => from -> to -> Oracle to hitTest from to = let kto = typeKey to in case readCacheFollower (dataBox from) kto of@@ -167,13 +69,13 @@ -- CACHE -- Store and compute the Follower and HitMap -data Cache = Cache HitMap (IntMap2 (Maybe Follower))+data Cache = Cache HitMap (TypeMap2 (Maybe Follower)) -- Indexed by the @from@ type, then the @to@ type -- Nothing means that we can't perform the trick on the set {-# NOINLINE cache #-} cache :: IORef Cache-cache = unsafePerformIO $ newIORef $ Cache emptyHitMap IntMap.empty+cache = unsafePerformIO $ newIORef $ Cache emptyHitMap Map.empty readCacheFollower :: DataBox -> TypeKey -> Maybe Follower@@ -197,7 +99,7 @@ readCacheHitMap :: DataBox -> Maybe HitMap readCacheHitMap from@(DataBox kfrom vfrom) = inlinePerformIO $ do Cache hit _ <- readIORef cache- case IntMap.lookup kfrom hit of+ case Map.lookup kfrom hit of Just _ -> return $ Just hit Nothing -> do res <- Control.Exception.catch (return $! Just $! insertHitMap from hit) (\(_ :: SomeException) -> return Nothing)@@ -209,15 +111,24 @@ ------------------------------------------------------------------------ INTMAP2+-- TYPEMAP2/INTMAP2 +type TypeMap2 a = TypeMap (TypeMap a)++lookup2 :: TypeKey -> TypeKey -> TypeMap2 a -> Maybe a+lookup2 x y mp = Map.lookup x mp >>= Map.lookup y++insert2 :: TypeKey -> TypeKey -> a -> TypeMap2 a -> TypeMap2 a+insert2 x y v mp = Map.insertWith (const $ Map.insert y v) x (Map.singleton y v) mp++ type IntMap2 a = IntMap (IntMap a) -lookup2 :: Int -> Int -> IntMap (IntMap x) -> Maybe x-lookup2 x y mp = IntMap.lookup x mp >>= IntMap.lookup y+intLookup2 :: Int -> Int -> IntMap2 a -> Maybe a+intLookup2 x y mp = IntMap.lookup x mp >>= IntMap.lookup y -insert2 :: Int -> Int -> x -> IntMap (IntMap x) -> IntMap (IntMap x)-insert2 x y v mp = IntMap.insertWith (const $ IntMap.insert y v) x (IntMap.singleton y v) mp+intInsert2 :: Int -> Int -> a -> IntMap2 a -> IntMap2 a+intInsert2 x y v mp = IntMap.insertWith (const $ IntMap.insert y v) x (IntMap.singleton y v) mp ---------------------------------------------------------------------@@ -230,23 +141,17 @@ -- HitMap must have addHitMap on the key follower :: TypeKey -> TypeKey -> HitMap -> Follower follower from to mp- | IntSet.null hit = const False- | IntSet.null miss = const True- | otherwise = \now -> now `IntSet.member` hit+ | Set.null hit = const False+ | Set.null miss = const True+ | otherwise = \now -> now `Set.member` hit where- (hit,miss) = IntSet.partition (\x -> to `IntSet.member` grab x) (IntSet.insert from $ grab from)- grab x = IntMap.findWithDefault (error "couldn't grab in follower") x mp+ (hit,miss) = Set.partition (\x -> to `Set.member` grab x) (Set.insert from $ grab from)+ grab x = Map.findWithDefault (error "couldn't grab in follower") x mp --------------------------------------------------------------------- -- DATA/TYPEABLE OPERATIONS -type TypeKey = Int--typeKey :: Typeable a => a -> Int-typeKey x = inlinePerformIO $ typeRepKey $ typeOf x-- -- | An existential box representing a type which supports SYB -- operations. data DataBox = forall a . (Data a) => DataBox {dataBoxKey :: TypeKey, dataBoxVal :: a}@@ -271,26 +176,26 @@ -- HITMAP -- What is the transitive closure of a type key -type HitMap = IntMap IntSet+type HitMap = TypeMap TypeSet emptyHitMap :: HitMap-emptyHitMap = IntMap.fromList- [(tRational, IntSet.singleton tInteger)- ,(tInteger, IntSet.empty)]+emptyHitMap = Map.fromList+ [(tRational, Set.singleton tInteger)+ ,(tInteger, Set.empty)] where tRational = typeKey (undefined :: Rational) tInteger = typeKey (0 :: Integer) insertHitMap :: DataBox -> HitMap -> HitMap-insertHitMap box hit = fixEq trans (populate box) `IntMap.union` hit+insertHitMap box hit = fixEq trans (populate box) `Map.union` hit where -- create a fresh box with all the necessary children that aren't in hit populate :: DataBox -> HitMap- populate x = f x IntMap.empty+ populate x = f x Map.empty where f (DataBox key val) mp- | key `IntMap.member` hit || key `IntMap.member` mp = mp- | otherwise = fs cs $ IntMap.insert key (IntSet.fromList $ map dataBoxKey cs) mp+ | key `Map.member` hit || key `Map.member` mp = mp+ | otherwise = fs cs $ Map.insert key (Set.fromList $ map dataBoxKey cs) mp where cs = sybChildren val fs [] mp = mp@@ -299,10 +204,10 @@ -- update every one to be the transitive closure trans :: HitMap -> HitMap- trans mp = IntMap.map f mp+ trans mp = Map.map f mp where- f x = IntSet.unions $ x : map g (IntSet.toList x)- g x = IntMap.findWithDefault (hit IntMap.! x) x mp+ f x = Set.unions $ x : map g (Set.toList x)+ g x = Map.findWithDefault (hit Map.! x) x mp fixEq :: Eq a => (a -> a) -> a -> a@@ -310,9 +215,6 @@ where x2 = f x -#endif-- --------------------------------------------------------------------- -- INSTANCE FUNCTIONS @@ -382,9 +284,6 @@ transformBis_ :: forall a . Data a => [[Transformer]] -> a -> a --#if __GLASGOW_HASKELL__ >= 606- -- basic algorithm: -- as you go down, given transformBis [fN..f1] -- if x is not in the set reachable by fN..f1, return x@@ -397,38 +296,36 @@ on = dataBox (undefined :: a) hitBoxM = readCacheHitMap on hitBox = fromJust hitBoxM- univ = IntSet.toAscList $ IntSet.insert (dataBoxKey on) $ hitBox IntMap.! dataBoxKey on+ univ = Set.toAscList $ Set.insert (dataBoxKey on) $ hitBox Map.! dataBoxKey on n = length ts -- (a,b), where a < b, and both in range 1..n- sliceMe i j = fromMaybe IntMap.empty $ lookup2 i j slices- slices :: IntMap2 (IntMap (Maybe Transformer))+ sliceMe i j = fromMaybe Map.empty $ intLookup2 i j slices+ slices :: IntMap2 (TypeMap (Maybe Transformer)) slices = IntMap.fromAscList [ (i, IntMap.fromAscList [(j, slice i j ts) | (j,ts) <- zip [i..n] (tail $ inits ts)]) | (i,ts) <- zip [1..n] (tails $ reverse ts)] - slice :: Int -> Int -> [[Transformer]] -> IntMap (Maybe Transformer)+ slice :: Int -> Int -> [[Transformer]] -> TypeMap (Maybe Transformer) slice from to tts = self where- self = f IntMap.empty (zip [from..] tts) -- FIXME: flattening out here gives different results...+ self = f Map.empty (zip [from..] tts) -- FIXME: flattening out here gives different results... f a ((i,[Transformer tk tr]):ts)- | tk `IntMap.member` a = f a ts- | otherwise = f (IntMap.insert tk t a) ts+ | tk `Map.member` a = f a ts+ | otherwise = f (Map.insert tk t a) ts where t = Just $ Transformer tk $ op (sliceMe (i+1) to) . tr . gmapT (op $ sliceMe from i) - f a [] = a `IntMap.union` IntMap.fromAscList (mapMaybe (g $ IntMap.keysSet a) $ univ)+ f a [] = a `Map.union` Map.fromAscList (mapMaybe (g $ Map.keysSet a) univ) g a t = if b then Nothing else Just (t, Nothing)- where b = IntSet.null $ a `IntSet.intersection` (hitBox IntMap.! t)+ where b = Set.null $ a `Set.intersection` (hitBox Map.! t) - op :: forall b . Data b => IntMap (Maybe Transformer) -> b -> b- op slice = case IntMap.lookup (typeKey (undefined :: b)) slice of+ op :: forall b . Data b => TypeMap (Maybe Transformer) -> b -> b+ op slice = case Map.lookup (typeKey (undefined :: b)) slice of Nothing -> id Just Nothing -> gmapT (op slice) Just (Just (Transformer _ t)) -> unsafeCoerce . t . unsafeCoerce--#endif transformBis_ [] = id
Data/Generics/Uniplate/Internal/Utils.hs view
@@ -8,7 +8,11 @@ unsafeCoerce, builder, unsafePerformIO, inlinePerformIO, concatCont ) where +#if __GLASGOW_HASKELL__ >= 702+import System.IO.Unsafe(unsafePerformIO)+#else import Foreign(unsafePerformIO)+#endif import Unsafe.Coerce(unsafeCoerce) #ifdef __GLASGOW_HASKELL__
Data/Generics/UniplateOn.hs view
@@ -1,6 +1,6 @@ {-# OPTIONS_GHC -fno-warn-deprecations #-} {- |-/RECOMMENDATION/: Use "Data.Generics.Uniplate.Operations" instead.+/DEPRECATED/: Use "Data.Generics.Uniplate.Operations" instead. This module retained Haskell 98 compatability, but users who are happy with multi-parameter type classes should look towards "Data.Generics.Biplate".@@ -13,7 +13,7 @@ -} module Data.Generics.UniplateOn- {- DEPRECATED "Use Data.Generics.UniplateStrOn instead" -}+ {- DEPRECATED "Use Data.Generics.Uniplate.Operations instead" -} ( module Data.Generics.Uniplate, module Data.Generics.UniplateOn
Data/Generics/UniplateStr.hs view
@@ -1,5 +1,5 @@ {- |-/RECOMMENDATION/: Use "Data.Generics.Uniplate.Operations" instead.+/DEPRECATED/: Use "Data.Generics.Uniplate.Operations" instead. This is the main Uniplate module, which defines all the essential operations in a Haskell 98 compatible manner.@@ -13,7 +13,9 @@ -} -module Data.Generics.UniplateStr(+module Data.Generics.UniplateStr+ {- DEPRECATED "Use Data.Generics.Uniplate.Operations instead" -}+ ( module Data.Generics.UniplateStr, module Data.Generics.Str ) where
Data/Generics/UniplateStrOn.hs view
@@ -1,5 +1,5 @@ {- |-/RECOMMENDATION/: Use "Data.Generics.Uniplate.Operations" instead.+/DEPRECATED/: Use "Data.Generics.Uniplate.Operations" instead. This module retained Haskell 98 compatability, but users who are happy with multi-parameter type classes should look towards "Data.Generics.Biplate".@@ -11,7 +11,9 @@ identically to their non @On@ counterparts. -} -module Data.Generics.UniplateStrOn(+module Data.Generics.UniplateStrOn+ {- DEPRECATED "Use Data.Generics.Uniplate.Operations instead" -}+ ( module Data.Generics.UniplateStr, module Data.Generics.UniplateStrOn ) where
uniplate.cabal view
@@ -1,16 +1,16 @@-Cabal-Version: >= 1.2-Build-Type: Simple-Name: uniplate-Version: 1.6-Copyright: 2006-10, Neil Mitchell-Maintainer: ndmitchell@gmail.com-Homepage: http://community.haskell.org/~ndm/uniplate/-License: BSD3-License-File: LICENSE-Author: Neil Mitchell-Synopsis: Help writing simple, concise and fast generic operations.-Category: Generics-Description:+cabal-version: >= 1.6+build-type: Simple+name: uniplate+version: 1.6.1+author: Neil Mitchell <ndmitchell@gmail.com>+maintainer: Neil Mitchell <ndmitchell@gmail.com>+copyright: Neil Mitchell 2006-2011+homepage: http://community.haskell.org/~ndm/uniplate/+license: BSD3+license-file: LICENSE+synopsis: Help writing simple, concise and fast generic operations.+category: Generics+description: Uniplate is library for writing simple and concise generic operations. Uniplate has similar goals to the original Scrap Your Boilerplate work, but is substantially simpler and faster.@@ -39,20 +39,24 @@ * "Data.Generics.Uniplate.DataOnly" - users making use of both @Data@ and @Direct@ to avoid getting instance conflicts. -Extra-Source-Files:+extra-source-files: uniplate.htm Data/Generics/Uniplate/Internal/DataInc.hs Data/Generics/Uniplate/Internal/OperationsInc.hs -Flag ghc_6_10+source-repository head+ type: darcs+ location: http://community.haskell.org/~ndm/darcs/hlint/ -Library+flag ghc_6_10++library if flag(ghc_6_10) build-depends: base >=4 && <5, containers, syb else build-depends: base >=3 && <4, containers - Exposed-modules:+ exposed-modules: Data.Generics.Str Data.Generics.Compos Data.Generics.SYB@@ -73,10 +77,10 @@ Data.Generics.PlateTypeable Data.Generics.PlateData - Other-modules:+ other-modules: Data.Generics.PlateInternal Data.Generics.Uniplate.Internal.Data Data.Generics.Uniplate.Internal.DataOnlyOperations Data.Generics.Uniplate.Internal.Utils - Extensions: CPP+ extensions: CPP