packages feed

fixplate 0.1 → 0.1.1

raw patch · 10 files changed

+381/−171 lines, 10 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Data.Generics.Fixplate.Attributes: numberNodes :: Traversable f => Mu f -> (Int, Attr f Int)
- Data.Generics.Fixplate.Attributes: numberNodes_ :: Traversable f => Mu f -> Attr f Int
- Data.Generics.Fixplate.Attributes: toList :: Foldable t => t a -> [a]
- Data.Generics.Fixplate.Traversals: apply :: Traversable f => (a -> a) -> f a -> f (f a)
- Data.Generics.Fixplate.Traversals: builder :: Traversable f => f a -> [b] -> f b
- Data.Generics.Fixplate.Traversals: holes :: Traversable f => f a -> f (a, a -> f a)
- Data.Generics.Fixplate.Traversals: holesList :: Traversable f => f a -> [(a, a -> f a)]
+ Data.Generics.Fixplate.Attributes: annMap :: Functor f => (a -> b) -> Attr f a -> Attr f b
+ Data.Generics.Fixplate.Attributes: annZip :: Functor f => Mu (Ann (Ann f a) b) -> Attr f (a, b)
+ Data.Generics.Fixplate.Attributes: annZip3 :: Functor f => Mu (Ann (Ann (Ann f a) b) c) -> Attr f (a, b, c)
+ Data.Generics.Fixplate.Attributes: annZipWith :: Functor f => (a -> b -> c) -> Mu (Ann (Ann f a) b) -> Attr f c
+ Data.Generics.Fixplate.Attributes: annZipWith3 :: Functor f => (a -> b -> c -> d) -> Mu (Ann (Ann (Ann f a) b) c) -> Attr f d
+ Data.Generics.Fixplate.Attributes: enumerateNodes :: Traversable f => Mu f -> (Int, Attr f Int)
+ Data.Generics.Fixplate.Attributes: enumerateNodes_ :: Traversable f => Mu f -> Attr f Int
+ Data.Generics.Fixplate.Open: apply :: Traversable f => (a -> a) -> f a -> f (f a)
+ Data.Generics.Fixplate.Open: builder :: Traversable f => f a -> [b] -> f b
+ Data.Generics.Fixplate.Open: enumerate :: Traversable f => f a -> (Int, f (Int, a))
+ Data.Generics.Fixplate.Open: enumerateWith :: Traversable f => (Int -> a -> b) -> f a -> (Int, f b)
+ Data.Generics.Fixplate.Open: enumerateWith_ :: Traversable f => (Int -> a -> b) -> f a -> f b
+ Data.Generics.Fixplate.Open: holes :: Traversable f => f a -> f (a, a -> f a)
+ Data.Generics.Fixplate.Open: holesList :: Traversable f => f a -> [(a, a -> f a)]
+ Data.Generics.Fixplate.Open: mapAccumL :: Traversable t => (a -> b -> (a, c)) -> a -> t b -> (a, t c)
+ Data.Generics.Fixplate.Open: mapAccumL_ :: Traversable f => (a -> b -> (a, c)) -> a -> f b -> f c
+ Data.Generics.Fixplate.Open: mapAccumR :: Traversable t => (a -> b -> (a, c)) -> a -> t b -> (a, t c)
+ Data.Generics.Fixplate.Open: mapAccumR_ :: Traversable f => (a -> b -> (a, c)) -> a -> f b -> f c
+ Data.Generics.Fixplate.Open: toList :: Foldable t => t a -> [a]
+ Data.Generics.Fixplate.Traversals: foldLeftLazy :: Foldable f => (a -> Mu f -> a) -> a -> Mu f -> a
+ Data.Generics.Fixplate.Zipper: locations :: Traversable f => Mu f -> Attr f (Loc f)
+ Data.Generics.Fixplate.Zipper: locationsList :: Traversable f => Mu f -> [Loc f]

Files

Data/Generics/Fixplate.hs view
@@ -44,10 +44,10 @@ -- 
 -- This module re-exports all the functionality present in the library.
 --
--- The library is fully Haskell98 compatible, with the exception
+-- The library should be fully Haskell98 compatible, with the exception
 -- of the module "Data.Generics.Fixplate.Structure", which needs
