packages feed

fixplate 0.1.1 → 0.1.2

raw patch · 8 files changed

+327/−12 lines, 8 filesdep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Data.Generics.Fixplate.Attributes: inheritM :: (Traversable f, Monad m) => (Mu f -> a -> m a) -> a -> Mu f -> m (Attr f a)
+ Data.Generics.Fixplate.Attributes: inheritM_ :: (Traversable f, Monad m) => (Mu f -> a -> m a) -> a -> Mu f -> m ()
+ Data.Generics.Fixplate.Attributes: synthRewrite :: Traversable f => (f a -> a) -> (Attr f a -> Maybe (f (Attr f a))) -> Attr f a -> Attr f a
+ Data.Generics.Fixplate.Attributes: synthTransform :: Traversable f => (f a -> a) -> (Attr f a -> Maybe (f (Attr f a))) -> Attr f a -> Attr f a
+ Data.Generics.Fixplate.Attributes: topDownSweepM :: (Traversable f, Monad m) => (f () -> a -> m (f a)) -> a -> Mu f -> m ()
+ Data.Generics.Fixplate.Attributes: topDownSweepM' :: (Traversable f, Monad m) => (b -> f b -> a -> m (f a)) -> a -> Attr f b -> m ()
+ Data.Generics.Fixplate.Open: data Shape f
+ Data.Generics.Fixplate.Open: instance EqF f => Eq (Shape f)
+ Data.Generics.Fixplate.Open: instance OrdF f => Ord (Shape f)
+ Data.Generics.Fixplate.Open: instance ShowF f => Show (Shape f)
+ Data.Generics.Fixplate.Open: project :: Foldable f => Int -> f a -> Maybe a
+ Data.Generics.Fixplate.Open: shape :: Functor f => f a -> Shape f
+ Data.Generics.Fixplate.Open: sizeF :: Foldable f => f a -> Int
+ Data.Generics.Fixplate.Open: unsafeProject :: Foldable f => Int -> f a -> a
+ Data.Generics.Fixplate.Open: unsafeZipWithF :: Traversable f => (a -> b -> c) -> f a -> f b -> f c
+ Data.Generics.Fixplate.Open: unsafeZipWithFM :: (Traversable f, Monad m) => (a -> b -> m c) -> f a -> f b -> m (f c)
+ Data.Generics.Fixplate.Open: unzipF :: Functor f => f (a, b) -> (f a, f b)
+ Data.Generics.Fixplate.Open: zipF :: (Traversable f, EqF f) => f a -> f b -> Maybe (f (a, b))
+ Data.Generics.Fixplate.Open: zipWithF :: (Traversable f, EqF f) => (a -> b -> c) -> f a -> f b -> Maybe (f c)
+ Data.Generics.Fixplate.Open: zipWithFM :: (Traversable f, EqF f, Monad m) => (a -> b -> m c) -> f a -> f b -> m (Maybe (f c))
+ Data.Generics.Fixplate.Zipper: fullPathDown :: Foldable f => Loc f -> [Int]
+ Data.Generics.Fixplate.Zipper: fullPathUp :: Foldable f => Loc f -> [Int]
+ Data.Generics.Fixplate.Zipper: horizontalPos :: Foldable f => Loc f -> Int
- Data.Generics.Fixplate: class Foldable t :: (* -> *)
+ Data.Generics.Fixplate: class Foldable (t :: * -> *)
- Data.Generics.Fixplate: class Functor f :: (* -> *)
+ Data.Generics.Fixplate: class Functor (f :: * -> *)
- Data.Generics.Fixplate: class (Functor t, Foldable t) => Traversable t :: (* -> *)
+ Data.Generics.Fixplate: class (Functor t, Foldable t) => Traversable (t :: * -> *)

Files

Data/Generics/Fixplate.hs view
@@ -14,7 +14,7 @@ --
 -- The main disadvantage is that it does not work well for
 -- mutually recursive data types, and that pattern matching becomes
--- more tedious (but there are solutions for the latter).
+-- more tedious (but there are partial solutions for the latter).
 --
 -- Consider as an example the following simple expression language,
 -- encoded by a recursive algebraic data type:
