Scala and for loop

Versions: Scala 2.12.1

For loop was maybe the most used iterative structure in Java before the introduction of lambda expressions. With this loop we're able to write everything - starting with a simple mapping and terminating with a more complex "find-first element in the collection" feature. In Scala we can made these operations with monads and despite that, for loop is a feature offering a lot of possibilities.

This post talks about for loops in Scala. The first section presents the syntax and some general information. The second one lists what we can do with the loop and gives an example for each point. The last section mentions recursion that can be used instead of for loops.

For loop and Scala

Scala's for loop definition is very similar to a foreach loop because it clearly suggests the iteration over a collection of items. From the big picture the syntax of this loop looks like for (myItem <- expression) where expression is usually a collection or a range, if we want to access the collection elements by index.

Right now an important point to notice is the lack of native support for break keyword, able in other languages as Java to stop the iteration. Another not supported operation is the one expressed with continue, used to skip loop execution for given criteria.

For loop features

Among the features of for loop in Scala we can distinguish:

For loop and recursion

A lot of programmers are familiar with for loops. However functional programmers (or at least some of them) tell that using for loops is not functional-friendly. It introduces mutability. Thus, the concept voted to replace them are recursion functions. After all they work purely on functions that in this paradigm are first-class citizens.

But recursion brings also some dangerous points. The first and maybe the most critical is the stack size. Each method has allocated a new stack frame and if the number of calls increases we can encounter a StackOverflow exception. Hopefully, to mitigate the issue, Scala proposes @tailrec annotation. The recursive methods annotated with it are translated at compilation time into loops. With that we can keep the functional style and avoid stack frames problem.

@tailrec is a constraint. It means that if a function can't be transformed the compilation error is produced:

Error:(162, 11) could not optimize @tailrec annotated method add: it is neither private nor final so can be overridden
      def add(nr: Int, limit: Int): Int = { 

Recursion is also used in Scala library classes. For instance it's implemented for Stream's foreach method:

@tailrec
override final def foreach[U](f: A => U) {
  if (!this.isEmpty) {
    f(head)
    tail.foreach(f)
  }
}

And translating for loop into recursive function call is pretty straightforward:

"recursion" should "be able to replace for loop" in {
  // we can simulate for loop behavior with a recursion call
  @tailrec
  def findFirst(toFind: Int, items: Seq[Int]): Option[Int] = {
    val currentNumber = items.head
    if (currentNumber == toFind) {
      Some(currentNumber)
    } else if (items.size == 1) {
      None
    } else {
      findFirst(toFind, items.slice(1, items.size))
    }
  }

  val number5 = findFirst(5, 1 to 10)

  number5 shouldBe defined
  number5.get shouldEqual 5
  val number101 = findFirst(101, 1 to 10)
  number101 shouldBe empty
}

Scala's for loop, as the whole language, mixes well procedural and functional style of writing. As shown in the first part, the loop resembles a lot to foreach loop where one item is read from collection eagerly. The eagerness doesn't change with comprehensions covered in the 2nd part, that are more like an alternative to map. But for the purists of functional programming, for loops are not completely good because they introduce mutability. It's why they can be replaced with recursion - even though under-the-hood it's converted into a loop with @tailrec annotation. It doesn't mean the for loops are bad and ugly. Their use depend a lot on coding style of the project. After all, that must be a pragmatic approach somewhere in the judgement.