How to Scala, Written by a Java: 3 – for

(Find these examples at my GitHub)

In the imperative world, for is a loop. It lists statements to be executed on each item in a declared item holder. In Scala, for is not a loop. It is an expression which describes how to transform item holders. The for expression comes in two broad flavors. The first flavor acts like the classical for statement; it returns nothing. The second flavor acts as an expression and returns data.

  val list1: List[Int] = List.range(1, 101)
  val list2: List[Int] = List.range(-100, 0)</p>
<p>  val mutableList = scala.collection.mutable.ListBuffer[Int]()
  for(i <- list1){
    for(j <- list2){
      if(0 == i + j){
        mutableList += (i - j)
      }
    }
  }

This code crates two List objects, one holding the integers from 1 to 100, the other holding integers from -100 to -1. It creates a list of even numbers from 2 through 200. The following code performs the same operations using the Scala Collections API.

  val collectionApiList =
    list1.
    flatMap{i =>
      list2.filter{j =>
        0 == i + j
      }.
      map{j =>
        i - j
      }
    }

The Scala Collections API is well documented in the above link. This post focuses on for comprehensions so, we’ll only cover the portions of the Scala Collections API necessary to understand them fully.

1. Collection Basics

For comprehensions are syntactic sugar. They help write code that is more easily reasoned about than the standard collection manipulation techniques. Three methods are most notably exploited here: map, flatMap, filter.

1.1 Collection map

The map method on collections maps elements to other values and returns a transformed collection.

  val all1To10 = List.range(1, 11)
  val even2To20 =
    all1To10.
    map{item =>
      2 * item
    }

This code, for example, creates a list of integers from 1 to 10, and maps those elements to their doubles creating a list of even integers from 2 to 20. For the more academically inclined, if you take collections to be categories, map is a functor from one collection to another.

1.2 Collection flatMap

The flatMap method on collections maps elements to collections of other values and returns a single collection of the values within the collections mapped. This is different from map as a map from item to collections will return a collection of collections rather than a collection of all the elements from each collection.

  val rangesOf10_1To100 =
    all1To10.
    map{item =>
      val base = (10 * (item - 1)) + 1
      List.range(base, base + 10)
    }
  val all1To100 =
    all1To10.
    flatMap{item =>
      val base = (10 * (item - 1)) + 1
      List.range(base, base + 10)
    }

The first expression produces a List of Lists the first from 1 to 10, the second from 11 to 20 and do on until the last from 91 to 100. The second expression produces a List from 1 to 100. flatMap like map can be seen as a functor on collections.

1.3 Collection filter

Filter produces a new collection by filtering (as its name suggests) the elements of the original collection.

  val even2To10 =
    all1To10.
    filter{item =>
      0 == item % 2
    }

This produces a List of even Integers from 2 to 10 from the List of Integers from 1 to 10 by applying the defined filter function.

2 The for comprehension

Going back to our original example, we show it as a for comprehension.

  val comprehendedList =
    for{
      i <- list1
      j <- list2
      if 0 == i + j//The if statements are called guards
    }yield{
      i - j
    }

This is equivalent to the combination of map, flatMap and filter shown above. In fact, this is merely syntactic sugar provided by the Scala compiler for those calls. We could just as simply provide a list of multiples of 4 from 2 to 200:

  val comprehendedListBy4_1 =
    for{
      i <- list1 if 0 == i % 2
      j <- list2 if 0 == i + j
    }yield{
      i - j
    }
  val comprehendedListBy4_2 =
    for{
      i <- list1
      if 0 == i % 2
      j <- list2
      if 0 == i + j
    }yield{
      i - j
    }
  val comprehendedListBy4_3 =
    for{
      i <- list1
      j <- list2
      if 0 == i % 2
      if 0 == i + j
    }yield{
      i - j
    }
  val comprehendedListBy4_4 =
    for{
      i <- list1
      j <- list2
      if(0 == i % 2)
      if(0 == i + j)
    }yield{
      i - j
    }

All four of these expressions produce the same results: a List of Integer multiples of 4 from 4 to 200. In Scala, collections are a good example of what’s called a monad. Monads are basically a container with special methods which allow the container to create more of itself. Collection map, flatMap and filter are some of these special functions. Monads will be covered another time.

How to Scala, Written by a Java: 2 – Classes and Objects

(Find these examples at my GitHub))

Classes in scala are defined in a similar manner to how they are defined in Java.

class ClassBasic{
  
}

This is the simplest possible class. It has no constructor. It has no methods. It does nothing. Its uninteresting.

1. Constructors

Let’s give it a constructor.

class ClassBasic(_number: Int){
  private val number = _number
}

In Scala, the constructor is defined by the class body itself. the arguments to the constructor are placed after the class name. The body of the constructor is accounted by the statements and expressions in the body of the class.

To make the constructor private or protected you place the appropriate word before the arguments. The default access level in Scala is public.

