Installation

  1. Section Other ways to install Scala, download for Linux (link)
  2. Define SCALA_HOME based on link
  3. Add $SCALA_HOME/bin to $PATH

Now, create file HelloWorld.scala:

1
2
3
4
5
6
7
object HelloWorld {
  def main(args: Array[String]): Unit = {
    val language = "Scala"
    val msg = s"Hello World, My Lang is ${language}"
    println(msg)
  }
}

To Compile & Run:

1
2
3
scalac HelloWorld.scala
scala -cp . HelloWorld
java -cp .:$SCALA_HOME/lib/* HelloWorld
  • Line 2, use scala command to execute the class
  • Since scalac generates bytecode, line 3 executes class by java command with extra classpath

The HelloWorld class can be rewritten as

1
2
3
4
object HelloWorld extends App {
  val language = "Scala"
  println(s"Hello World, My Lang is $language")
}

Basics

  • No semicolon to terminate lines, unless multiple expressions on the same line
  • No need for identical name of the class & its file
    • in spite of Java, Scala file names do not need to be the same as class names
  • No need of directory structure corresponding to package hierarchy
    • however, the correspondence is recommended
  • No need to public modifier
    • by default everything is public
  • Same operator precedence as in Java
    • In fact all operators are the object methods using as the infix operator syntax
  • Unit of Scala = void of Java
    • however Unit has a single value which is ()
  • Any of Scala = Object of Java
  • No need using return
    • automatically the value of last expression of every block is returned
  • No checked exception
  • No try-with-resource as in Java
  • No?: operator in Scala, use if-else instead (section)
  • No primitive type in Scala, everything is object (the reason Scala is pure OO while Java is not)
  • Relaxed method name, such as +, *, …, similar to operator overloading
    • In fact 2 + 3 is 2.+(3)
  • No enum keyword and enumeration like Java
    • use object extends Enumeration
  • Using object keyword to create singleton objects in Scala
    • Adding methods to these objects is like static methods in Java, and they are accessible by object name
    • So no static in Scala

Define a constant:

val ID[:TYPE] = VALUE

Define a variable:

var ID[:TYPE] = VALUE

1
2
3
4
5
val xmax, ymax = 100
var greeting, message: String = null

// assign two different constants using a tuple in a single line
val (sum, count) = (1.0, 1)

Note: In Scala, use val unless you really need to change the contents! (Scala loves immutability)

Support Java classes:

1
2
3
4
5
6
7
8
9
10
import java.util.{Date, Locale}
import java.text.DateFormat._

object FrenchDate {
  def main(args: Array[String]) {
    val now = new Date
    val df = getDateInstance(LONG, Locale.FRANCE)
    println(df format now)
  }
}

TODO: look lazy val

Control Structures

  • In Java these two have differences:
    • expression, such as 3 + 4, has a value
    • statement, such as if(...) ..., carries out an action
  • In Scala, almost all constructs have values
    • An if expression has a value
    • A block has a value - the value of its last expression
  • Scala does not have ?: operator! Use if else instead
1
2
3
4
5
6
7
8
9
val x: Double = Math.random()

val b: Boolean = if (x > .5) true else false
val a: Any = if (x > .5) true else "Oops!"
val u: Any = if (x > .5) true // or if (x > .5) true else ()
val distance = {val dx = x-x0; val dy = y-y0; sqrt(dx * dx + dy * dy)}

val q: Double = if (x >= 0) Math.sqrt(x) else 
  throw new IllegalArgumentException("No Negative")
  • Line 3, the if-else has a definite Boolean return type
  • Line 4, each branch has a different type, so the variable is of type Any (it is the common supertype), however the returning object is Boolean or String
  • Line 5, like line 4, but the returning object is Boolean or Unit. Unit type has one value which is ()
  • Line 6, the value of sqrt() is assigned to distance (a good practice to initialize a val)
  • Line 8, the type of if is Double and the type of else is Nothing which is ignored!

Loops

while is the same as Java

while (BOOLEAN_EXPRESSION) …

1
2
3
4
5
6
var (r, n) = (1.0, 10) // multiple variables by tuple, r:Double, n:Int
while (n > 0) {
  r = r * n
  n -= 1
}
println(s"r = $r")

Scala has no loop of for (initialize; test; update) as Java. Instead, the syntax of for is

1
2
3
4
5
for (i <- 1 to 10) {
  val x: Double = Math.random()
  val u: Any = if (x > .5) true
  println(f"i = $i%02d, u = $u")
}
1
2
3
4
val s = "Hello"
var sum = 0
for (i <- 0 until s.length) // or for (i <- 0 to s.length - 1) 
  sum += s(i)

or

1
2
var sum = 0
for (ch <- "Hello") sum += ch

Note: Scala’s Int has a special API for range

  • [a, b]: 1.to(5) or 1 to 5
  • [a, b): 1.until(5) or 1 until 5

Advanced for

1
2
3
4
5
6
7
8
9
10
11
12
13
// multiple generators:  variable <- expression
for (i <- 1 to 3; j <- 1 to 3) print(f"${10 * i + j}%3d") // 11 12 13 21 22 23 31 32 33

// second generator with a 'guard' starting with an 'if'
for (i <- 1 to 3; j <- 1 to 3 if i != j) print(f"${10 * i + j}%3d") // 12 13 21 23 31 32

// using one or more definitions, e.g. the 'from' var
for (i <- 1 to 3; from = 4 - i; j <- from to 3) print(f"${10 * i + j}%3d") // 13 22 23 31 32 33
//or
for {i <- 1 to 3
     from = 4 - i
     j <- from to 3}
  print(f"${10 * i + j}%3d")

for Comprehension

A for loop with yield returns a sequence

1
2
3
4
5
val v = for (i <- 1 to 10) yield i % 3 // v is Vector(1, 2, 0, 1, 2, 0, 1, 2, 0, 1)
v.foreach(println)

val str: String = for (c <- "Hello"; i <- 0 to 1) yield (c + i).toChar // HIeflmlmop
val vc = for (i <- 0 to 1; c <- "Hello") yield (c + i).toChar // Vector(H, e, l, l, o, I, f, m, m, p)

Note: Lines 4 & 5 look similar, but the results are different. So the generated collection is compatible with the first generator.

Function & Procedure

  • Scala functions are like static methods in Java (C++ also has functions)
  • No need to specify the return type, unless the function is recursive
1
2
3
4
5
6
7
8
9
10
def abs(x: Double) = if (x >= 0) x else -x

def fac(n : Int) = {
  var r = 1
  for (i <- 1 to n) r = r * i
  r // no need to use return keyword
}

// recursive factorial must declare Int as return
def fac(n: Int): Int = if (n <= 0) 1 else n * fac(n - 1)

Possible to define default value for parameters! To set a specific parameter, use named arguments!

1
2
3
4
5
def decorate(str: String, left: String = "[", right: String = "]") = left + str + right

println(decorate("Hello")) // [Hello]
println(decorate("Hello", "(")) // (Hello]
println(decorate("Hello", right = ")")) // [Hello)

A procedure is just a named block with 0-to-many parameter(s), but without any return value. So in its syntax no =.

1
2
3
4
def box(s : String) {
  val border = "-" * (s.length + 2)
  print(f"$border%n|$s|%n$border%n")
}

Scala has another way for varargs in Java

  • The type of args is Seq
  • Like Java, these type of parameters must be defined as last ones
  • Note: Not possible to set default value for other parameters when such parameter is declared
1
2
3
4
5
6
7
8
9
10
def sum(args: Int*) = {
  var result = 0
  for (arg <- args) result += arg
  result
}

// to call:
println(sum(1, 3, 66))   // ok
println(sum(1 to 5))     // error
println(sum(1 to 5: _*)) // consider 1 to 5 as an argument sequence

Arrays

Fixed-size Arrays

  • Implemented as a Java array
1
2
3
4
5
6
val nums = new Array[Int](10) // All initialized with zero

val a = new Array[String](10) // All initialized with null

val s = Array("Hello", "World") // No new operator!
s(0) = "Goodbye" // Access element by (INDEX) not [INDEX]

Variable-Length Arrays: ‍‍ArrayBuffer‍‍

1
2
3
4
5
6
7
8
9
10
11
import scala.collection.mutable.ArrayBuffer

val b = ArrayBuffer[Int]() // also new ArrayBuffer[Int]
b += 1                     // append an element with +=
b += (1, 2, 3, 5)          // append multiple elements
b ++= Array(8, 13, 21)     // append any collection with ++=
b.trimEnd(5)               // remove the last five elements
b.trimStart(1)             // remove the first element
b.insert(2, 6, 7)          // insert one or more elements from index 2 
b.remove(2)                // remove element at index
b.remove(2, 3)             // from index 2, remove 3 elements

Traversing

1
2
3
4
5
6
7
8
9
10
val b = ArrayBuffer[Int](1, 6, 10)

for(i <- 0 until b.length) // all collections
  println(s"$i = ${b(i)}")

for(i <- b.indices)        // all indexed-collections (array, array buffer, list, ...)
  println(s"$i = ${b(i)}")

for(e <- b)
  println(s"element = $e")

It is possible to use for comprehension for array & array buffer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
val b = ArrayBuffer[Int](1, 6, 10) 

val p1: ArrayBuffer[Int] = for(e <- b)         yield e * e
val p2: Array[Int]       = for(e <- b.toArray) yield e * e
val p3: ArrayBuffer[Int] = b.map(Math.pow(_, 2).toInt)

val result = for (e <- b if e >= 0) yield e

val positionsToRemove = for (i <- b.indices if b(i) < 0) yield i
for(i <- positionsToRemove.reverse) b.remove(i)

val positionsToKeep = for (i <- b.indices if b(i) >= 0) yield i
for (j <- positionsToKeep.indices) b(j) = b(positionsToKeep(j))
b.trimEnd(b.length - positionsToKeep.length)
  • p3 has the same result as p2
  • Line 7, returns only positive elements but in a new ArrayBuffer
  • Lines 9-10 try to remove negative numbers from the original array buffer
  • Lines 12-13 try to remove negative numbers from the original array buffer, another way

Collections

Use List(), Set(), and Map() to create an immutable instance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
val list = List(1, 2, 3)
list.foreach(value => println(value))
list.foreach(println)

// defining type of 'scores' is redundant!
val scores: Map[String, Int] = Map(("A", 1), ("b", 20), "C" -> 300)
scores.foreach((entry: (String, Int)) => 
  println(f"K=${entry._1}, V=${entry._2}%03d")) // formatted string
//or
for ((k, v) <- scores)
  println(f"K=$k, V=$v%03d")

val r = if(scores.contains("A")) scores("A") else 0
// or
val r = scores.getOrElse("A", 0)

val swap: Map[Int, String] = for ((k, v) <- scores) yield (v, k) // Switch key & value
  • Line 7,
    • The entry is a tuple of (String, Int)
    • Note: Unlike array or string positions, the component positions of a tuple start with 1, not 0.
    • The f makes the string formatted and acts like printf in Java. The %03d pads the ${entry._2} with max two leading zeros

For mutable ones

1
2
3
4
5
// M A P
val updatingScores = scala.collection.mutable.SortedMap[String, Int]()
updatingScores("A") = 8
updatingScores += (("D", 10), "B" -> 10, "C" -> 7)
updatingScores -= "C"  // Remove by key

Java and Scala Interoperability

  • Scala arrays are implemented as Java arrays, so you can pass them back and forth between Java and Scala
  • Java’s collection needs conversion if Scala’s type are required!
    • import scala.collection.JavaConverters._ for asScala
    • import scala.collection.mutable the returning object is mutable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import scala.collection.JavaConverters._
import scala.collection.Searching.search
import scala.collection.mutable

import java.util.Arrays.binarySearch

//...

val myJava = new MyJava

val fromJavaArr = myJava.getArrayOfString
println(fromJavaArr.mkString("{", ", ", ")"))      // output: {Hello, World)
println(myJava.sumArrayOfInteger(Array(1, 2, 4)))  // output: 7

// Error, no specific method for String type, although there is one for Object!
// java.util.Arrays.binarySearch(Array("b", "a").sorted, "a")
val arr = Array("b", "a", "d").sorted
println(s"idx=${binarySearch(arr.asInstanceOf[Array[Object]], "b")}") // output: idx=1
println(s"idx=${arr.search("c")}")                                    // output: idx=InsertionPoint(2)


val fromJavaList = myJava.getListOfInteger
println(fromJavaList.stream().mapToInt(i => i).sum())   // like Java i -> i
println(fromJavaList.stream().mapToInt(_.intValue).sum) // like Java i -> i.intValue


val toScalaList: mutable.Seq[Int] = myJava.getListOfInteger
  .asScala                // 1. convert list of Java to Scala
  .map(_.toInt)           // 2. convert Java's Integer to Scala's Int
println(toScalaList.sum)  // the sum needs 'List' & 'Int' of Scala


val toScalaMap: mutable.Map[String, Long] = myJava.getAMap
  .asScala
  .map((t: (String, lang.Long)) => (t._1, t._2.toLong))
println(toScalaMap)  // Map(A -> 0, B -> 5, C -> 10)
  • Line 18, call Java’s binarySearch and passing an array of String, however a type conversion is necessary
  • Line 19
    • search method requires import scala.collection.Searching.search for binary search
    • two types of result:
      • Found(n): the element is found at index n
      • InsertionPoint(n): not found, however proposed insertion at index n (above, insert ‘c’ at index 2)
  • Line 27-30: two steps of conversion from Java to Scala
    • Line 28: call asScala (applicable on all Java collections) to convert to Scala equivalent
    • Line 29: call map to convert Java’s Integer to Scala’s Int