When applying `map` to a `Set` you sometimes want the result not to be a set but overlook this

后端 未结 2 1648
孤城傲影
孤城傲影 2020-12-16 03:50

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:

         


        
相关标签:
2条回答
  • 2020-12-16 04:12

    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)
    
    0 讨论(0)
  • 2020-12-16 04:16

    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.

    0 讨论(0)
提交回复
热议问题