Scala 2.8 breakOut

后端 未结 4 1067
无人及你
无人及你 2020-11-22 03:43

In Scala 2.8, there is an object in scala.collection.package.scala:

def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing         


        
4条回答
  •  情书的邮戳
    2020-11-22 03:45

    Daniel Sobral's answer is great, and should be read together with Architecture of Scala Collections (Chapter 25 of Programming in Scala).

    I just wanted to elaborate on why it is called breakOut:

    Why is it called breakOut?

    Because we want to break out of one type and into another:

    Break out of what type into what type? Lets look at the map function on Seq as an example:

    Seq.map[B, That](f: (A) -> B)(implicit bf: CanBuildFrom[Seq[A], B, That]): That
    

    If we wanted to build a Map directly from mapping over the elements of a sequence such as:

    val x: Map[String, Int] = Seq("A", "BB", "CCC").map(s => (s, s.length))
    

    The compiler would complain:

    error: type mismatch;
    found   : Seq[(String, Int)]
    required: Map[String,Int]
    

    The reason being that Seq only knows how to build another Seq (i.e. there is an implicit CanBuildFrom[Seq[_], B, Seq[B]] builder factory available, but there is NO builder factory from Seq to Map).

    In order to compile, we need to somehow breakOut of the type requirement, and be able to construct a builder that produces a Map for the map function to use.

    As Daniel has explained, breakOut has the following signature:

    def breakOut[From, T, To](implicit b: CanBuildFrom[Nothing, T, To]): CanBuildFrom[From, T, To] =
        // can't just return b because the argument to apply could be cast to From in b
        new CanBuildFrom[From, T, To] {
          def apply(from: From) = b.apply()
          def apply()           = b.apply()
        }
    

    Nothing is a subclass of all classes, so any builder factory can be substituted in place of implicit b: CanBuildFrom[Nothing, T, To]. If we used the breakOut function to provide the implicit parameter:

    val x: Map[String, Int] = Seq("A", "BB", "CCC").map(s => (s, s.length))(collection.breakOut)
    

    It would compile, because breakOut is able to provide the required type of CanBuildFrom[Seq[(String, Int)], (String, Int), Map[String, Int]], while the compiler is able to find an implicit builder factory of type CanBuildFrom[Map[_, _], (A, B), Map[A, B]], in place of CanBuildFrom[Nothing, T, To], for breakOut to use to create the actual builder.

    Note that CanBuildFrom[Map[_, _], (A, B), Map[A, B]] is defined in Map, and simply initiates a MapBuilder which uses an underlying Map.

    Hope this clears things up.

提交回复
热议问题