@@ -42,7 +42,7 @@ -- The functions in this library work on types like that: 'Mu'@ f@,
 -- where @f@ is a functor, and sometimes explicitely on 'Attr'@ f a@.
 -- 
--- This module re-exports all the functionality present in the library.
+-- This module re-exports most of the functionality present in the library.
 --
 -- The library should be fully Haskell98 compatible, with the exception
 -- of the module "Data.Generics.Fixplate.Structure", which needs
@@ -54,7 +54,7 @@   , module Data.Generics.Fixplate.Traversals
   , module Data.Generics.Fixplate.Morphisms
   , module Data.Generics.Fixplate.Attributes
-  , module Data.Generics.Fixplate.Zipper
+--  , module Data.Generics.Fixplate.Zipper
   , module Data.Generics.Fixplate.Structure
   , Functor(..) , Foldable(..) , Traversable(..)
   )
@@ -66,7 +66,7 @@ import Data.Generics.Fixplate.Traversals
 import Data.Generics.Fixplate.Morphisms
 import Data.Generics.Fixplate.Attributes
-import Data.Generics.Fixplate.Zipper
+-- import Data.Generics.Fixplate.Zipper
 import Data.Generics.Fixplate.Structure  
 
 import Data.Foldable
Data/Generics/Fixplate/Attributes.hs view
@@ -9,10 +9,16 @@   , synthetise , synthetise' , synthetiseList , synthetiseM
   -- * Inherited attributes
   , inherit , inherit'
+  , inheritM , inheritM_
+  -- * Top-down folds
+  , topDownSweepM , topDownSweepM'
   -- * Traversals
   , synthAccumL    , synthAccumR
   , synthAccumL_   , synthAccumR_
   , enumerateNodes , enumerateNodes_
+  -- * Resynthetising transformations
+  , synthTransform
+  , synthRewrite
   -- * Stacking attributes
   , annZip  , annZipWith
   , annZip3 , annZipWith3  
@@ -31,9 +37,10 @@ import Control.Monad (liftM)
 import Data.Foldable
 import Data.Traversable
-import Prelude hiding (foldl,foldr,mapM,mapM_,concat,concatMap,sum)
+import Prelude hiding (foldl,foldr,mapM,mapM_,concat,concatMap,sum,and,or)
 
 import Data.Generics.Fixplate.Base
+import Data.Generics.Fixplate.Open
 
 #ifdef WITH_QUICKCHECK
 import Test.QuickCheck
@@ -77,6 +84,7 @@ synthetiseList :: (Functor f, Foldable f) => ([a] -> a) -> Mu f -> Attr f a
 synthetiseList h = synthetise (h . toList)
 
+-- | Monadic version of 'synthetise'.
 synthetiseM  ::  (Traversable f, Monad m) =>  (f a -> m a) -> Mu f -> m (Attr f a)
 synthetiseM act = go where
   go (Fix x) = do
@@ -103,7 +111,43 @@ inherit' h root = go root where
   go p (Fix (Ann a t)) = let b = h p a in Fix (Ann b (fmap (go b) t))
 
+-- | Monadic version of 'inherit'.
+inheritM :: (Traversable f, Monad m) => (Mu f -> a -> m a) -> a -> Mu f -> m (Attr f a)
+inheritM act root = go root where
+  go p s@(Fix t) = do
+    a <- act s p 
+    u <- mapM (go a) t
+    return (Fix (Ann a u))
+
+inheritM_ :: (Traversable f, Monad m) => (Mu f -> a -> m a) -> a -> Mu f -> m ()
+inheritM_ act root = go root where
+  go p s@(Fix t) = do
+    a <- act s p 
+    _ <- mapM (go a) t
+    return ()
+
 --------------------------------------------------------------------------------