--- the @Rank2Types@ extension. For compatibility, this functionality
--- of this module is at the moment only provided when compiled with GHC.
+-- the @Rank2Types@ extension. For compatibility, the functionality
+-- of this module is at the moment only provided when compiled with GHC or Hugs.
 --
 module Data.Generics.Fixplate 
   ( module Data.Generics.Fixplate.Base
@@ -73,4 +73,3 @@ import Data.Traversable
 
 --------------------------------------------------------------------------------
-
Data/Generics/Fixplate/Attributes.hs view
@@ -1,18 +1,21 @@ 
--- | Synthetising attributes, motivated by Attribute Grammars.
+-- | Synthetising attributes, partly motivated by Attribute Grammars.
 
 {-# LANGUAGE CPP #-}
 module Data.Generics.Fixplate.Attributes
-  ( toList
-  , Attrib(..)
+  ( Attrib(..)
+  , annMap
   -- * Synthetised attributes
   , synthetise , synthetise' , synthetiseList , synthetiseM
   -- * Inherited attributes
   , inherit , inherit'
   -- * Traversals
-  , synthAccumL  , synthAccumR
-  , synthAccumL_ , synthAccumR_
-  , numberNodes  , numberNodes_
+  , synthAccumL    , synthAccumR
+  , synthAccumL_   , synthAccumR_
+  , enumerateNodes , enumerateNodes_
+  -- * Stacking attributes
+  , annZip  , annZipWith
+  , annZip3 , annZipWith3  
 #ifdef WITH_QUICKCHECK
   -- * Tests
   , runtests_Attributes  
@@ -39,6 +42,14 @@ #endif 
 
 --------------------------------------------------------------------------------
+
+-- | Map over annotations 
+-- 
+-- > annMap f = unAttrib . fmap f . Attrib
+annMap :: Functor f => (a -> b) -> Attr f a -> Attr f b
+annMap h = unAttrib . fmap h . Attrib
+
+--------------------------------------------------------------------------------
 -- Synthetised attributes
 
 -- | /Synthetised/ attributes are created in a bottom-up manner. 
@@ -123,11 +134,30 @@ -- a left-to-right traversal fashion, where
 -- @n == length (universe tree)@ is the number of substructures,
 -- which is also returned.
-numberNodes :: Traversable f => Mu f -> (Int, Attr f Int)
-numberNodes tree = synthAccumL (\i _ -> (i+1,i)) 0 tree
+enumerateNodes :: Traversable f => Mu f -> (Int, Attr f Int)
+enumerateNodes tree = synthAccumL (\i _ -> (i+1,i)) 0 tree
 
-numberNodes_ :: Traversable f => Mu f -> Attr f Int
-numberNodes_ = snd . numberNodes
+enumerateNodes_ :: Traversable f => Mu f -> Attr f Int
+enumerateNodes_ = snd . enumerateNodes
+
+--------------------------------------------------------------------------------
+-- Stacking attributes
+
+-- | Merges two layers of annotations into a single one.
+annZip :: Functor f => Mu (Ann (Ann f a) b) -> Attr f (a,b)
+annZip (Fix (Ann y (Ann x t))) = Fix (Ann (x,y) (fmap annZip t))
+
+annZipWith :: Functor f => (a -> b -> c) -> Mu (Ann (Ann f a) b) -> Attr f c
+annZipWith h = go where 
+  go (Fix (Ann y (Ann x t))) = Fix (Ann (h x y) (fmap go t))
+
+-- | Merges three layers of annotations into a single one.
+annZip3 :: Functor f => Mu (Ann (Ann (Ann f a) b) c) -> Attr f (a,b,c)
+annZip3 (Fix (Ann z (Ann y (Ann x t)))) = Fix (Ann (x,y,z) (fmap annZip3 t))
+
+annZipWith3 :: Functor f => (a -> b -> c -> d) -> Mu (Ann (Ann (Ann f a) b) c) -> Attr f d
+annZipWith3 h = go where 
+  go (Fix (Ann z (Ann y (Ann x t)))) = Fix (Ann (h x y z) (fmap go t))
 
 --------------------------------------------------------------------------------
 -- Tests
Data/Generics/Fixplate/Base.hs view
@@ -41,7 +41,7 @@ --------------------------------------------------------------------------------
 
 -- | \"Functorised\" versions of standard type classes. 
--- If you have your own structure functor, for example
+-- If you have your a structure functor, for example
 --
 -- > Expr e 
 -- >   = Kst Int 
Data/Generics/Fixplate/Misc.hs view
@@ -7,16 +7,18 @@ import Data.Traversable
 
 --------------------------------------------------------------------------------
-
-mapAccumL_ :: Traversable f => (a -> b -> (a, c)) -> a -> f b -> f c
-mapAccumL_ f x t = snd (mapAccumL f x t)
-
---------------------------------------------------------------------------------
         
 data Two a b 
   =  Empty 
   |  One a
   |  Two b
+  deriving Show
+  
+data Both a b 
+  =  None
+  |  First a
+  |  Both  a b
+  deriving Show
 
 --------------------------------------------------------------------------------
 
Data/Generics/Fixplate/Morphisms.hs view
@@ -39,7 +39,8 @@ -- | An /anamorphism/ is simply an unfold.
 ana :: Functor f => (a -> f a) -> a -> Mu f
 ana h = go where
-  go x = Fix (fmap go (h x))
+  go = Fix . fmap go . h
+  -- go x = Fix (fmap go (h x))
 
 -- | A /hylomorphism/ is the composition of a catamorphism and an anamorphism.
 hylo :: Functor f => (f a -> a) -> (b -> f b) -> (b -> a) 
+ Data/Generics/Fixplate/Open.hs view
@@ -0,0 +1,78 @@+
+-- | \"Open\" functions, working on functors instead of trees.
+
+{-# LANGUAGE CPP #-}
+module Data.Generics.Fixplate.Open 
+  ( 
+    toList
+  -- * Accumulating maps
+  , mapAccumL  , mapAccumR
+  , mapAccumL_ , mapAccumR_
+  -- * Open functions
+  , holes , holesList
+  , apply , builder
+  -- * Enumerations
+  , enumerate
+  , enumerateWith
+  , enumerateWith_
+  )
+where
+
+--------------------------------------------------------------------------------
+
+import Control.Monad (liftM)
+import Data.Foldable
+import Data.Traversable ( Traversable(..) , mapAccumL , mapAccumR )
+import Prelude hiding (foldl,foldr,mapM,mapM_,concat,concatMap)
+
+import Data.Generics.Fixplate.Base 
+import Data.Generics.Fixplate.Misc
+
+--------------------------------------------------------------------------------
+-- Accumulating maps
+
+mapAccumL_ :: Traversable f => (a -> b -> (a, c)) -> a -> f b -> f c
+mapAccumL_ f x t = snd (mapAccumL f x t)
+
+mapAccumR_ :: Traversable f => (a -> b -> (a, c)) -> a -> f b -> f c
+mapAccumR_ f x t = snd (mapAccumR f x t)
+
+--------------------------------------------------------------------------------
+-- Open functions
+
+-- | The children together with functions replacing that particular child.    
+holes :: Traversable f => f a -> f (a, a -> f a)
+holes tree = mapAccumL_ ithHole 0 tree where
+  ithHole i x = (i+1, (x,h)) where          
+    h y = mapAccumL_ g 0 tree where         
+      g j z = (j+1, if i==j then y else z)  
+
+holesList :: Traversable f => f a -> [(a, a -> f a)]
+holesList = toList . holes
+
+-- | Apply the given function to each child in turn.
+apply :: Traversable f => (a -> a) -> f a -> f (f a)
+apply f tree = fmap g (holes tree) where
+  g (x,replace) = replace (f x)
+
+-- | Builds up a structure from a list of the children.
+builder :: Traversable f => f a -> [b] -> f b
+builder tree xs = mapAccumL_ g xs tree where
+  g (x:xs) _ = (xs,x)
+
+--------------------------------------------------------------------------------
+-- 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@.
+enumerate :: Traversable f => f a -> (Int, f (Int, a))
+enumerate = mapAccumL (\i x -> (i+1,(i,x))) 0
+
+enumerateWith :: Traversable f => (Int -> a -> b) -> f a -> (Int, f b)
+enumerateWith h = mapAccumL (\i x -> (i+1, h i x)) 0
+
+enumerateWith_ :: Traversable f => (Int -> a -> b) -> f a -> f b
+enumerateWith_ h = snd . enumerateWith h
+  
+--------------------------------------------------------------------------------
Data/Generics/Fixplate/Structure.hs view
@@ -1,47 +1,53 @@-
-{-# LANGUAGE CPP #-}
-#ifdef __GLASGOW_HASKELL__
-{-# LANGUAGE Rank2Types #-}
-#endif
-
--- | Changing the structure of a tree.
-module Data.Generics.Fixplate.Structure 
-  ( NatTrafo
-  , restructure
-  , liftAnn
-  )
-  where
---------------------------------------------------------------------------------
-
-import Data.Generics.Fixplate.Base
-
---------------------------------------------------------------------------------
-
-#ifdef __GLASGOW_HASKELL__
-
--- | The type of natural transformations.
-type NatTrafo f g = forall a. (f a -> g a)
-
--- | Changing the structure of a tree.
-restructure :: Functor f => NatTrafo f g -> Mu f -> Mu g
-restructure trafo = go where
-  go = Fix . trafo . fmap go . unFix
-
-#else  
-
-data NatTrafo f g = NatTrafo (f Int) (g Int)   -- fake and opaque data type
-
--- | Unfortunately, this function requires Rank2Types,
--- thus we only provide it for GHC.
-restructure :: Functor f => NatTrafo f g -> Mu f -> Mu g
-restructure = error "restructure: this operation requires Rank2Types"
-
-#endif
-  
---------------------------------------------------------------------------------
-
--- | Lifting natural transformations to annotations.
-liftAnn :: (f e -> g e) -> Ann f a e -> Ann g a e
-liftAnn trafo (Ann a x) = Ann a (trafo x)
-
---------------------------------------------------------------------------------
++{-# LANGUAGE CPP, Rank2Types #-}++-- it seems that older GHCs do not like multiple LANGUAGE pragmas?+-- {- LANGUAGE CPP -}+-- ifdef __GLASGOW_HASKELL__+-- {- LANGUAGE Rank2Types -}+-- endif++-- | Changing the structure of a tree.+module Data.Generics.Fixplate.Structure +  ( NatTrafo+  , restructure+  , liftAnn+  )+  where+  +--------------------------------------------------------------------------------++import Data.Generics.Fixplate.Base++--------------------------------------------------------------------------------++-- #ifdef __GLASGOW_HASKELL__++-- | The type of natural transformations.+type NatTrafo f g = forall a. (f a -> g a)++-- | Changing the structure of a tree.+restructure :: Functor f => NatTrafo f g -> Mu f -> Mu g+restructure trafo = go where+  go = Fix . trafo . fmap go . unFix++-- #else  ++{-+data NatTrafo f g = NatTrafo (f Int) (g Int)   -- fake and opaque data type++-- | Unfortunately, this function requires Rank2Types,+-- thus we only provide it for GHC.+restructure :: Functor f => NatTrafo f g -> Mu f -> Mu g+restructure = error "restructure: this operation requires Rank2Types"+-}++-- #endif+  +--------------------------------------------------------------------------------++-- | Lifting natural transformations to annotations.+liftAnn :: (f e -> g e) -> Ann f a e -> Ann g a e+liftAnn trafo (Ann a x) = Ann a (trafo x)++--------------------------------------------------------------------------------
Data/Generics/Fixplate/Traversals.hs view
@@ -12,6 +12,7 @@ import Prelude hiding (foldl,foldr,mapM,mapM_,concat,concatMap)
 
 import Data.Generics.Fixplate.Base 
+import Data.Generics.Fixplate.Open
 import Data.Generics.Fixplate.Misc
 
 #ifdef WITH_QUICKCHECK
@@ -27,7 +28,11 @@ children = foldr (:) [] . unFix
 
 -- | The list of all substructures. Together with list-comprehension syntax
--- this is a powerful query tool.
+-- this is a powerful query tool. For example the following is how you get
+-- the list of all variable names in an expression:
+--
+-- > variables expr = [ s | Fix (Var s) <- universe expr ]
+--
 universe :: Foldable f => Mu f -> [Mu f]
 universe x = x : concatMap universe (children x)
 
@@ -92,10 +97,19 @@ --------------------------------------------------------------------------------
 -- * Folds
 
--- | Left fold. Since @Mu f@ is not a functor, but a type, we cannot make
+-- | (Strict) left fold. Since @Mu f@ is not a functor, but a data type, we cannot make
 -- it an instance of the @Foldable@ type class.
 foldLeft :: Foldable f => (a -> Mu f -> a) -> a -> Mu f -> a
+#ifdef __GLASGOW_HASKELL__
 foldLeft h x0 t = go x0 t where
+  go !x !t = foldl go (h x t) (unFix t)
+#else           
+foldLeft h x0 t = go x0 t where
+  go x t = x `seq` t `seq` foldl go (h x t) (unFix t)
+#endif
+
+foldLeftLazy :: Foldable f => (a -> Mu f -> a) -> a -> Mu f -> a
+foldLeftLazy h x0 t = go x0 t where
   go x t = foldl go (h x t) $ unFix t
 
 foldRight :: Foldable f => (Mu f -> a -> a) -> a -> Mu f -> a
@@ -103,29 +117,6 @@   go t x = h t $ foldr go x $ unFix t 
 
 --------------------------------------------------------------------------------
--- * Open functions
-
--- | The children together with functions replacing that particular child.    
-holes :: Traversable f => f a -> f (a, a -> f a)
-holes tree = mapAccumL_ ithHole 1 tree where
-  ithHole i x = (i+1, (x,h)) where          
-    h y = mapAccumL_ g 1 tree where         
-      g j z = (j+1, if i==j then y else z)  
-
-holesList :: Traversable f => f a -> [(a, a -> f a)]
-holesList = toList . holes
-
--- | Apply the given function to each child in turn.
-apply :: Traversable f => (a -> a) -> f a -> f (f a)
-apply f tree = fmap g (holes tree) where
-  g (x,replace) = replace (f x)
-
--- | Builds up a structure from a list of the children.
-builder :: Traversable f => f a -> [b] -> f b
-builder tree xs = mapAccumL_ g xs tree where
-  g (x:xs) _ = (xs,x)
-
---------------------------------------------------------------------------------
 #ifdef WITH_QUICKCHECK
 -- * Tests
 
@@ -134,6 +125,7 @@ 
 runtests_Traversals = do
   quickCheck prop_leftFold
+  quickCheck prop_leftFoldLazy
   quickCheck prop_rightFold
   quickCheck prop_universe1
   quickCheck prop_universe2
@@ -148,6 +140,10 @@ prop_leftFold tree = 
   foldLeft (\xs (Fix (TreeF l s)) -> (l:xs)) [] tree == foldl (flip (:)) [] (fromFixT tree)
 
+prop_leftFoldLazy :: FixT Label -> Bool
+prop_leftFoldLazy tree = 
+  foldLeftLazy (\xs (Fix (TreeF l s)) -> (l:xs)) [] tree == foldl (flip (:)) [] (fromFixT tree)
+  
 prop_rightFold :: FixT Label -> Bool
 prop_rightFold tree = 
   foldRight (\(Fix (TreeF l s)) xs -> (l:xs)) [] tree == foldr (:) [] (fromFixT tree)
Data/Generics/Fixplate/Zipper.hs view
@@ -1,10 +1,11 @@ 
 {-# LANGUAGE CPP #-}
-#ifdef WITH_QUICKCHECK
-{-# LANGUAGE TypeSynonymInstances #-}
-#endif
 
--- | The Zipper.
+-- | The Zipper is a data structure which maintains a location in 
+-- a tree, and allows O(1) movement and local changes
+-- (to be more precise, in our case it is O(k) where k is the number
+-- of children of the node at question; typically this is a very small number).
+--
 module Data.Generics.Fixplate.Zipper where
 
 --------------------------------------------------------------------------------
@@ -18,11 +19,12 @@ import Text.Read 
 
 import Data.Generics.Fixplate.Base
+import Data.Generics.Fixplate.Open
 import Data.Generics.Fixplate.Misc
+import Data.Generics.Fixplate.Traversals
 
 #ifdef WITH_QUICKCHECK
 import Test.QuickCheck
-import Data.Generics.Fixplate.Traversals
 import Data.Generics.Fixplate.Attributes
 import Data.Generics.Fixplate.Test.Tools
 import Control.Monad (liftM)
@@ -31,11 +33,15 @@ --------------------------------------------------------------------------------
 -- * Types
 
+-- | A context node. 
 type Node f  =  Either (Mu f) (Path f)
 
+-- | The context or path type. The invariant we must respect is that there is exactly
+-- one child with the 'Right' constructor.
 data Path f  =  Top
              |  Path { unPath :: f (Node f) } 
                
+-- | The zipper type itself, which encodes a locations in thre tree @Mu f@.
 data Loc f   =  Loc { focus :: Mu f , path :: Path f } 
 
 --------------------------------------------------------------------------------
@@ -118,6 +124,16 @@     h  old  (Left   y)  =  (old     , y)
     h  _    (Right  p)  =  (Just p  , t)
 
+-- | We attribute all nodes with a zipper focused at that location.
+locations :: Traversable f => Mu f -> Attr f (Loc f)
+locations tree = go (root tree) tree where
+  go loc (Fix t) = Fix (Ann loc t') where
+    t' = enumerateWith_ (\j x -> go (unsafeMoveDown j loc) x) t
+
+-- | The list of all locations.
+locationsList :: Traversable f => Mu f -> [Loc f]
+locationsList = toList . Attrib . locations
+    
 -- | The zipper version of 'forget'.
 locForget :: Functor f => Loc (Ann f a) -> Loc f    
 locForget (Loc foc path) = Loc (forget foc) (go path) where
@@ -132,19 +148,22 @@ --------------------------------------------------------------------------------
 -- * Manipulating the subtree at focus
 
+-- | Extracts the subtree at focus. Synonym of 'focus'.
 extract :: Loc f -> Mu f
 extract = focus
 
+-- | Replaces the subtree at focus. 
 replace :: Mu f -> Loc f -> Loc f
 replace new loc = loc { focus = new }
 
+-- | Modifies the subtree at focus. 
 modify :: (Mu f -> Mu f) -> Loc f -> Loc f
 modify h loc = replace (h (focus loc)) loc
 
 --------------------------------------------------------------------------------
 -- * Safe movements
 
--- | Moves down the child with the given index.
+-- | Moves down to the child with the given index.
 -- The leftmost children has index @0@.
 moveDown :: Traversable f => Int -> Loc f -> Maybe (Loc f)
 moveDown pos (Loc foc path) = new where
@@ -156,7 +175,7 @@     then  ((Just x  , j+1),  Right  path  ) 
     else  ((old     , j+1),  Left   x     )      
 
--- | Moves down the leftmost child.
+-- | Moves down to the leftmost child.
 moveDownL :: Traversable f => Loc f -> Maybe (Loc f)
 moveDownL (Loc foc path) = new where
   new = case mfoc' of  
@@ -167,7 +186,7 @@     Nothing  -> (Just x  ,  Right  path  ) 
     _        -> (old     ,  Left   x     )      
 
--- | Moves down the rightmost child.
+-- | Moves down to the rightmost child.
 moveDownR :: Traversable f => Loc f -> Maybe (Loc f)
 moveDownR (Loc foc path) = new where
   new = case mfoc' of  
@@ -180,6 +199,7 @@     
 --------------------------------------------------------------------------------
 
+-- | Moves up.
 moveUp :: Traversable f => Loc f -> Maybe (Loc f)
 moveUp (Loc foc path) = case path of
   Top         -> Nothing
@@ -230,7 +250,7 @@ --------------------------------------------------------------------------------
 -- * Testing for borders
 
--- | Checks whether we are the top.
+-- | Checks whether we are at the top (root).
 isTop :: Loc f -> Bool
 isTop (Loc _ p) = case p of { Top -> True ; _ -> False }
 
@@ -252,13 +272,51 @@ moveTop = tillNothing moveUp
 
 -- | Moves left until it can.
+-- It should be faster than repeated left steps.
 leftmost :: Traversable f => Loc f -> Loc f
-leftmost = tillNothing moveLeft
+leftmost orig@(Loc foc path) = case path of
+  Top         -> orig
+  Path nodes  -> 
+    case both of
+      Both {}  -> Loc foc' (Path nodes')
+      _        -> error "leftmost: shouldn't happen"
+    where  
+      -- this tricky implementation uses lazyness 
+      -- so that we only need a single traversal
+      (foc',pnew) = case both of { Both f p -> (f,p) ; _ -> error "leftmost: shouldn't happen" }    
+      (both,nodes') = mapAccumL g None nodes 
+      g old ei = case old of
+        None -> case ei of
+          Left  x  -> (First x    , Right pnew)
+          Right p  -> (Both foc p , ei        )   -- we are already at the leftmost position
+        First f -> case ei of
+          Left  x  -> (old        , ei        )
+          Right p  -> (Both f p   , Left  foc )
+        Both {} -> (old, ei)
 
 -- | Moves right until it can.
+-- It should be faster than repeated right steps.
 rightmost :: Traversable f => Loc f -> Loc f
-rightmost = tillNothing moveRight
-
+rightmost orig@(Loc foc path) = case path of
+  Top         -> orig
+  Path nodes  -> 
+    case both of
+      Both {}  -> Loc foc' (Path nodes')
+      _        -> error "rightmost: shouldn't happen"
+    where  
+      -- this tricky implementation uses lazyness 
+      -- so that we only need a single traversal
+      (foc',pnew) = case both of { Both f p -> (f,p) ; _ -> error "rightmost: shouldn't happen" }    
+      (both,nodes') = mapAccumR g None nodes 
+      g old ei = case old of
+        None -> case ei of
+          Left  x  -> (First x    , Right pnew)
+          Right p  -> (Both foc p , ei        )   -- we are already at the rightmost position
+        First f -> case ei of
+          Left  x  -> (old        , ei        )
+          Right p  -> (Both f p   , Left  foc )
+        Both {} -> (old, ei)
+          
 --------------------------------------------------------------------------------
 -- * Unsafe movements
 
@@ -283,6 +341,7 @@ 
 type LocT a = Loc (TreeF a)
 
+{-
 data Step
   = StepUp
   | StepLeft
@@ -322,6 +381,7 @@ instance Arbitrary Walk where
   arbitrary = liftM Walk arbitrary
   shrink (Walk steps) = map Walk (shrink steps)
+-}
 
 -- | Assuming a left-to-right canonical numbering, we find the given
 -- location.
@@ -342,18 +402,18 @@     Nothing -> goUpR (unsafeMoveUp loc)
     Just zz -> go zz
 
---
+----
 tmp = treeF "root"
   [ treeF "a" [ treeF "a1" [] , treeF "a2" [] ]
   , treeF "b" []
   , treeF "c" [ treeF "c1" [] , treeF "c2" [] , treeF "c3" [] ]
   ]
---
+----
   
 instance Arbitrary a => Arbitrary (LocT a) where
   arbitrary = do
     tree <- arbitrary 
-    let (n,numbered) = numberNodes tree
+    let (n,numbered) = enumerateNodes tree
     k <- choose (0,n-1)
     return $ locForget $ findLoc k (root numbered)
 
@@ -370,8 +430,12 @@ runtests_Zipper = do
   quickCheck prop_ReadShowLoc
   quickCheck prop_findLoc
+  quickCheck prop_locationsList
   quickCheck prop_contextList
   quickCheck prop_Top
+  quickCheck prop_defocus
+  quickCheck prop_leftmost
+  quickCheck prop_rightmost
   quickCheck prop_DownLUp
   quickCheck prop_DownRUp
   quickCheck prop_UpDownL 
@@ -384,13 +448,26 @@ 
 ----------------------------------------
 
+leftmostNaive :: Traversable f => Loc f -> Loc f
+leftmostNaive = tillNothing moveLeft
+
+rightmostNaive :: Traversable f => Loc f -> Loc f
+rightmostNaive = tillNothing moveRight
+
+----------------------------------------
+
 prop_ReadShowLoc :: LocT Label -> Bool
 prop_ReadShowLoc loc = read (show loc) == loc
 
+prop_locationsList :: FixT Label -> Bool
+prop_locationsList tree = locationsList tree == [ locForget $ findLoc i top | i<-[0..n-1] ] where
+  top = root numbered
+  (n,numbered) = enumerateNodes tree
+
 prop_findLoc :: FixT Label -> Bool
 prop_findLoc tree = [0..n-1] == [ attribute $ focus $ findLoc i top | i<-[0..n-1] ] where
   top = root numbered
-  (n,numbered) = numberNodes tree
+  (n,numbered) = enumerateNodes tree
 
 prop_contextList :: FixT Label -> Bool  
 prop_contextList tree =
@@ -399,13 +476,26 @@   [ defocus $ modify (\(Fix (TreeF l ts)) -> Fix (TreeF (h l) ts) ) $ locForget $ findLoc i top | i<-[0..n-1] ]
   where
     top = root numbered
-    (n,numbered) = numberNodes tree
+    (n,numbered) = enumerateNodes tree
     h (Label xs) = Label ('_':xs)
   
 prop_Top :: LocT Label -> Bool
 prop_Top loc = root (defocus loc) == moveTop loc
 
+prop_defocus :: FixT Label -> Bool
+prop_defocus tree = 
+  Prelude.and [ defocus (findLoc i top) == numbered | i<-[0..n-1] ] 
+  where
+    top = root numbered
+    (n,numbered) = enumerateNodes tree
+
 ----------------------------------------
+    
+prop_leftmost :: LocT Label -> Bool
+prop_leftmost loc = leftmost loc == leftmostNaive loc
+
+prop_rightmost :: LocT Label -> Bool
+prop_rightmost loc = rightmost loc == rightmostNaive loc
     
 prop_DownLUp :: LocT Label -> Property
 prop_DownLUp loc = 
fixplate.cabal view
@@ -1,61 +1,69 @@-
-Name:                fixplate
-Version:             0.1
-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.
-                     See the module "Data.Generics.Fixplate" and then the individual modules
-                     for more detailed information.
-License:             BSD3
-License-file:        LICENSE
-Author:              Balazs Komuves
-Copyright:           (c) 2011 Balazs Komuves
-Maintainer:          bkomuves (plus) hackage (at) gmail (dot) com
-Homepage:            http://code.haskell.org/~bkomuves/
-Stability:           Experimental
-Category:            Generics
-Tested-With:         GHC == 7.0.3
-Cabal-Version:       >= 1.2
-Build-Type:          Simple
-
-Flag withQuickCheck
-  Description: Compile with the QuickCheck tests. 
-  default: False
-
-Flag base4
-  Description: Base v4
-  
-Library
-  if flag(base4)
-    Build-Depends:       base >= 4 && < 5
-    cpp-options:         -DBASE_VERSION=4
-  else 
-    Build-Depends:       base >= 3 && < 4
-    cpp-options:         -DBASE_VERSION=3
-
-  if flag(withQuickCheck)
-    Build-Depends:       QuickCheck > 2.4
-    cpp-options:         -DWITH_QUICKCHECK
-
-  Exposed-Modules:     Data.Generics.Fixplate
-                       Data.Generics.Fixplate.Base
-                       Data.Generics.Fixplate.Traversals
-                       Data.Generics.Fixplate.Morphisms
-                       Data.Generics.Fixplate.Attributes
-                       Data.Generics.Fixplate.Zipper
-                       Data.Generics.Fixplate.Structure
- 
-  Other-Modules:       Data.Generics.Fixplate.Misc
-
-  if flag(withQuickCheck)  
-    Exposed-Modules:     Data.Generics.Fixplate.Test.Tools
-                         Data.Generics.Fixplate.Test.Instances
-                         Data.Generics.Fixplate.Tests
- 
-  Extensions:          CPP
-
-  Hs-Source-Dirs:      .
-
-  ghc-options:         -Wall -fno-warn-unused-matches
-
-    ++Name:                fixplate+Version:             0.1.1+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.+                     See the module "Data.Generics.Fixplate" and then the individual modules+                     for more detailed information.+License:             BSD3+License-file:        LICENSE+Author:              Balazs Komuves+Copyright:           (c) 2011 Balazs Komuves+Maintainer:          bkomuves (plus) hackage (at) gmail (dot) com+Homepage:            http://code.haskell.org/~bkomuves/+Stability:           Experimental+Category:            Generics+Tested-With:         GHC == 7.0.3+Cabal-Version:       >= 1.2+Build-Type:          Simple++Flag withQuickCheck+  Description: Compile with the QuickCheck tests. +  default: False++Flag base4+  Description: Base v4+  +Library+  if flag(base4)+    Build-Depends:       base >= 4 && < 5+    cpp-options:         -DBASE_VERSION=4+  else +    Build-Depends:       base >= 3 && < 4+    cpp-options:         -DBASE_VERSION=3++  if flag(withQuickCheck)+    Build-Depends:       QuickCheck > 2.4+    cpp-options:         -DWITH_QUICKCHECK++  Exposed-Modules:     Data.Generics.Fixplate+                       Data.Generics.Fixplate.Base+                       Data.Generics.Fixplate.Open+                       Data.Generics.Fixplate.Traversals+                       Data.Generics.Fixplate.Morphisms+                       Data.Generics.Fixplate.Attributes+                       Data.Generics.Fixplate.Zipper+                       +  if impl(ghc)||impl(hugs)                     +    Exposed-Modules:     Data.Generics.Fixplate.Structure+    cpp-options:         -DHAS_RANK2TYPES++  Other-Modules:       Data.Generics.Fixplate.Misc++  if flag(withQuickCheck)  +    Exposed-Modules:     Data.Generics.Fixplate.Test.Tools+                         Data.Generics.Fixplate.Test.Instances+                         Data.Generics.Fixplate.Tests+    Extensions:          TypeSynonymInstances+ +  Extensions:          CPP++  if impl(ghc)+    Extensions:          BangPatterns++  Hs-Source-Dirs:      .++  ghc-options:         -Wall -fno-warn-unused-matches++