class ClassBasic private(_number: Int){
  private val number = _number
}

Since the body of the class is the constructor body, there is a very limited sense of constructor overloading in Scala. You cannot have two different constructors that do wildly different things. However you can create overloaded constructors by calling another constructor.

class ClassBasic(_number: Int){
  def this(numberString: String) = this(numberString.toInt)
  def this() = this(0)
  
  private val number = _number
}

Here we have three constructors. The first constructor takes an Int and sets a member to the argument’s value. The second takes a String and calls the first constructor on the Int representation of the passed in String. The third takes no arguments and is a simple currying of the first constructor. All three of them at the end of the day call the same code.

Let’s make our class a little more interesting for the rest of the post.

class ClassBasic(_number: Int, _text: String){
  def this(_text: String) = this(0, _text)
  def this(_number: Int) = this(_number, "")
  
  private val number = _number
  private val text = _text
  
  private val concat = text + number
}

2. Methods

Methods are simply functions within a class. You define them like any other function.

  def getNumber(): Int = {
    number
  }
  def getText(): String = {
    text
  }
  def getValue(): String = {
    concat
  }

These define the get methods for the members of the class. More succinctly, we may write:

  def getNumber(): Int = number
  def getText(): String = text
  def getValue(): String = concat

Finally we’ll give our class all the bells and whistles by overriding the toString, equals and hashCode methods. In Scala, every method which override a method defined in a super class must be labelled with override.

  override def toString(): String = {
    "number = " + number + "\n" +
    "text = " + text + "\n" +
    "concat = " + concat
  }
  override def equals(any: Any): Boolean = {
    if(!any.isInstanceOf[ClassBasic]){
      false
    }
    else{
      val other = any.asInstanceOf[ClassBasic]
      (getNumber() equals other.getNumber()) &&
      (getText() equals other.getText())
    }
  }
  override def hashCode(): Int = concat.hashCode()

3. Non-instance Members

Non-instance members are typically referred to as static. These are members of the class which exist outside any instance of the class. In Scala, there is a clear distinction between instance and non-instance class members. The keyword object implies the members within exist outside the class instances. In Java terms the object is where all the static members of the class would be written. Taking advantage of this is easy. Our class becomes:

object ClassBasic{
  private val DEFAULT_NUMBER = 0
  private val DEFAULT_TEXT = ""
}
class ClassBasic(_number: Int, _text: String){
  def this(_text: String) = this(ClassBasic.DEFAULT_NUMBER, _text)
  def this(_number: Int) = this(_number, ClassBasic.DEFAULT_TEXT)
  
  private val number = _number
  private val text = _text
  
  private val concat = text + number
  
  def getNumber(): Int = number
  def getText(): String = text
  def getValue(): String = concat
  
  override def toString(): String = {
    "number = " + number + "\n" +
    "text = " + text + "\n" +
    "concat = " + concat
  }
  override def equals(any: Any): Boolean = {
    if(!any.isInstanceOf[ClassBasic]){
      false
    }
    else{
      val other = any.asInstanceOf[ClassBasic]
      (getNumber() equals other.getNumber()) &&
      (getText() equals other.getText())
    }
  }
  override def hashCode(): Int = concat.hashCode()
}

We say this is the class ClassBasic and its companion object. The companion object is defined in the same file as the class, takes the same name as the class and has access to all the members of the class. Likewise, the class has access to all the members of its companion object. Common uses are holding non-instance members and creating factory methods for your class (note the singleton is a special case of the factory).

  //factory
  def apply(number: Int, text: String): ClassBasic =
    new ClassBasic(number, text)
  def apply(number: Int): ClassBasic =
    new ClassBasic(number)
  def apply(text: String): ClassBasic =
    new ClassBasic(text)

The apply method is a special method in Scala. The language provides some syntactic sugar to make calling this method easy. The following two code snippets are provide the same results.

val number = 9
val text = "nine"
val basic1 = ClassBasic.apply(number, text)
val basic2 = ClassBasic.apply(number)
val basic3 = ClassBasic.apply(text)
println(basic1 + "\n" + basic2 + "\n" + basic3)
val number = 9
val text = "nine"
val basic1 = ClassBasic(number, text)
val basic2 = ClassBasic(number)
val basic3 = ClassBasic(text)
println(basic1 + "\n" + basic2 + "\n" + basic3)

Next we’ll take a look at for comprehensions.

How to Scala, Written by a Java: 1 – Functions

(Find these examples at my GitHub))
I, like many others breaking into Scala come from a Java background. I found one of the largest hurdles to using Scala effectively was getting used to functions as a data type: functions are first class citizens in Scala.

Functional Basics

Functions may be assigned to values and variables, passed into other functions as arguments, and returned by functions. Let’s take for instance a simple function which takes as arguments three integer arguments and returns a double representing the sum of the first two arguments divided by the third.

def foo(sum1: Int, sum2: Int, divisor: Int): Double={
  val sum = sum1 + sum2
  sum.toDouble / divisor.toDouble
}

