anonygrits
anonygrits

Reputation: 1499

Scala for ( ) vs for { }

I'm trying to understand for comprehensions in Scala, and I have a lot of examples that I sort of understand...

One thing I'm having a hard time figuring out is for ( ) vs for { }. I've tried both, and it seems like I can do one thing in one but it breaks in the other.

For example, this does NOT work:

def encode(number: String): Set[List[String]] =
  if (number.isEmpty) Set(List())
  else {
    for (
      split <- 1 to number.length
      word <- wordsForNum(number take split)
      rest <- encode(number drop split)
    ) yield word :: rest
  }.toSet

However, if you change it to { }, it does compile:

def encode(number: String): Set[List[String]] =
  if (number.isEmpty) Set(List())
  else {
    for {
      split <- 1 to number.length
      word <- wordsForNum(number take split)
      rest <- encode(number drop split)
    } yield word :: rest
  }.toSet                                 

These examples are from a Coursera class I'm taking. The professor didn't mention the "why" in the video & I was wondering if anyone else knows.

Thanks!

Upvotes: 7

Views: 1246

Answers (2)

som-snytt
som-snytt

Reputation: 39577

From the syntax in the spec, it might seem that parens and braces are interchangeable:

http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#for-comprehensions-and-for-loops

but because the generators are separated by semis, the following rules kick in:

http://www.scala-lang.org/files/archive/spec/2.11/01-lexical-syntax.html#newline-characters

I have read and understood that section in the past, from which I vaguely recall the gist that newlines are enabled in the braces, which is to say, a newline char is taken as nl which serves as a semi.

So you can put the generators on separate lines instead of using semicolons.

This is the usual "semicolon inference" that lets you not write semicolons as statement terminators. So the newline in the middle of the generator is not taken as a semi, for instance:

scala> for (c <-
     | List(1,2,3)
     | ) yield c+1
res0: List[Int] = List(2, 3, 4)

scala> for { c <-
     | List(1,2,3)
     | i = c+1
     | } yield i
res1: List[Int] = List(2, 3, 4)

Upvotes: 8

Andreas Du Rietz
Andreas Du Rietz

Reputation: 1171

In Scala () are usually for when you only have one statement. Something like this would have worked:

def encode(number: String): Set[Int] =
  if (number.isEmpty) Set()
  else {
    for (
      split <- 1 to number.length // Only one statement in the comprehension
    ) split
  }.toSet

Add one more and it would fail to compile. The same is true for map for example

OK

List(1,2,3).map(number =>
  number.toString
)

Not OK (have to use curly braces)

List(1,2,3).map(number =>
  println("Hello world")
  number.toString
)

Why that is. I have no idea :)

Upvotes: 4

Related Questions