Why does Scala's toSeq convert an immutable Set to a mutable ArrayBuffer?

W.P. McNeill picture W.P. McNeill · Dec 4, 2012 · Viewed 7.3k times · Source

If I call toSeq on an immutable Set collection I get an ArrayBuffer.

scala> Set(1,2,3).toSeq // returns Seq[Int] = ArrayBuffer(1, 2, 3)

This surprises me. Given Scala's emphasis on using immutable data structures, I expect to get back an immutable sequence like a Vector or List instead of a mutable ArrayBuffer. The returned ordering of the set elements should of course be undefined, but there doesn't seem to be any semantic reason why that ordering should also be mutable.

In general, I expect Scala operations to always produce immutable results unless I explicitly request a mutable one. This has been my assumption all along, but it is an incorrect one here, and I actually just spent an hour debugging a problem where the unexpected presence of an ArrayBuffer led to a runtime error in a match statement. My fix was to change Set(...).toSeq to Set(...).toList, but this feels like a hack because there's nothing about my application that requires a list in particular at that point.

Is having Set(...).toSeq return a mutable object a flaw in Scala's implementation, or is there a principle I am misunderstanding here?

This is Scala 2.9.2.

Answer

som-snytt picture som-snytt · Dec 4, 2012

Here is the recent thread on whether Seq should mean immutable.Seq.

Roland Kuhn:

collection.Seq not having mutators is not at all a valid defense!

The example of mutable varargs is rather sneaky.

Recently,

scala> Set(1,2,3)
res0: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

scala> res0.toSeq
res1: Seq[Int] = ArrayBuffer(1, 2, 3)

scala> res0.to[collection.immutable.Seq]
res2: scala.collection.immutable.Seq[Int] = Vector(1, 2, 3)