How to be able to apply implict conversions in a recursive way in Scala
implicits, recursion, scala
Solution
You can solve this kind of problem in an elegant and type-safe way with type classes:
// Evidence that we can turn an A into some XML:
trait Markup[-A] { def toHtml(a: A): xml.Node }
def baseMarkup[A] = new Markup[A] { def toHtml(a: A) = xml.Text(a.toString) }
implicit def markup[A](implicit m: Markup[A] = baseMarkup[A]) =
new Markup[Iterable[A]] {
def toHtml(c: Iterable[A]) = <ul>
{ c.map(child => <li>{ m.toHtml(child) }</li>) }
</ul>
}
implicit def fromMarkup[A](a: A)(implicit m: Markup[A]) = new {
def toHtml = m toHtml a
}
This works for nested lists of arbitrary depth:
val messy = List(List(List("a")), List(List("b", "c"), List("c")))
val printer = new xml.PrettyPrinter(80, 2)
And then:
scala> printer format messy.toHtml
res1: String =
<ul>
<li>
<ul>
<li>
<ul>
<li>a</li>
</ul>
</li>
</ul>
</li>
<li>
<ul>
<li>
<ul>
<li>b</li>
<li>c</li>
</ul>
</li>
<li>
<ul>
<li>c</li>
</ul>
</li>
</ul>
</li>
</ul>
See my answer here for a more detailed explanation of the methods above.
Problem
I am trying to write a conversions library for converting some scala types to an HTML representation. I would want, say, to execute `List(1,2).toHtml` and obtain `<ul><li>1</li><li>2</li></ul>`, as a `String`. So far i've written a set of implicit conversions that work well on detecting the result type and applying the correct `toHtml`. Let me show a sample: ``` object Conversions { implicit def fromIterable[A](l : Iterable[A]) = new Object { def toHtml = <ul> { l.map{ e => <li>{ e }</li> } } </ul> toString } } import Conversions._ ``` With this code, whenever i ask the compiler `List(1,2).toHtml` i get the correct conversion. As it would with any other `Iterable` val. My problem and question here is how can i use this toHtml conversion recursively? Because if i typed `List( List(1,2), List(3,4) ).toHtml` i would want to get `<ul> <li><ul><li>1</li><li>2</li></ul></li> <li><ul><li>3</li><li>4</li></ul></li> </ul>`, the `toHtml` conversion applied recursively to every element of the input `Iterable`. I tried to change the `toHtml` definition to `def toHtml = <ul> { l.map{ e => <li>{ e.toHtml }</li> } } </ul> toString` which didn't work because the compiler told me `value toHtml is not a member of type parameter A` which makes perfect sense. I know that my problem probably resides in the `new Object { ... }` i am returning from the `fromIterable[A]` implicit definition, which should probably be returning a class with a trait or something. I've tried reading a lot about implicits but i haven't figured it out yet out to be able to apply recursively this `toHtml` conversion without de-parametizing the fromIterable signature and defining several specific cases like `fromIterable(l : List[List[Any]])` or something like that... Can you guys please me give me some advices on how to achieve it and what am i doing wrong? Thanks!