
Tim Newsham
I thought this one would be easy but I'm starting to think its not. I am playing with HaXml and I want to transform an XML tree into another tree. The transforms are simple enough, but the kicker is that I want them to be "stateful." In this example, the state is a random number generator. So the transformation depends on the current node and the random number generator state. I want to transform every node in the tree.
Indeed, the HaXml functions are pure, and in particular foldXml does not thread a state through. To introduce state, you provide your own state monad (fortunately there is always Control.Monad.State). To traverse the whole tree in this monad, you write your own recursion and deconstruct the nodes yourself (because foldXml is too specific to be re-usable). Here is my example. It replaces attribute values by random integers between 0 and 99, so it is a similar task but slightly different from yours. Some names are inspired by yours, but I have simplified their nature: The state I thread through is not a stream of generators, but rather a stream of numbers; as long as this stream comes from Random.randomRs, I'm good. import Text.XML.HaXml import Control.Monad.State import Random newtweak :: [Int] -> CFilter newtweak xs c = [evalState (reco c) xs] reco :: Content -> State [Int] Content reco (CElem (Elem nm ats cs)) = do { ats' <- mapM newtweakAttr ats ; cs' <- mapM reco cs ; return (CElem (Elem nm ats' cs')) } reco c = return c newtweakAttr (k,_) = do { n <- gets head ; modify tail ; return (k, AttValue[Left (show n)]) } main = do gen <- getStdGen processXmlWith (newtweak (randomRs (0,99) gen))