---------------------------------------------------------------------------
--- Library to support lightweight generic traversals
--- through tree-structured data. See
--- [here](http://www-ps.informatik.uni-kiel.de/~sebf/projects/traversal.html)
--- for a description of the library.
---
--- @author Sebastian Fischer
--- @version January 2019
---------------------------------------------------------------------------
module Data.Traversal (
Traversable, noChildren,
children, replaceChildren, mapChildren,
family, childFamilies, mapFamily, mapChildFamilies,
evalFamily, evalChildFamilies, fold, foldChildren,
replaceChildrenIO, mapChildrenIO, mapFamilyIO, mapChildFamiliesIO,
evalFamilyIO, evalChildFamiliesIO
) where
--- A datatype is Traversable
if it defines a function
--- that can decompose a value into a list of children of the same type
--- and recombine new children to a new value of the original type.
---
type Traversable a b = a -> ([b], [b] -> a)
--- Traversal function for constructors without children.
---
noChildren :: Traversable _ _
noChildren x = ([], const x)
--- Yields the children of a value.
---
children :: Traversable a b -> a -> [b]
children tr = fst . tr
--- Replaces the children of a value.
---
replaceChildren :: Traversable a b -> a -> [b] -> a
replaceChildren tr = snd . tr
--- Applies the given function to each child of a value.
---
mapChildren :: Traversable a b -> (b -> b) -> a -> a
mapChildren tr f x = replaceChildren tr x (map f (children tr x))
--- Computes a list of the given value, its children, those children, etc.
---
family :: Traversable a a -> a -> [a]
family tr x = familyFL tr x []
--- Computes a list of family members of the children of a value.
--- The value and its children can have different types.
---
childFamilies :: Traversable a b -> Traversable b b -> a -> [b]
childFamilies tra trb x = childFamiliesFL tra trb x []
--- Applies the given function to each member of the family of a value.
--- Proceeds bottom-up.
---
mapFamily :: Traversable a a -> (a -> a) -> a -> a
mapFamily tr f = f . mapChildFamilies tr tr f
--- Applies the given function to each member of the families of the children
--- of a value. The value and its children can have different types.
--- Proceeds bottom-up.
---
mapChildFamilies :: Traversable a b -> Traversable b b -> (b -> b) -> a -> a
mapChildFamilies tra trb = mapChildren tra . mapFamily trb
--- Applies the given function to each member of the family of a value
--- as long as possible. On each member of the family of the result the given
--- function will yield Nothing
.
--- Proceeds bottom-up.
---
evalFamily :: Traversable a a -> (a -> Maybe a) -> a -> a
evalFamily tr f = mapFamily tr g
where g x = maybe x (mapFamily tr g) (f x)
--- Applies the given function to each member of the families of the children
--- of a value as long as possible.
--- Similar to 'evalFamily'.
---
evalChildFamilies :: Traversable a b -> Traversable b b
-> (b -> Maybe b) -> a -> a
evalChildFamilies tra trb = mapChildren tra . evalFamily trb
--- Implements a traversal similar to a fold with possible default cases.
---
fold :: Traversable a a -> (a -> [r] -> r) -> a -> r
fold tr f = foldChildren tr tr f f
--- Fold the children and combine the results.
---
foldChildren :: Traversable a b -> Traversable b b
-> (a -> [rb] -> ra) -> (b -> [rb] -> rb) -> a -> ra
foldChildren tra trb f g a = f a (map (fold trb g) (children tra a))
--- IO version of replaceChildren
---
replaceChildrenIO :: Traversable a b -> a -> IO [b] -> IO a
replaceChildrenIO tr = liftIO . replaceChildren tr
--- IO version of mapChildren
---
mapChildrenIO :: Traversable a b -> (b -> IO b) -> a -> IO a
mapChildrenIO tr f a = replaceChildrenIO tr a (mapIO f (children tr a))
--- IO version of mapFamily
---
mapFamilyIO :: Traversable a a -> (a -> IO a) -> a -> IO a
mapFamilyIO tr f a = mapChildFamiliesIO tr tr f a >>= f
--- IO version of mapChildFamilies
---
mapChildFamiliesIO :: Traversable a b -> Traversable b b
-> (b -> IO b) -> a -> IO a
mapChildFamiliesIO tra trb = mapChildrenIO tra . mapFamilyIO trb
--- IO version of evalFamily
---
evalFamilyIO :: Traversable a a -> (a -> IO (Maybe a)) -> a -> IO a
evalFamilyIO tr f = mapFamilyIO tr g
where g a = f a >>= maybe (return a) (mapFamilyIO tr g)
--- IO version of evalChildFamilies
---
evalChildFamiliesIO :: Traversable a b -> Traversable b b
-> (b -> IO (Maybe b)) -> a -> IO a
evalChildFamiliesIO tra trb = mapChildrenIO tra . evalFamilyIO trb
-- implementation of 'family' with functional lists for efficiency reasons
type FunList a = [a] -> [a]
concatFL :: [FunList a] -> FunList a
concatFL [] ys = ys
concatFL (x:xs) ys = x (concatFL xs ys)
familyFL :: Traversable a a -> a -> FunList a
familyFL tr x xs = x : childFamiliesFL tr tr x xs
childFamiliesFL :: Traversable a b -> Traversable b b -> a -> FunList b
childFamiliesFL tra trb x xs = concatFL (map (familyFL trb) (children tra x)) xs
liftIO :: (a -> b) -> IO a -> IO b
liftIO f ioa = ioa >>= return . f