I'm traversing an AST using simple pattern matching and the Reader
monad.
Elsewhere in my project I've defined a walk
function for traversing the AST, which at its heart uses foldl
to reduce the results of visiting each node in the tree into a single monoidal result (for example, to produce a "symbol table" from the special nodes in the tree).
My question is: is it possible to combine these two approaches and use a function like my walk
function:
walk :: Monoid a => (Node -> a) -> a -> Node -> a
walk f acc n = foldl (walk f) (acc <> f n) children
where
children = case n of
Blockquote b -> b
DocBlock d -> d
FunctionDeclaration {} -> functionBody n
List l -> l
ListItem i -> i
Paragraph p -> p
Unit u -> u
_ -> [] -- no Node children
and the Reader
— like the traversal in the code below (some bits omitted for brevity) — at the same time?
markdown :: Node -> String
markdown n = runReader (node n) state
where state = State (getSymbols n) (getPluginName n)
node :: Node -> Env
node n = case n of
Blockquote b -> blockquote b >>= appendNewline >>= appendNewline
DocBlock d -> nodes d
FunctionDeclaration {} -> nodes $ functionBody n
Paragraph p -> nodes p >>= appendNewline >>= appendNewline
Link l -> link l
List ls -> nodes ls >>= appendNewline
ListItem l -> fmap ("- " ++) (nodes l) >>= appendNewline
Unit u -> nodes u
My use motivation here is that my walk
function already encodes the knowledge of how to do get the children for each pattern and how to perform an in-order traversal of the AST. I don't really want to reimplement that for every traversal, so it would be nice to use walk
in more places, including ones where I need to use Reader
(and likely later on, State
, probably in a stack).
Can these things be fruitfully combined?