Programming  /  Scala August 23, 2010

Learning Scala part six – If statements and Loops

In the previous part of the Learning Scala series we looked at defining and invoking methods in Scala which has some interesting characteristics. In this part we’ll look at how standard programming constructs such as if statements and different types of loops work in Scala. As we’ll see they to have some interesting characteristics compared to languages such as Java or C#.

If statements are expressions

In Java, C# and many other programming languages conditional branches are statements that can control execution of code but don’t produce any resulting value themselves. In Scala however if statements are expressions meaning that they return a (single) value. This means that we can re-write if statements such as this:

if(a == b) 
    result = "It's true" 
else 
    result = "Not really"

…like this:

result = if(a == b) 
    "It's true" 
else 
    "Not really"

//Or, as a nice one-liner
result = if(a == b) "It's true" else "Not really"

For comprehensions

The for loop in Scala is referred to as “for comprehension” or “for expression” in the Scala community and it can do some pretty funky stuff. To have something to work with in our examples, let’s begin by creating a simple Person class and a list of Person instances:

class Person(val name: String, val female: Boolean)

val people = List(
    new Person("Cliff Barnes", false),
    new Person("J. R. Ewing", false),
    new Person("Sue Ellen Ewing", true),
    new Person("Ellie Ewing", true),
    new Person("Bobby Ewing", false)
    new Person("Donna Culver Krebbs", true))

We can traverse the list, “comprehending” each instance using a for loop that syntactically is very similar to Java’s.

for(person <- people)
    println(person.name)

As you might guess the code above will print the name of each Person object in the list. The same loop in Java would have looked like this:

for(Person person : people)
    system.out.println(person.name);

And in C# it would have looked like this:

foreach (var person in people)
    Console.WriteLine(person.name);

As we can see the for loop is very similar to Java’s, except it doesn’t require us to specify the type of the temporary variable as the compiler can infer it and the : operator is replaced with the <- operator which can be thought of as directing instance from the sequence being iterated over to the temporary variable. The <- operator is called a generator and a for expressions must always start with a generator which specifies which collection of objects to iterate over and a temporary variable that holds the current object.

Compared to the C# equivalent there is more differences, most notably that we in C# use the foreach keyword to traverse a sequence of objects instead of the for keyword.

Filtering

While the functionality of the for loop pretty much ends with the above example in Java or C# Scala’s version of the for loop has a lot more to offer. First of all it' supports filtering. Let’s say that we only want to print the names of the women in the collection. We can add a filter for that:

for(person: Person <- people
    if person.female)
    println(person.name)

Or let’s say that we want all the men that are named Ewing, we can do that using multiple filters:

for(person: Person <- people
    if !person.female
    if person.name.contains("Ewing"))
    println(person.name)

Another interesting thing that we can do except adding filters inside the first part of the for expression is declaring variables there. Using that we can rewrite the previous example like this:

for (person: Person <- people
    if !person.female;
    name = person.name;
    if name.contains("Ewing"))
    println(name)

We made three changes in the code above compared to how it looked before. First of all we extract the name of the Person object into a variable named name inside the first part of the for expression. We then use that to when filtering the objects being iterated over by name. Finally, in the second part of the for expression we print the name variable instead of the Person object’s name.

Note that we had to add semicolons after the first filter and the declaration of the name variable in the code above. In Scala for comprehensions can be defined using either parenthesis or curly braces. If we use curly braces we don’t have to add semicolons to separate filters and variables using semicolons but can instead use line breaks. In other words we can remove the semi colons in our example by replacing the parenthesis with curly braces.

for {person: Person <- people
    if !person.female
    name = person.name
    if name.contains("Ewing")}
    println(name)

Nested loops

The first part of the for expression can actually contain multiple generators which then forms a nested loop. Let’s say that we have two separate lists of Person objects grouped together in a third list, like this:

val ewings = List(
    new Person("J. R. Ewing", false),
    new Person("Sue Ellen Ewing", true),
    new Person("Ellie Ewing", true),
    new Person("Bobby Ewing", false))

val outsiders = List(
    new Person("Cliff Barnes", false),
    new Person("Donna Culver Krebbs", true))
    
val groups = List(ewings, outsiders)

To loop over all of the Person objects nested inside the list named groups we can create a nested loop.

for(people <- groups; person <- people)
    println(person.name)

Yielding

Using the yield keyword we can use for expressions to create new collections. To illustrate, we can rewrite the first example that we looked at, printing names from a single list of Person objects named people like this:

val names = for(person <- people) yield person.name

for(name <- names) println(name)

The above code creates a new collection named names containing all of the names of the people in the collection of Persons. It then prints all of the names using a second for expressions. This is of course not a very realistic example but it illustrates that the yield keyword can be used in the second part of for expressions to create new collections.

For more on the subject of the yield keyword there are couple of very interesting answers to this question on StackOverflow.

More about for comprehensions

Besides the book that I’m using for learning Scala I found an article titled The Scala “for comprehension” from a Java perspective by Doug Pardee to be very useful in learning about for comprehensions. Go read it if you want to learn more about how it all works.

Other types of loops

Other than for comprehension Scala also have the common while and do while-loops which works as expected when coming from C# or Java. The below script will wait for user input from the console until the user types “exit”.

var input = ""
while(input != "exit")
    input = readLine();

As will the script below. Note that the variable named input is initialized with the value “exit” but as this is a do-while loop the code inside it will execute anyway as the condition is evaluated after the first iteration.

var input = "exit"
do {
    input = readLine();
    } while(input != "exit")

Recap and a look at what’s next

We’ve just taken a look at if statements which are expressions in Scala and loops, especially for comprehensions which are quite powerful. There is definitely more to learn about for comprehension so I recommend you to check out the sites that was linked to if you have an appetite to learn more about it.

In the next part we’ll look at something that I find really, really interesting – traits. Oh, and we’ll also get to know the majestic Frigatebird!

About this post and the Learning Scala series

This post is a part of a series of posts in which I describe my experiences while trying to learn Scala. I try to do it in the form of a tutorial as I find doing so is an excellent way of consolidating knowledge and hopefully I can also help others. However, keep in mind that I’m in no way an expert on Scala. This is just a way to document what I’ve learned so far and there might be some things that I’ve misunderstood. If you’ve found such a thing and would like to help me correct that misunderstanding, or if you would like to leave any other kind of feedback don’t hesitate to leave a comment!

You can find links to all posts in this series at the bottom of the first post.

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Joel Abrahamsson

Joel Abrahamsson

I'm a passionate web developer and systems architect living in Stockholm, Sweden. I work as CTO for a large media site and enjoy developing with all technologies, especially .NET, Node.js, and ElasticSearch. Read more

Comments

comments powered by Disqus

More about Scala