packages feed

uniplate 1.6.3 → 1.6.4

raw patch · 5 files changed

+271/−9 lines, 5 files

Files

Data/Generics/Uniplate/Data.hs view
@@ -9,7 +9,9 @@     >             deriving (Data,Typeable)      All the Uniplate operations defined in "Data.Generics.Uniplate.Operations"-    can be used.+    can be used. If you are working with abstract data types, such as @Map@ or @Set@+    from the @containers@ package, you  may also need to use the data types defined+    in "Data.Generics.Uniplate.Data.Instances".      For faster performance (5x faster, but requires writing instances) switch to     "Data.Generics.Uniplate.Direct". If you get instance conflicts
+ Data/Generics/Uniplate/Data/Instances.hs view
@@ -0,0 +1,244 @@+{-# LANGUAGE DeriveDataTypeable #-}++-- | In some cases, 'Data' instances for abstract types are incorrect,+--   and fail to work correctly with Uniplate. This module defines three helper+--   types ('Hide', 'Trigger' and 'Invariant') to assist when writing instances+--   for abstract types. The 'Hide' type is useful when you want to mark some part+--   of your data type as being ignored by "Data.Generics.Uniplate.Data"+--   (and any other 'Data' based generics libraries, such as @syb@).+--+--   Using the helper types, this module defines wrappers for types in+--   the @containers@ package, namely 'Map', 'Set', 'IntMap' and 'IntSet'.+--   The standard @containers@ 'Data' instances all treat the types as abstract,+--   but the wrapper types allow you to traverse within the data types, ensuring+--   the necessary invariants are maintained. In particular, if you do not modify+--   the keys reconstruct will be /O(n)/ instead of /O(n log n)/.+--+--   As an example of how to implement your own abstract type wrappers, the 'Map' data+--   type is defined as:+--+-- @+--   newtype Map k v = Map ('Invariant' ('Trigger' [k], 'Trigger' [v], Hide (Map.Map k v)))+--      deriving (Data, Typeable)+-- @+--+--   The 'Map' type is defined as an 'Invariant' of three components - the keys, the values, and+--   the underlying @Map@. We use 'Invariant' to ensure that the keys/values/map always remain in sync.+--   We use 'Trigger' on the keys and values to ensure that whenever the keys or values change we+--   rebuild the @Map@, but if they don't, we reuse the previous @Map@. The 'fromMap' function is+--   implemented by pattern matching on the 'Map' type:+--+-- @+--   'fromMap' ('Map' ('Invariant' _ (_,_,'Hide' x))) = x+-- @+--+--   The 'toMap' function is slightly harder, as we need to come up with an invariant restoring function:+--+-- > toMap :: Ord k => Map.Map k v -> Map k v+-- > toMap x = Map $ Invariant inv $ create x+-- >     where+-- >         create x = (Trigger False ks, Trigger False vs, Hide x)+-- >             where (ks,vs) = unzip $ Map.toAscList x+-- > +-- >         inv (ks,vs,x)+-- >             | trigger ks = create $ Map.fromList $ zip (fromTrigger ks) (fromTrigger vs)+-- >             | trigger vs = create $ Map.fromDistinctAscList $ zip (fromTrigger ks) (fromTrigger vs)+-- >             | otherwise = (ks,vs,x)+--+--   The 'create' function creates a value from a @Map@, getting the correct keys and values. The 'inv'+--   function looks at the triggers on the keys/values. If the keys trigger has been tripped, then we+--   reconstruct the @Map@ using @fromList@. If the values trigger has been tripped, but they keys trigger+--   has not, we can use @fromDistinctAscList@, reducing the complexity of constructing the @Map@. If nothing+--   has changed we can reuse the previous value.+--+--   The end result is that all Uniplate (or @syb@) traversals over 'Map' result in a valid value, which has+--   had all appropriate transformations applied.+module Data.Generics.Uniplate.Data.Instances(+    Hide(..), Trigger(..), Invariant(..),+    Map, fromMap, toMap,+    Set, fromSet, toSet,+    IntMap, fromIntMap, toIntMap,+    IntSet, fromIntSet, toIntSet+    ) where++import Data.Data+import qualified Data.Map as Map+import qualified Data.Set as Set+import qualified Data.IntMap as IntMap+import qualified Data.IntSet as IntSet+++---------------------------------------------------------------------+-- DATA TYPES++-- | The 'Hide' data type has a 'Data' instance which reports having no constructors,+--   as though the type was defined as using the extension @EmptyDataDecls@:+--+-- > data Hide a+--+--   This type is suitable for defining regions that are avoided by Uniplate traversals.+--   As an example:+--+-- > transformBi (+1) (1, 2, Hide 3, Just 4) == (2, 3, Hide 3, Just 4)+--+--   As a result of having no constructors, any calls to the methods 'toConstr' or 'gunfoldl'+--   will raise an error.+newtype Hide a = Hide {fromHide :: a}+    deriving Typeable+++instance Typeable a => Data (Hide a) where+    gfoldl k z x = z x+    gunfold k z c = error "Data.Generics.Uniplate.Data.Instances.Hide: gunfold not implemented - data type has no constructors"+    toConstr _ = error "Data.Generics.Uniplate.Data.Instances.Hide: toConstr not implemented - data type has no constructors"+    dataTypeOf _ = tyHide++tyHide = mkDataType "Data.Generics.Uniplate.Data.Instances.Hide" []+++-- | The 'Trigger' data type has a 'Data' instance which reports as being defined:+--+-- > data Trigger a = Trigger a+--+--   However, whenever a 'gfoldl' or 'gunfold' constructs a new value, it will have the+--   'trigger' field set to 'True'. The trigger information is useful to indicate whether+--   any invariants have been broken, and thus need fixing. As an example:+--+-- > data SortedList a = SortedList (Trigger [a]) deriving (Data,Typeable)+-- > toSortedList xs = SortedList $ Trigger False $ sort xs+-- > fromSortedList (SortedList (Trigger t xs)) = if trigger x then sort xs else xs+--+--   This data type represents a sorted list. When constructed the items are initially sorted,+--   but operations such as 'gmapT' could break that invariant. The 'Trigger' type is used to+--   detect when the Data operations have been performed, and resort the list.+--+--   The 'Trigger' type is often used in conjunction with 'Invariant', which fixes the invariants.+data Trigger a = Trigger {trigger :: Bool, fromTrigger :: a}+    deriving Typeable++instance (Data a, Typeable a) => Data (Trigger a) where+    gfoldl k z (Trigger _ x) = z (Trigger True) `k` x+    gunfold k z c = k $ z $ Trigger True+    toConstr Trigger{} = conTrigger+    dataTypeOf _ = tyTrigger++conTrigger = mkConstr tyTrigger "Trigger" [] Prefix+tyTrigger = mkDataType "Data.Generics.Uniplate.Data.Instances.Trigger" [conTrigger]+++-- | The 'Invariant' data type as a 'Data' instance which reports as being defined:+--+-- > data Invariant a = Invariant a+--+--   However, whenever a 'gfoldl' constructs a new value, it will have the function in+--   the 'invariant' field applied to it. As an example:+--+-- > data SortedList a = SortedList (Invariant [a]) deriving (Data,Typeable)+-- > toSortedList xs = SortedList $ Invariant sort (sort xs)+-- > fromSortedList (SortedList (Invariant _ xs)) = xs+--+--   Any time an operation such as 'gmapT' is applied to the data type, the 'invariant' function+--   is applied to the result. The @fromSortedList@ function can then rely on this invariant.+--+--   The 'gunfold' method is partially implemented - all constructed values will have an undefined+--   value for all fields, regardless of which function is passed to 'fromConstrB'. If you only use+--   'fromConstr' (as Uniplate does) then the 'gunfold' method is sufficient.+data Invariant a = Invariant {invariant :: a -> a, fromInvariant :: a}+    deriving Typeable++instance (Data a, Typeable a) => Data (Invariant a) where+    gfoldl k z (Invariant f x) = z (Invariant f . f) `k` x+    gunfold k z c = k $ z $ \x -> Invariant (error msg) (error msg `asTypeOf` x)+        where msg = "Data.Generics.Uniplate.Data.Instances.Invariant: gunfold only partially implemented"+    toConstr Invariant{} = conInvariant+    dataTypeOf _ = tyInvariant++conInvariant = mkConstr tyInvariant "Invariant" [] Prefix+tyInvariant = mkDataType "Data.Generics.Uniplate.Data.Instances.Invariant" [conInvariant]+++---------------------------------------------------------------------+-- DATA TYPES++-- | Invariant preserving version of @Map@ from the @containers@ packages, suitable for use with 'Uniplate'.+--   Use 'toMap' to construct values, and 'fromMap' to deconstruct values.+newtype Map k v = Map (Invariant (Trigger [k], Trigger [v], Hide (Map.Map k v)))+    deriving (Data, Typeable)++-- | Deconstruct a value of type 'Map'.+fromMap :: Map k v -> Map.Map k v+fromMap (Map (Invariant _ (_,_,Hide x))) = x++-- | Construct a value of type 'Map'.+toMap :: Ord k => Map.Map k v -> Map k v+toMap x = Map $ Invariant inv $ create x+    where+        create x = (Trigger False ks, Trigger False vs, Hide x)+            where (ks,vs) = unzip $ Map.toAscList x++        inv (ks,vs,x)+            | trigger ks = create $ Map.fromList $ zip (fromTrigger ks) (fromTrigger vs)+            | trigger vs = create $ Map.fromDistinctAscList $ zip (fromTrigger ks) (fromTrigger vs) -- recreate ks/vs to reduce memory usage+            | otherwise = (ks,vs,x)+++-- | Invariant preserving version of @Set@ from the @containers@ packages, suitable for use with 'Uniplate'.+--   Use 'toSet' to construct values, and 'fromSet' to deconstruct values.+newtype Set k = Set (Invariant (Trigger [k], Hide (Set.Set k)))+    deriving (Data, Typeable)++-- | Deconstruct a value of type 'Set'.+fromSet :: Set k -> Set.Set k+fromSet (Set (Invariant _ (_,Hide x))) = x++-- | Construct a value of type 'Set'.+toSet :: Ord k => Set.Set k -> Set k+toSet x = Set $ Invariant inv $ create x+    where+        create x = (Trigger False $ Set.toList x, Hide x)++        inv (ks,x)+            | trigger ks = create $ Set.fromList $ fromTrigger ks+            | otherwise = (ks,x)+++-- | Invariant preserving version of @IntMap@ from the @containers@ packages, suitable for use with 'Uniplate'.+--   Use 'toIntMap' to construct values, and 'fromIntMap' to deconstruct values.+newtype IntMap v = IntMap (Invariant (Trigger [Int], Trigger [v], Hide (IntMap.IntMap v)))+    deriving (Data, Typeable)++-- | Deconstruct a value of type 'IntMap'.+fromIntMap :: IntMap v -> IntMap.IntMap v+fromIntMap (IntMap (Invariant _ (_,_,Hide x))) = x++-- | Construct a value of type 'IntMap'.+toIntMap :: IntMap.IntMap v -> IntMap v+toIntMap x = IntMap $ Invariant inv $ create x+    where+        create x = (Trigger False ks, Trigger False vs, Hide x)+            where (ks,vs) = unzip $ IntMap.toAscList x++        inv (ks,vs,x)+            | trigger ks = create $ IntMap.fromList $ zip (fromTrigger ks) (fromTrigger vs)+            | trigger vs = create $ IntMap.fromDistinctAscList $ zip (fromTrigger ks) (fromTrigger vs) -- recreate ks/vs to reduce memory usage+            | otherwise = (ks,vs,x)+++-- | Invariant preserving version of @IntSet@ from the @containers@ packages, suitable for use with 'Uniplate'.+--   Use 'toIntSet' to construct values, and 'fromIntSet' to deconstruct values.+newtype IntSet = IntSet (Invariant (Trigger [Int], Hide (IntSet.IntSet)))+    deriving (Data, Typeable)++-- | Deconstruct a value of type 'IntSet'.+fromIntSet :: IntSet -> IntSet.IntSet+fromIntSet (IntSet (Invariant _ (_,Hide x))) = x++-- | Construct a value of type 'IntSet'.+toIntSet :: IntSet.IntSet -> IntSet+toIntSet x = IntSet $ Invariant inv $ create x+    where+        create x = (Trigger False $ IntSet.toList x, Hide x)++        inv (ks,x)+            | trigger ks = create $ IntSet.fromList $ fromTrigger ks+            | otherwise = (ks,x)
Data/Generics/Uniplate/Direct.hs view
@@ -31,9 +31,20 @@     >     biplate (Seq x  ) = plate Seq ||+ x     >     biplate (Sel x  ) = plate Sel ||* x     >     biplate (Let x y) = plate Let |-  x |* y--}-     +    To define instances for abstract data types, such as @Map@ or @Set@ from the @containers@ package,+    use 'plateProject'.++    This module provides a few monomorphic instances of 'Uniplate' / 'Biplate'+    for common types available in the base library, but does not provide any polymorphic+    instances. Given only monomorphic instances it is trivial to ensure that all instances+    are disjoint, making it easier to add your own instances.++    When defining polymorphic instances, be carefully to mention all potential children.+    Consider @Biplate Int (Int, a)@ - this instance cannot be correct because it will fail+    to return both @Int@ values on @(Int,Int)@. There are some legitimate polymorphic instances,+    such as @Biplate a [a]@ and @Biplate a a@, but take care to avoid overlapping instances.+-} module Data.Generics.Uniplate.Direct(     module Data.Generics.Uniplate.Operations,     -- * The Combinators@@ -101,11 +112,14 @@ -- | Write an instance in terms of a projection/injection pair. Usually used to define instances --   for abstract containers such as Map: --+-- > instance Biplate (Map.Map [Char] Int) Char where+-- >     biplate = plateProject Map.toList Map.fromList+--+--   If the types ensure that no operations will not change the keys we+--   we can use the 'fromDistictAscList' function to reconstruct the Map:+-- -- > instance Biplate (Map.Map [Char] Int) Int where -- >     biplate = plateProject Map.toAscList Map.fromDistinctAscList------ Because we know that Uniplate operations will not change the keys,--- we can use the 'fromDistictAscList' function to reconstruct it. plateProject :: Biplate item to => (from -> item) -> (item -> from) -> from -> Type from to plateProject into outof = second (outof . ) . biplate . into 
Data/Generics/Uniplate/Internal/Data.hs view
@@ -107,7 +107,7 @@   {-# NOINLINE uniplateVerbose #-}-uniplateVerbose :: Int -- 0 = quiet, 1 = errors only, 2 = everything+uniplateVerbose :: Int -- -1 = error if failed, 0 = quiet, 1 = print errors only, 2 = print everything uniplateVerbose = unsafePerformIO $ do     fmap read (getEnv "UNIPLATE_VERBOSE") `Control.Exception.catch` \(_ :: SomeException) -> return 0 
uniplate.cabal view
@@ -1,7 +1,7 @@ cabal-version:      >= 1.6 build-type:         Simple name:               uniplate-version:            1.6.3+version:            1.6.4 author:             Neil Mitchell <ndmitchell@gmail.com> maintainer:         Neil Mitchell <ndmitchell@gmail.com> copyright:          Neil Mitchell 2006-2011@@ -13,7 +13,8 @@ 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.+    but is substantially simpler and faster. The Uniplate manual is available at+    <http://community.haskell.org/~ndm/darcs/uniplate/uniplate.htm>.     .     To get started with Uniplate you should import one of the three following     modules:@@ -68,6 +69,7 @@         Data.Generics.Compos         Data.Generics.SYB         Data.Generics.Uniplate.Data+        Data.Generics.Uniplate.Data.Instances         Data.Generics.Uniplate.DataOnly         Data.Generics.Uniplate.Direct         Data.Generics.Uniplate.Operations