Adam Wojtuniak's Blog

September 24, 2010

Scala for comprehensions

Filed under: Scala — adamwojtuniak @ 8:20 pm
Tags: , ,

In this post I am going to write about one of the most powerful feature in Scala a for comprehension.
Syntax from Scala 2.8.0 Spec:

  • Expr1 ::= ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’) {nl} [‘yield’] Expr
  • Enumerators ::= Generator {semi Enumerator}
  • Enumerator ::= Generator | Guard | ‘val’ Pattern1 ‘=’ Expr
  • Generator ::= Pattern1 ‘<-‘ Expr [Guard]
  • Guard ::= ‘if’ PostfixExpr
  • For comprehension is evaluating all bindings for each bindings generated by enumerators. For comprehension with yield evaluates each bindings and collect result. What is the enumerator? It could be a generator, which produces values like an iterator: ‘a <- list’. Then it could be a guard (filter) ‘a>1’ and finally could be a value definition ‘val’ pattern ‘=’ expression: val x=a+1.
    For comprehension always starts with generator and could be followed by other generators, value definition and then guards.
    Generators and guards are translated to map,withFilter,flatmap and foreach.
    Here is a quote from Scala 2.8.0 Spec (page 89/90 – For Comprehensions and For Loops) about translation rules:

    The translation scheme is as follows. In a first step, every generator p <-e, where p is not irrefutable (§8.1) for the type of e is replaced by
    p <- e.withFilter { case p => true; case _ => false }

    Then, the following rules are applied repeatedly until all comprehensions have been

    • A for comprehension for (p <-e) yield e0 is translated to { case p => e0 }.
    • A for loop for (p <-e) e0 is translated to
    e.foreach { case p => e0 }.
    • A for comprehension for (p <-e;p0 <-e0 . . .) yield e00 ,
    where . . . is a (possibly empty) sequence of generators, definitions, or guards,
    is translated to
    e.flatMap { case p => for (p0 <-e0 . . .) yield e00 } .
    • A for loop for (p <-e;p0 <-e0 . . .) e00 .
    where . . . is a (possibly empty) sequence of generators, definitions, or guards,
    is translated to
    e.foreach { case p => for (p0 <-e0 . . .) e00 } .
    • A generator p <-e followed by a guard if g is translated to a single generator
    p <-e.withFilter((x1, . . . , xn) => g ) where x1, . . . , xn are the free
    variables of p.
    • A generator p <-e followed by a value definition p0 = e0 is translated to the
    following generator of pairs of values, where x and x0 are fresh names:
    (p, p0) <-for (x@p <-e) yield { val x0@p0 = e0; (x, x0) }


    private def loadDescriptors(bundle: Bundle): Array[URL] = {
    	val descriptorLocations = bundle.getHeaders().get( "Service-Component" ).asInstanceOf[String]
    	val entries = for(descriptor <-descriptorLocations.split(","); val entry = descriptor.split("/") ) yield (entry(0),entry(1))
    	for((path, filen) <- entries; url <- bundle.findEntries(path,filen,false).toTraversable) yield url.asInstanceOf[URL]

    In the example I am reading the header “Service-Component” and I have to find service descriptor URL.
    In the first for comprehension first enum is an Array[String] generator then for each element in the array value definition enum is evaluated and finally yield collects values from each evaluation and every result I am collecting as a tuple of file path and file name.
    Second for comprehension is pretty same, two generators and collecting values as URL objects.

    Example with a guard:

    private def readFromURL(url: URL): Iterator[String] = {
        for (line <- Source.fromURL(url).getLines() if ((line.length > 0) && (line.charAt(0) != '#')))
        yield line

    The for comprehension is translated to:

      		withFilter { line => ((line.length > 0) && (line.charAt(0) != '#'))}.map { case line => line }

    For more information please have a look at Scala 2.8.0 Specification. It’s included in Scala distribution.

    Leave a Comment »

    No comments yet.

    RSS feed for comments on this post. TrackBack URI

    Leave a Reply

    Fill in your details below or click an icon to log in: Logo

    You are commenting using your account. Log Out / Change )

    Twitter picture

    You are commenting using your Twitter account. Log Out / Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out / Change )

    Google+ photo

    You are commenting using your Google+ account. Log Out / Change )

    Connecting to %s

    Blog at

    %d bloggers like this: