Reputation: 27190
I have anti-xml Elem, something like this (auto-generated data):
<library>
<bookshelf number="0">
<book year="1997" title="Puzzled Coordinators" author="Lily Williams"></book>
<book year="2005" title="Jittery Fare" author="Lucy Taylor"></book>
<book year="2001" title="Dizzy Jurisdiction" author="Lucy Robinson"></book>
</bookshelf>
<bookshelf number="1">
<book year="1997" title="Bashful Trusts" author="Lucas Wilson"></book>
<book year="2003" title="Outrageous Consequences" author="Byron White"></book>
<book year="1992" title="Irritated Accusations" author="Anne Roberts"></book>
</bookshelf>
</library>
And I want to apply several transformations to it, for example:
val transforms: Seq[...] = ...
val result = transforms.foldLeft(library)(l,t) => t(l))
But I got only as far as this solution:
val transforms: Seq[Elem => Zipper[Node]] = Seq(
x => x \\ "book" filter (_.attrs("year").toInt > 2000) unselect,
x => x \\ "book" filter (_.attrs("title").contains("J")) unselect
)
val result = transforms.foldLeft(lib)((l,t) => t(l).head.asInstanceOf[Elem])
Is there some way to get better types for transformations (Elem => Elem) and avoid those ugly casts?
Upvotes: 3
Views: 114
Reputation: 139028
Given that unselect
currently just returns a Zipper[Node]
, I don't see a way to get what you want without bossing the type system around a bit, and that's going to take something like a cast.
In this case you really do know something the type system can't, given the current state of the Anti-XML library: you know that the parent of the zipper resulting from your transformation is a Zipper[Elem]
, and therefore that what unselect
gives you actually will be a Zipper[Elem]
, even though it's typed as a Zipper[Node]
.
So I think the best you can do is package up the unpleasantness a little more cleanly:
def toEndo(t: Elem => Zipper[Elem]): Elem => Elem =
t andThen (_.unselect.head.asInstanceOf[Elem])
Or, to make your intent more apparent:
def toEndo(t: Elem => Zipper[Elem]) = t andThen (_.unselect.head match {
case e: Elem => e
case _ => throw new RuntimeException("Aaaaah! This was never meant to happen!")
})
Then you can write the following:
val transforms: Seq[Elem => Zipper[Elem]] = Seq(
_ \\ "book" filter (_.attrs("year").toInt > 2000),
_ \\ "book" filter (_.attrs("title").contains("J"))
)
val result = Function.chain(transforms map toEndo)(lib)
Note that I've moved the unselect
into the helper to give us a bit more type safety.
Upvotes: 1