I'm trying to use xml-conduit and xml-lens for parsing and traversing an XML document. Instead of having to traverse the same parts of the document multiple times, I would like to store the traversal up until the previous point and then drill down further.
ex.
let pos = doc ^. root . el "foo"
bar = pos . text
baz = pos ./ el "quux" . text
When I try to do this, I get the following error:
No instance for (Data.Monoid.Monoid Element)
arising from a use of `el'
Possible fix:
add an instance declaration for (Data.Monoid.Monoid Element)
In the second argument of `(.)', namely `el "foo"'
In the second argument of `(^.)', namely `root . el "foo"'
In the expression: doc ^. root . el "foo"
What can I do to store this intermediate position?
The type error happens because el
is a Traversal
. Traversals point to multiple values (in this case, all foo
elements). (^.)
, on the other hand, always returns a single value. Using (^.)
with a Traversal
only works if the target type is a Monoid
; in that case, the multiple values are smashed into a single one using mappend
. Element
, however, is not a Monoid
(because there is no sensible way to combine XML elements into a single element), and so (^.)
does not work. To get the matched XML elements, you should use (^..)
instead, which will return them as a list.
As for the question proper ("What can I do to store this intermediate position?"), the Traversal
is a reference to the position, so if you want to compose it further you don't want to "dereference" it with (^.)
or (^..)
. The following should work:
let pos = root . el "foo"
baz = pos ./ el "quux" . text
elemTexts = doc ^.. baz