Wednesday, January 25, 2012

For-confusion in Scala

In Scala, the "for" structure can throw off the Java programmer. For one, it looks enough like a Java "for" loop for a Java programmer to make a guess on how it works -- but that guess is likely to be wrong. For another, the for-loop is implemented in Scala as a special case of the for-comprehension, and its behaviour and purpose are quite different.

This post tackles the second of the two causes for confusion.
What is the difference between a for-loop and a for-comprehension? How can we tell them apart?

We have a sequence:
val seq = Seq(1,2,4)


Here's a super-simple for-loop, and its REPL output:
scala> for (s <- seq) println ("number " + s)
number 1
number 2
number 4


Here's a super-simple for-comprehension:
scala> for (s <- seq) yield 2 * s
res15: Seq[Int] = List(2, 4, 8)


The first part, for (s <- seq), is identical. However, these two are different constructs. Most blatantly, a for-loop returns Unit and a for-comprehension returns a sequence.

The for-loop is an imperative construct; it executes a series of statements. In this aspect it is the same as a for-loop in Java. You can put curly braces around the body of the for-loop and add as many statements as you like in there. This for-loop is implemented under the covers as a call to foreach on the sequence.

The for-comprehension is a functional construct; it performs a translation. The body of a for-comprehension contains exactly one statement, and it begins with yield. You can't put curly braces around the body of a for-comprehension; you'll get "illegal start of statement." Each time yield is executed, an element is added to the output sequence. Under the covers, this simple for-comprehension is implemented through the map method on the sequence. map is very familiar to functional programmers.

Keep in mind that since the for-loop is a special case of for-comprehension in Scala, all the power of the data generation and filtering in a for-comprehension are available to the for-loop. These simple examples here do not illustrate the power of each of these constructs; my purpose is to clarify the differences. When you use a for-loop, you're doing imperative programming, familiar to OO developers. When you use a for-comprehension, you're doing functional programming. Scala supports both paradigms, which is great, but watch out for confusion.

2 comments:

  1. Scala for-comprehension are also a gateway drug to Monads:

    http://www.slideshare.net/daltontf/grokking-monads-inscala

    ReplyDelete
  2. Thanks, Tim! Wish I'd been there for your talk.

    ReplyDelete