+-- Top-down folds
+
+-- | Monadic top-down \"sweep\" of a tree. It's kind of a more complicated folding version of 'inheritM'.
+-- This is unsafe in the sense that the user is responsible to retain the shape of the node.
+-- TODO: better name?
+topDownSweepM :: (Traversable f, Monad m) => (f () -> a -> m (f a)) -> a -> Mu f -> m ()
+topDownSweepM act root = go root where
+  go p (Fix t) = do
+    s <- act (fmap (const ()) t) p 
+    _ <- unsafeZipWithFM go s t
+    return ()
+
+-- | An attributed version of 'topDownSweepM'. Probably more useful.
+topDownSweepM' :: (Traversable f, Monad m) => (b -> f b -> a -> m (f a)) -> a -> Attr f b -> m ()
+topDownSweepM' act root = go root where
+  go p (Fix (Ann u t)) = do
+    s <- act u (fmap attribute t) p 
+    _ <- unsafeZipWithFM go s t
+    return ()
+
+--------------------------------------------------------------------------------
 -- Traversals
 
 -- | Synthetising attributes via an accumulating map in a left-to-right fashion
@@ -140,6 +184,46 @@ enumerateNodes_ :: Traversable f => Mu f -> Attr f Int
 enumerateNodes_ = snd . enumerateNodes
 
+--------------------------------------------------------------------------------
+-- Resynthetising transformations
+
+-- | Bottom-up transformations which automatically resynthetise attributes 
+-- in case of changes.
+synthTransform :: Traversable f => (f a -> a) -> (Attr f a -> Maybe (f (Attr f a))) -> Attr f a -> Attr f a 
+synthTransform calc h0 = snd . go False where
+  synth x = Fix $ Ann (calc $ fmap attribute x) x
+  hsynth x = case h0 (synth x) of 
+    Nothing -> Nothing
+    Just y  -> Just (synth y)
+  go changed0 old@(Fix (Ann _ x)) = 
+    let (changed1,y) = mapAccumL go changed0 x
+        new = case hsynth y of
+          Nothing -> (changed1,w) where
+            w = if changed1
+              then synth y
+              else old
+          Just z -> (True, z)
+    in  new
+
+-- | Bottom-up transformations to normal form (applying transformation exhaustively)
+-- which automatically resynthetise attributes in case of changes.
+synthRewrite :: Traversable f => (f a -> a) -> (Attr f a -> Maybe (f (Attr f a))) -> Attr f a -> Attr f a 
+synthRewrite calc h0 = rewrite where
+  rewrite = snd . go False 
+  synth x = Fix $ Ann (calc $ fmap attribute x) x
+  hsynth x = case h0 (synth x) of 
+    Nothing -> Nothing
+    Just y  -> Just (synth y)
+  go changed0 old@(Fix (Ann _ x)) = 
+    let (changed1,y) = mapAccumL go changed0 x
+        new = case hsynth y of
+          Nothing -> (changed1,w) where
+            w = if changed1
+              then synth y
+              else old
+          Just z -> (True, rewrite z)
+    in  new
+    
 --------------------------------------------------------------------------------
 -- Stacking attributes
 
Data/Generics/Fixplate/Misc.hs view
@@ -4,7 +4,10 @@ 
 --------------------------------------------------------------------------------
 
+import Prelude hiding (mapM,mapM_)
+import Control.Monad (liftM)
 import Data.Traversable
+--import Control.Monad.Trans.State
 
 --------------------------------------------------------------------------------
         
@@ -58,5 +61,38 @@ iterateN n f = go n where 
   go 0 x = x
   go n x = go (n-1) (f x)
