Or how to avoid accidental removal of duplicates when mapping a Set
?
This is a mistake I\'m doing very often. Look at the following code:
Workarounds:
def countSubelements[A](sl: Set[List[A]]): Int = sl.toSeq.map(_.size).sum
def countSubelements[A](sl: Set[List[A]]): Int = sl.foldLeft(0)(_ + _.size)
It seems there will be many possible "gotcha's" if one expects a Seq
and gets a Set
. It's not a surprise that method implementations can depend on the type of the object and (with overloading) the arguments. With Scala implicits, the method can even depend on the expected return type.
A way to defend against surprises is to explicitly label types. For example, annotating methods with return types, even if it's not required. At least this way, if the type of graph.nodes
is changed from Seq
to Set
, the programmer is aware that there's potential breakage.
For your specific issue, why not define your ownmapToSeq
method,
scala> def mapToSeq[A, B](t: Traversable[A])(f: A => B): Seq[B] =
t.map(f)(collection.breakOut)
mapToSeq: [A, B](t: Traversable[A])(f: A => B)Seq[B]
scala> mapToSeq(Set(Seq(1), Seq(1,2)))(_.sum)
res1: Seq[Int] = Vector(1, 3)
scala> mapToSeq(Seq(Seq(1), Seq(1,2)))(_.sum)
res2: Seq[Int] = Vector(1, 3)
The advantage of using breakOut: CanBuildFrom
is that the conversion from a Set
to a Seq
has no additional overhead.
You can make use the pimp my library pattern to make mapToSeq
appear to be part of the Traversable
trait, inherited by Seq
and Set
.