Home > Mobile >  Explain the implementation of Scala's immutable List map method
Explain the implementation of Scala's immutable List map method

Time:01-19

I'm just trying to understand the logic behind the implementation of Scala's List[A] map[B]

Scala 2.12 describes it as such:

final override def map[B](f: A => B): List[B] = {
    if (this eq Nil) Nil else {
      val h = new ::[B](f(head), Nil)
      var t: ::[B] = h
      var rest = tail
      while (rest ne Nil) {
        val nx = new ::(f(rest.head), Nil)
        t.next = nx
        t = nx
        rest = rest.tail
      }
      releaseFence()
      h
    }
  }

I am confused as to how can h be a new list with f applied to each of its elements. h is declared as a val, so how can it be affected by what happens inside the while loop?

does reassigning the value of t retroactively changes the value of h? Wouldn't that make h mutable though?

CodePudding user response:

I'll use as a reference the version of List.scala on latest available commit on the 2.12.x branch of the Scala repository on GitHub, which is slightly different but similar enough.

final override def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That = {
  if (isLikeListReusableCBF(bf)) {
    if (this eq Nil) Nil.asInstanceOf[That] else {
      val h = new ::[B](f(head), Nil)
      var t: ::[B] = h
      var rest = tail
      while (rest ne Nil) {
        val nx = new ::(f(rest.head), Nil)
        t.tl = nx
        t = nx
        rest = rest.tail
      }
      h.asInstanceOf[That]
    }
  }
  else super.map(f)
}

First notice that a val is an immutable reference, not a immutable value. You can mutate an object through an immutable reference if you have access to its mutable state. Notice how t is taken as a mutable reference to h (the value which is ultimately returned) and then a singleton list is associated with t.tl = nx (which corresponds to t.next = nx in your snippet, I believe).

That tl field is the tail of the :: type, kept as mutable state for internal usage only, as you can see here:

/** A non empty list characterized by a head and a tail.
 *  @param head the first element of the list
 *  @param tl   the list containing the remaining elements of this list after the first one.
 *  @tparam B   the type of the list elements.
 *  @author  Martin Odersky
 *  @since   2.8
 */
@SerialVersionUID(509929039250432923L) // value computed by serialver for 2.11.2, annotation added in 2.11.4
final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] {
  override def tail : List[B] = tl
  override def isEmpty: Boolean = false
}
  • Related