5

I would like to implicitly convert between the Scala XML Elem object and another representation of an XML element, in my case dom4j Element. I wrote the following implicit conversions:

implicit def elemToElement(e: Elem): Element = ... do conversion here ...
implicit def elementToElem(e: Element): Elem = ... do conversion here ...

So far so good, this works.

Now I also need collections of said elements to convert both ways. First, do I absolutely need to write additional conversion methods? Things didn't seem to work if I didn't.

I tried to write the following:

implicit def elemTToElementT(t: Traversable[Elem]) = t map (elemToElement(_))
implicit def elementTToElemT(t: Traversable[Element]) = t map (elementToElem(_))

This doesn't look too ideal because if the conversion method takes a Traversable, then it also returns a Traversable. If I pass a List, I also get a Traversable out. So I assume the conversion should be parametrized somehow.

So what's the standard way of writing these conversions in order to be as generic as possible?

ebruchez
  • 7,760
  • 6
  • 29
  • 41

2 Answers2

2

This is non-trivial so in order get what you want I think you'll have to go a bit deeper. This article explains a lot about how the scala collections work (and interesting read it is too): http://www.artima.com/scalazine/articles/scala_collections_architecture.html

You're basically trying to do the same as List.map(...) (or TraversableLike which has the implementation) and similar methods.... only with implicit conversion in addition.

Update:

I started experimenting a little bit with this and wrote a conversion based on what TraversableLike.map(...) does. However I discovered that it worked even without it. Seems like Scala supports it out of the box (at least on my machine :-) ):

case class Element(e: Elem)
implicit def elemToElement(e: Elem): Element = Element(e)
implicit def elementToElem(e: Element): Elem = e.e

val a: List[Element] = List(<a/>, <b/>, <c/>)
val b: List[Elem] = List(Element(<a/>), Element(<b/>), Element(<c/>))
val c: Set[Element] = Set(<a/>, <b/>, <c/>)

This was what you were after?

thoredge
  • 12,237
  • 1
  • 40
  • 55
  • Thanks for the pointer I actually did go through that document a while ago but was hoping for a direct answer to my question. #laziness – ebruchez Jan 13 '11 at 18:21
  • Wow, your example does work in the REPL! Now the question is why it didn't work in the context of my own code. I suspect it's because I am passing a Scala List[Elem] to a method requiring java.util.List[Element]. The "One-at-a-time Rule" kicking in, only one implicit conversion is attempted. Looking more into it. – ebruchez Jan 14 '11 at 00:32
  • Mmh, so that doesn't work when calling functions: `def fa(p: List[Element]) {}; fa(b)` > error: type mismatch – ebruchez Jan 14 '11 at 00:47
1

I think this might be taking implicits too far. Especially as you can just use the converter method in a map

val listOfElements = listOfElems map elemToElement(_)

I think that the level conciseness you're going for is getting into obfuscation. I would create a converter layer and work in only one of the representations so as to keep things from getting to confused.

sblundy
  • 60,628
  • 22
  • 121
  • 123
  • Why is it any more confusing to be able to convert automatically from `X` to `Y` than from `List[X]` to `List[Y]`? It fills exactly the same need, namely that you don't actually care about which representation is used and you want the compiler to handle the busywork for you. – Rex Kerr Jan 13 '11 at 15:49
  • It's largely a matter of style and best practices. Asking implicits to work that much magic is asking for trouble. They are, in my view, one of the BFGs of scala: be careful what you point it at. – sblundy Jan 13 '11 at 16:09
  • I agre with Rex: I don't agree that it is confusing. On the contrary, I would had even hoped that defining an implicit conversion of a given type would automatically make implicit conversion of (shall I say "monadic") containers for that type work as well. – ebruchez Jan 13 '11 at 18:18
  • I would say it's a bad idea for a slightly different reason, namely that it hides significant (O(n)) computation. – Tom Crockett Jan 13 '11 at 23:55