+
+--------------------------------------------------------------------------------  
+
+mapM_ :: (Traversable t, Monad m) => (a -> m ()) -> t a -> m ()
+mapM_ act t = do
+  _ <- mapM act t
+  return ()
+
+mapAccumM :: (Traversable t, Monad m) => (a -> b -> m (a, c)) -> a -> t b -> m (a, t c)
+mapAccumM act x0 t = runStateT (mapM (StateT . flip act) t) x0 where
+
+--------------------------------------------------------------------------------  
+
+newtype StateT s m a = StateT { runStateT :: s -> m (s,a) }
+
+instance (Monad m) => Monad (StateT s m) where
+  return a = state $ \s -> (s,a)
+  m >>= k  = StateT $ \s -> do
+    ~(s', a) <- runStateT m s
+    runStateT (k a) s'
+  fail str = StateT $ \_ -> fail str
+
+state :: Monad m => (s -> (s,a)) -> StateT s m a 
+state f = StateT (return . f)
+
+sget :: (Monad m) => StateT s m s
+sget = state $ \s -> (s,s)
+
+sput :: (Monad m) => s -> StateT s m ()
+sput s = state $ \_ -> (s,())
+
+smodify :: (Monad m) => (s -> s) -> StateT s m ()
+smodify f = state $ \s -> (f s,())
 
 --------------------------------------------------------------------------------  
Data/Generics/Fixplate/Open.hs view
@@ -9,12 +9,20 @@   , mapAccumL  , mapAccumR
   , mapAccumL_ , mapAccumR_
   -- * Open functions
-  , holes , holesList
-  , apply , builder
+  , holes   , holesList
+  , apply   , builder
+  -- * Individual elements
+  , project , unsafeProject
+  , sizeF
   -- * Enumerations
   , enumerate
   , enumerateWith
   , enumerateWith_
+  -- * Zips
+  , Shape , shape
+  , zipF , unzipF
+  , zipWithF  , unsafeZipWithF
+  , zipWithFM , unsafeZipWithFM
   )
 where
 
@@ -56,16 +64,46 @@   g (x,replace) = replace (f x)
 
 -- | Builds up a structure from a list of the children.
+-- It is unsafe in the sense that it will throw an exception
+-- if there are not enough elements in the list.
 builder :: Traversable f => f a -> [b] -> f b
 builder tree xs = mapAccumL_ g xs tree where
   g (x:xs) _ = (xs,x)
 
 --------------------------------------------------------------------------------
+
+-- | Extracts the ith child.
+project :: Foldable f => Int -> f a -> Maybe a
+project i tree = 
+  case foldl f (Left 0) tree of
+    Right x -> Just x
+    Left  _ -> Nothing
+  where
+    f (Left j) x = if i==j then Right x else Left (j+1)
+    f old      _ = old
+    
+unsafeProject :: Foldable f => Int -> f a -> a
+unsafeProject i tree = 
+  case foldl f (Left 0) tree of
+    Right x -> x
+    Left  _ -> error "unsafePoject: invalid index"
+  where
+    f (Left j) x = if i==j then Right x else Left (j+1)
+    f old      _ = old
+
+-- | Number of children. This is the generalization of 'length' to foldable functors:
+--
+-- > sizeF x = length (toList x)
+--
+sizeF :: Foldable f => f a -> Int
+sizeF = foldl (\i _ -> i+1) 0  
+
+--------------------------------------------------------------------------------
 -- Enumerations
 
 -- | Enumerates children from the left to the right, starting with zero.
 -- Also returns the number of children. This is just a simple application
--- of @mapAccumL@.
+-- of 'mapAccumL'.
 enumerate :: Traversable f => f a -> (Int, f (Int, a))
 enumerate = mapAccumL (\i x -> (i+1,(i,x))) 0
 
@@ -76,3 +114,58 @@ enumerateWith_ h = snd . enumerateWith h
   
 --------------------------------------------------------------------------------
+-- Shapes
+
+-- | A type encoding the \"shape\" of the functor data:
+-- We ignore all the fields whose type is the parameter type,
+-- but remember the rest:
+--
+-- > newtype Shape f = Shape { unShape :: f () }
+--
+-- This can be used to decide whether two realizations are compatible.
+newtype Shape f = Shape { unShape :: f () }
+
+-- | Extracting the \"shape\" of the functor
+shape :: Functor f => f a -> Shape f
+shape = Shape . fmap (const ())
+
+instance EqF   f => Eq   (Shape f) where x == y        = equalF       (unShape x) (unShape y)
+instance OrdF  f => Ord  (Shape f) where compare x y   = compareF     (unShape x) (unShape y)
+instance ShowF f => Show (Shape f) where showsPrec d x = showsPrecF d (unShape x)
+
+-- | Zips two structures if they are compatible.
+zipF :: (Traversable f, EqF f) => f a -> f b -> Maybe (f (a,b))
+zipF = zipWithF (,)
+
+unzipF :: Functor f => f (a,b) -> (f a, f b)
+unzipF t = (fmap fst t, fmap snd t)
+
+-- | Zipping two structures using a function.
+zipWithF :: (Traversable f, EqF f) => (a -> b -> c) -> f a -> f b -> Maybe (f c)
+zipWithF f x y = 
+  if shape x == shape y
+    then Just (unsafeZipWithF f x y)
+    else Nothing
+
+-- | Unsafe version of 'zipWithF': does not check if the two structures are compatible.
+-- It is left-biased in the sense that the structure of the second argument is retained.
+unsafeZipWithF :: Traversable f => (a -> b -> c) -> f a -> f b -> f c
+unsafeZipWithF f x y = z where
+  z = mapAccumL_ g (toList y) x
+  g (b:bs) a = (bs, f a b)
+
+--------------------------------------------------------------------------------
+
+-- | Monadic version of 'zipWithF'. TODO: better name?
+zipWithFM :: (Traversable f, EqF f, Monad m) => (a -> b -> m c) -> f a -> f b -> m (Maybe (f c))
+zipWithFM f x y = 
+  if shape x == shape y
+    then liftM Just (unsafeZipWithFM f x y) 
+    else return Nothing
+
+unsafeZipWithFM :: (Traversable f, Monad m) => (a -> b -> m c) -> f a -> f b -> m (f c)
+unsafeZipWithFM f x y = liftM snd $ mapAccumM g (toList y) x where
+  g (b:bs) a = f a b >>= \r -> return (bs, r)
+
+--------------------------------------------------------------------------------
+
Data/Generics/Fixplate/Test/Tools.hs view
@@ -54,6 +54,31 @@ attrTreeF x s = Fix . Ann x . TreeF s
 
 --------------------------------------------------------------------------------
+-- * draw trees
+
+printTree :: Tree Label -> IO ()
+printTree = printTree' (\(Label s) -> s)
+
+printTreeF :: FixT Label -> IO ()
+printTreeF = printTreeF' (\(Label s) -> s)
+
+printTree' :: (a -> String) -> Tree a -> IO ()
+printTree' h = go 0 where
+  go i (Tree label children) = do
+    putStrLn $ if i>0 
+      then concat (replicate (i-1) "| " ++ ["|-", h label])
+      else h label
+    mapM_ (go (i+1)) children
+
+printTreeF' :: (a -> String) -> Mu (TreeF a) -> IO ()
+printTreeF' h = go 0 where
+  go i (Fix (TreeF label children)) = do
+    putStrLn $ if i>0 
+      then concat (replicate (i-1) "| " ++ ["|-", h label])
+      else h label
+    mapM_ (go (i+1)) children
+    
+--------------------------------------------------------------------------------
 -- * random trees
 
 rndTree :: IO (Tree Label)
Data/Generics/Fixplate/Traversals.hs view
@@ -50,7 +50,7 @@   go (Fix x) = do 
     y <- mapM go x
     action (Fix y)
-
+    
 -- | Top-down transformation. This provided only for completeness;
 -- usually, it is 'transform' what you want use instead.
 topDownTransform :: Functor f => (Mu f -> Mu f) -> Mu f -> Mu f 
Data/Generics/Fixplate/Zipper.hs view
@@ -265,6 +265,55 @@ isRightmost = isNothing . moveRight
 
 --------------------------------------------------------------------------------