1. Currying

Say, you have a need for a function which takes an int adds 3 to it then divides that sum by 4. at this point we could write a new function:

def plus3Over4AsNew(arg: Int): Double={
  val sum = arg + 3
  sum.toDouble / 4.0
}

But, this is very reminiscent of foo. Its the same code with immediate values in place of a couple variables. Luckily in Scala we can avoid the extra typing by creating a curried function.

If f(x, y, z) is a function, a currying of f would partially apply f over one or more of its arguments: g(x) = f(x, 3, 4) in our case. So, back to our example.

def plus3Over4(arg: Int): Double = foo(arg, 3, 4)

This helps us in a few ways. Most obviously, we save on typing. Secondly and more importantly, we save on maintenance; the curried function benefits from any optimizations or bug fixes applied to the first.

2. Functions as Arguments

Suppose you need to perform many different operations upon the result of summing two integers. Say, in one part of the application you need to divide the sum by 4 and in another part of the application you need to raise the result to the power of 4. We could write these as two separate functions:

def addThenDivideBy4AsNew(sum1: Int, sum2: Int): Double={
  val sum = sum1 + sum2
  sum / 4.0
}
def addThenPowerOf4AsNew(sum1: Int, sum2: Int): Double={
  val sum = sum1 + sum2
  Math.pow(sum, 4)
}

But, like before, there will be duplication of code. As both these functions sum the arguments before applying the final step. We can remove this duplication by having a function which takes the arguments to be summed and a third argument that is a function from Int to Double.

def addThen(sum1: Int, sum2: Int, function: (Int => Double)): Double={
  val sum = sum1 + sum2
  function(sum)
}
private def divideBy4(arg: Int): Double ={
  arg / 4.0
}
private def powerOf4(arg: Int): Double={
  Math.pow(arg, 4)
}
def addThenDivideBy4(sum1: Int, sum2: Int) =
  addThen(sum1, sum2, divideBy4)
def addThenPowerOf4(sum1: Int, sum2: Int) =
  addThen(sum1, sum2, powerOf4)

The syntax for declaring a function uses the ‘=>’ symbol (sometimes called ‘arrow’ or ‘missile’). To the left of the => lies the arguments. If using more than one argument, separate them by commas and surround the block by parentheses:

def multiArgumentFunctionFunction(
  multiArgumentFunction: ((Int, Int, Double) => Double)
): Double = 0.0

You can also pass in functions anonymously using the => operator and curly braces.

def addThenDouble(sum1: Int, sum2: Int): Double =
  addThen(sum1, sum2,
    {sum =>
      sum * 2.0
    }
  )

3. Functions as Values & Partially Applied Functions

This is pretty straight forward. You may assign functions to values (or variables) and pass them about your application:

val fourArgs: ((Int, Int, Int, Int) => Int) = {(a, b, c, d) =>
  a + b + c + d
}
def manyArgs(
  a: Int,
  b: Int,
  c: Int,
  d: Int,
  e: Int,
  f: Int,
  g: Int,
  h: Int,
  i: Int,
  j: Int
): Int={
  val first = fourArgs(a, b, c, d)
  
  val threeArgs: ((Int, Int, Int) => Int) = fourArgs(_, _, _, 4)
  val second = threeArgs(e,f,g)
  
  val twoArgs: ((Int, Int) => Int) = threeArgs(_, _, 0)
  val third = twoArgs(h, i)
  
  val oneArg: (Int => Int) = twoArgs(_, 7)
  val fourth = oneArg(j)
  
  fourArgs(first, second, third, fourth)
}

Scala makes extensive use of the ‘_’ operator. In this context, it implies the function is partially applied. A partially applied function is similar to currying. The difference is, a curried function returns the result of the underlying function where partial application provides another function object.

3. Closures

Again, functions are first-class citizens in Scala. This means they may be declared anywhere any other data type may be declared including within another function:

def function(): Int={
  def innerFunction() = 7
  innerFunction()
}

This will return 7. Like any other value, you may define a function using other values and variables in scope.

def function(): Int={
  var variable = 1
  def innerFunction(arg: Int): Int={
    val increment = variable * 2
    arg + increment
  }
  variable = innerFunction(variable)
  innerFunction(variable)
}

This will return 9.
The var variable is declared outside of innerFunction’s scope yet its able to be read and modified by innerFunction. This is similar to using final pointers in Java anonymous classes. But, what’s happening in Scala is the compiler sees a reference to variable but cannot find that reference in scope so it goes to the next scope up, function’s scope, and checks there, this continues until variable is found. All the references required by innerFunction is called the closure of innerFunction (think set theory). The compiler brings in what’s necessary and no more.
With closures, objects that are needed by multiple nested scopes can live on even after their enclosing scope is exited. As long as a function which closes over an object is still in play, that object is still in play.

Next we’ll take a look at how to create classes.