Added some comments to my changes to GenericUtils
This commit is contained in:
parent
77b75e3134
commit
09e0e46ec2
|
@ -160,7 +160,47 @@ gmapMFor typeset f = gmapM (each f)
|
|||
Just Miss -> return x
|
||||
Nothing -> return x
|
||||
|
||||
|
||||
-- | A Route is a way of navigating to a particular node in a tree structure.
|
||||
--
|
||||
-- Let's say that you have some binary tree structure:
|
||||
--
|
||||
-- > data BinTree a = Leaf a | Branch (BinTree a) (BinTree a)
|
||||
--
|
||||
-- Suppose you then have a big binary tree of integers, potentially with duplicate values,
|
||||
-- and you want to be able to modify a particular integer. You can't modify in-place,
|
||||
-- because this is a functional language. So you instead want to be able to apply
|
||||
-- a modify function to the whole tree that really just modifies the particular
|
||||
-- integer.
|
||||
--
|
||||
-- To do this you can use:
|
||||
--
|
||||
-- > myRoute :: Route Int (BinTree Int)
|
||||
--
|
||||
-- You apply it as follows (for example, to increment the integer):
|
||||
--
|
||||
-- > runIdentity $ routeModify myRoute (return . (+1)) myTree
|
||||
--
|
||||
-- The modifier is monadic because that's usually how we want to use it, but we
|
||||
-- can use the identity monad as above for pure functions. This will only work
|
||||
-- if the route is valid on the given tree.
|
||||
--
|
||||
-- Another useful aspect is composition. If your tree was in a tree of trees:
|
||||
--
|
||||
-- > routeToInnerTree :: Route (BinTree Int) (BinTree (BinTree Int))
|
||||
--
|
||||
-- You could compose this with the earlier route:
|
||||
--
|
||||
-- > routeToInnerTree @-> myRoute :: Route Int (BinTree (BinTree Int))
|
||||
--
|
||||
-- These routes are a little like zippers, but (in my opinion) easier to use, and
|
||||
-- tack on to existing code with complex data structures (without needing any code
|
||||
-- generation). You can either compose routes yourself (as the flow-graph building
|
||||
-- does) or by using 'gmapMForRoute'.
|
||||
--
|
||||
-- Routes support Eq, Show and Ord. All these instances represent a route as a
|
||||
-- list of integers: a route-map. [0,2,1] means first child (zero-based), then
|
||||
-- third child, then first child of the given data-type. Routes are ordered using
|
||||
-- the standard list ordering (lexicographic) over this representation.
|
||||
data Route inner outer = Route [Int] (forall m. Monad m => (inner -> m inner) -> (outer -> m outer))
|
||||
|
||||
instance Eq (Route inner outer) where
|
||||
|
@ -172,30 +212,53 @@ instance Ord (Route inner outer) where
|
|||
instance Show (Route inner outer) where
|
||||
show (Route ns _) = "Route " ++ show ns
|
||||
|
||||
-- | Gets the integer-list version of a route. See the documentation of 'Route'.
|
||||
routeId :: Route inner outer -> [Int]
|
||||
routeId (Route ns _) = ns
|
||||
|
||||
-- | Given an index (zero is the first item), forms a route to that index item
|
||||
-- in the list. So for example:
|
||||
--
|
||||
-- > runIdentity $ routeModify (routeList 3) (return . (*10)) [0,1,2,3,4,5] == [0,1,2,30,4,5]
|
||||
--
|
||||
routeList :: Int -> Route a [a]
|
||||
routeList 0 = Route [0] (\f (x:xs) -> f x >>* (: xs))
|
||||
routeList n = Route [1] (\f (x:xs) -> f xs >>* (x:)) @-> routeList (n-1)
|
||||
|
||||
-- | Applies a monadic modification function using the given route.
|
||||
routeModify :: Monad m => Route inner outer -> (inner -> m inner) -> (outer -> m
|
||||
outer)
|
||||
routeModify (Route _ wrap) = wrap
|
||||
|
||||
-- | Given a route, gets the value in the large data structure that is pointed
|
||||
-- to by that route.
|
||||
routeGet :: Route inner outer -> outer -> inner
|
||||
routeGet route = flip execState undefined . routeModify route (\x -> put x >> return x)
|
||||
|
||||
-- | Given a route, sets the value in the large data structure that is pointed
|
||||
-- to by that route.
|
||||
routeSet :: Route inner outer -> inner -> outer -> outer
|
||||
routeSet route x = runIdentity . routeModify route (const $ return x)
|
||||
|
||||
-- | Composes two routes together. The outer-to-mid route goes on the left hand
|
||||
-- side, and the mid-to-inner goes on the right hand side to form an outer-to-inner
|
||||
-- route.
|
||||
(@->) :: Route mid outer -> Route inner mid -> Route inner outer
|
||||
(@->) (Route outInds outF) (Route inInds inF) = Route (outInds ++ inInds) (outF
|
||||
. inF)
|
||||
|
||||
-- | The identity route. This has various obvious properties:
|
||||
--
|
||||
-- > routeGet routeIdentity == id
|
||||
-- > routeSet routeIdentity == const
|
||||
-- > routeModify routeIdentity == id
|
||||
-- > routeIdentity @-> route == route
|
||||
-- > route @-> routeIdentity == route
|
||||
routeIdentity :: Route a a
|
||||
routeIdentity = Route [] id
|
||||
|
||||
-- | Acts just like gmapMFor, except that it also tells you the route to the node
|
||||
-- that your generic function is being applied to.
|
||||
gmapMForRoute :: forall m t. (Monad m, Data t) =>
|
||||
TypeSet ->
|
||||
(forall s. Data s => (s, Route s t) -> m s)
|
||||
|
@ -211,6 +274,7 @@ gmapMForRoute typeset f = gmapMWithRoute (each f)
|
|||
Just Miss -> return x
|
||||
Nothing -> return x
|
||||
|
||||
-- A helper for gmapMForRoute that maps over the direct children and supplies routes
|
||||
gmapMWithRoute :: forall a m. (Monad m, Data a) => (forall b. Data b => (b, Route
|
||||
b a) -> m b) -> a -> m a
|
||||
gmapMWithRoute f = gmapFuncs [GM {unGM = f' n} | n <- [0..]]
|
||||
|
|
Loading…
Reference in New Issue
Block a user