+-- * Location queries
+
+-- | Gives back the index of the given location among the children of its parent.
+-- Indexing starts from zero. In case of root node (no parent), we also return zero.
+horizontalPos :: Foldable f => Loc f -> Int
+horizontalPos (Loc _ path) = case path of
+  Top        -> 0
+  Path nodes -> 
+    case mpos of
+      Right pos -> pos
+      Left _    -> error "horizontalPos: shouldn't happen"
+    where
+      mpos = foldl g (Left 0) nodes 
+      g old ei = case old of
+        Right _ -> old
+        Left  j -> case ei of
+          Left _  -> Left (j+1)
+          Right _ -> Right j
+
+-- | We return the full path from the root as a sequence of child indices.
+-- This means that
+-- 
+-- > loc == foldl (flip unsafeMoveDown) (moveTop loc) (fullPathDown loc)
+--
+fullPathDown :: Foldable f => Loc f -> [Int]
+fullPathDown = reverse . fullPathUp
+
+-- | The following equations hold for 'fullPathUp' and 'fullPathDown':
+-- 
+-- > fullPathUp == reverse . fullPathDown
+-- > loc == foldr unsafeMoveDown (moveTop loc) (fullPathUp loc)
+--
+fullPathUp :: Foldable f => Loc f -> [Int]
+fullPathUp (Loc _ pth) = go pth where    
+  go path = case path of
+    Top        -> []
+    Path nodes -> 
+      case mpos of
+        Right (pos,parent) -> pos : go parent
+        Left _             -> error "fullPathUp: shouldn't happen"
+      where
+        mpos = foldl g (Left 0) nodes 
+        g old ei = case old of
+          Right _ -> old
+          Left  j -> case ei of
+            Left _  -> Left (j+1)
+            Right p -> Right (j,p)    
+
+--------------------------------------------------------------------------------
 -- * Compound movements
   
 -- | Moves to the top, by repeatedly moving up.
@@ -434,6 +483,10 @@   quickCheck prop_contextList
   quickCheck prop_Top
   quickCheck prop_defocus
+  quickCheck prop_horizontalPos
+  quickCheck prop_fullPathDown
+  quickCheck prop_fullPathUp
+  quickCheck prop_fullPathUp2
   quickCheck prop_leftmost
   quickCheck prop_rightmost
   quickCheck prop_DownLUp
@@ -454,6 +507,12 @@ rightmostNaive :: Traversable f => Loc f -> Loc f
 rightmostNaive = tillNothing moveRight
 
+fullPathUpNaive :: Traversable f => Loc f -> [Int]
+fullPathUpNaive = go where
+  go loc@(Loc _ path) = case path of
+    Top -> []
+    _   -> horizontalPos loc : go (unsafeMoveUp loc)
+
 ----------------------------------------
 
 prop_ReadShowLoc :: LocT Label -> Bool
@@ -489,6 +548,24 @@     top = root numbered
     (n,numbered) = enumerateNodes tree
 
+----------------------------------------
+
+prop_horizontalPos :: LocT Label -> Bool
+prop_horizontalPos loc = 
+  loc == iterateN (horizontalPos loc) unsafeMoveRight (leftmost loc)
+
+prop_fullPathDown :: LocT Label -> Bool
+prop_fullPathDown loc = 
+  loc == foldl (flip unsafeMoveDown) (moveTop loc) (fullPathDown loc)
+
+prop_fullPathUp :: LocT Label -> Bool
+prop_fullPathUp loc = 
+  fullPathUp loc == fullPathUpNaive loc
+
+prop_fullPathUp2 :: LocT Label -> Bool 
+prop_fullPathUp2 loc = 
+  loc == foldr unsafeMoveDown (moveTop loc) (fullPathUp loc)  
+  
 ----------------------------------------
     
 prop_leftmost :: LocT Label -> Bool
fixplate.cabal view
@@ -1,6 +1,6 @@  Name:                fixplate-Version:             0.1.1+Version:             0.1.2 Synopsis:            Uniplate-style generic traversals for fixed-point types, with some extras. Description:         Uniplate-style generic traversals for fixed-point types, which can be                        optionally annotated with attributes. We also provide a generic zipper.@@ -9,7 +9,7 @@ License:             BSD3 License-file:        LICENSE Author:              Balazs Komuves-Copyright:           (c) 2011 Balazs Komuves+Copyright:           (c) 2011-2012 Balazs Komuves Maintainer:          bkomuves (plus) hackage (at) gmail (dot) com Homepage:            http://code.haskell.org/~bkomuves/ Stability:           Experimental@@ -64,6 +64,6 @@    Hs-Source-Dirs:      . -  ghc-options:         -Wall -fno-warn-unused-matches+  ghc-options:         -Wall -fno-warn-unused-matches -fno-warn-name-shadowing