Activity Sequence: Scala

This is as an illustrative example for how Expression Tutor activities could be used in a Scala course. The content on this page may not be understandable for someone without significant prior knowledge.

Scala supports expressions that look familiar to programmers of languages like Java or C. However, behind the scenes these expressions get desugared. You can use the Scala REPL to show these desugarings: enter an expression followed by //print, hit the TAB key, and the REPL shows the desugared version.

Operators as methods

Chapter 3 of Programming in Scala, First Edition - Next Steps in Scala

"Scala doesn't technically have operator overloading, because it doesn't actually have operators in the traditional sense. Instead, characters such as +, -, *, and / can be used in method names. Thus, when you typed 1 + 2 into the Scala interpreter in Step 1, you were actually invoking a method named + on the Int object 1, passing in 2 as a parameter. As illustrated in Figure 3.1, you could alternatively have written 1 + 2 using traditional method invocation syntax,(1).+(2)"

Here is an example from the REPL:

scala> 1 + 2 //print (1).+(2) // : Int(3)

Below you can see the desugared expression as a tree. The root node, .+(), is marked with a start. It represents the method invocation. The method is invoked on 1 and receives 2 as argument.

Scala not only desugars binary operators like +, but also unary operators. For example, the unary bitwise complement operator ~ is translated to a call of a method named unary_~.

Now draw the corresponding desugared expression tree below. Double click on a node to make it the root of the tree (the root is marked with a star). You do not need all the available nodes. You can delete a node by clicking on it and then clicking on the red circle.

Applying parenthesis to expressions

Chapter 3 of Programming in Scala, First Edition - Next Steps in Scala

"Another important idea illustrated by this example will give you insight into why arrays are accessed with parentheses in Scala. Scala has fewer special cases than Java. Arrays are simply instances of classes like any other class in Scala. When you apply parentheses surrounding one or more values to a variable, Scala will transform the code into an invocation of a method named apply on that variable. So greetStrings(i) gets transformed into greetStrings.apply(i). Thus accessing an element of an array in Scala is simply a method call like any other. This principle is not restricted to arrays: any application of an object to some arguments in parentheses will be transformed to an apply method call. Of course this will compile only if that type of object actually defines an apply method. So it's not a special case; it's a general rule."

Now assume a variable greetStrings defined as follows:

val greetStrings = new Array[String](3) greetStrings(0) = "Hello" greetStrings(1) = ", " greetStrings(2) = "world!\n"

Let's desugar an expression that accesses an element in this array. We can use the REPL and hit TAB:

scala> val i = 1 val i: Int = 1 scala> greetStrings(i) //print greetStrings.apply(i) // : String

This kind of desugaring is not limited to arrays. Here is another example:

scala> Option(1) //print scala.Option.apply[Int](1) // : Option[Int]

Construct the tree corresponding to the desugared expression. Delete the superfluous node by selecting it and clicking the red circle. Double click on a node to denote it as the root.

For-expressions

Chapter 23 of Programming in Scala, First Edition - For Expressions Revisited

"What about for loops that simply perform a side effect without returning anything? Their translation is similar, but simpler than for expressions [...] For instance, the expression:

for (x <- expr_1) body

translates to:

expr_1 foreach (x => body)

Here is an example for loop, desugared in the REPL:

scala> for (n <- 1 to 3) { println(n) } //print scala.Predef.intWrapper(1).to(3).foreach[Unit](((n: Int) => scala.Predef.println(n))) // : Unit

The desugared version can be represented as the following expression tree. In the tree, annotate each subexpression with its type. For this, click on each tree node and enter its type. Note that some types, like Int and Int => Unit are available for use with a single click. The node .to() produces a scala.collection.immutable.Range.Inclusive.

References: Scala Language Specification - Chapter 6. Expressions