tags:

views:

278

answers:

3

The following code (copied from a question from about a year ago) works fine under Scala 2.7.7, but does not behave correctly under Scala 2.8.0 (Beta 1, RC8).

import scala.xml

class Person(name : String, age : Int) {
    def toXml(): xml.Elem =
        <person><name>{ name }</name><age>{ age }</age></person>
}

def peopleToXml(people: Array[Person]): xml.Elem = {
    <people>{ for {person <- people} yield person.toXml }</people>
}

val data = Array(new Person("joe",40), new Person("mary", 35))

println(peopleToXml(data))

The output (according to 2.7.7) should be:

<people><person><name>joe</name><age>40</age></person><person><name>mary</name><age>35</age></person></people>

but instead comes out as:

<people>\[Lscala.xml.Elem;@17821782</people>

How do I get this to behave as it did in 2.7.x?

+1  A: 

The yield is returning a real java Array which is being string-ified per normal Java rules. This is given away by "[Ltype;@ref" output.

Perhaps you can convert it (people in or result out) to a List and get better "to string" semantics. I imagine Scala 2.7.7 returns a different (non-array) collection type entirely.

Fighting Bit Rot With Types talks about new Scala 2.8 Collections setup "in general" but leaves out specifics. There might be more goodness on scala-lang.org and, as always, welcome to visit #scala on freenode :-)

pst
Sort of figured it was something like that, but couldn't work out how (or where) to get the Java array converted back to something useful. Thanks.
Shadowlands
+3  A: 

This code needs two conversions. First, you have to convert Array[xml.Elem] which is returned by the for comprehension to Seq[xml.Elem] and then an implicit conversion in xml.NodeSeq will convert this one to xml.NodeSeq.

If you change peopleToXml to the following, it will work as expected:

def peopleToXml(people: Array[Person]): xml.Elem = {
  val a: Seq[xml.Elem] = for {person <- people} yield person.toXml
  <people>{a}</people>
}

You can also do the following if you like it short :-)

def peopleToXml(people: Array[Person]): xml.Elem = {
  <people>{(for {person <- people} yield person.toXml): Seq[xml.Elem]}</people>
}
Michel Krämer
Thanks Michael, that will work nicely.
Shadowlands
+4  A: 

As pst says, the output is what you would expect for a real Java array. One of the changes between 2.7 and 2.8 is that Array[T] is now exactly a real Java array. The new collections design means that doing the Scala collection operations on a Java array (e.g. map in your example) will return a Java array as the result. This means that the following

for {person <- people} yield person.toXml

Used to not return a Java array, but now does. You can fix the problem by changing the the type of the people argument of your peopleToXml method as follows:

def peopleToXml(people: Seq[Person]): xml.Elem = {
    <people>{ for {person <- people} yield person.toXml }</people>
}

This will mean that when you call this method with an Array argument, the array will be implicitly converted to a WrappedArray that behaves as you expect. This will also have the benefit of allowing you to call this method with non-array collections.

Ben Lings