/ technical

Nick's Scala Notes

Nick's Scala Notes

Basics

This section should be most of the things you need to get going. The goal
is that you can read most scala code and write your own simple applications.
(Also check out Intermediate and Advanced)

The REPL (Scala playground)

Scala has a REPL. Simply enter scala from the command line:

$ scala
...
> 1 + 3
res0: Int = 4

res0 is a variable that the REPL defines. It has the type Int and
the value of 4. You can actually interact with this afterwards:

> res0 + 4
res1: Int = 8

The Scala REPL can do nearly anything. If you need to do more than
one-liners, you can enter paste mode:

> :paste
def sum(a: Int, b: Int) = {
  a + b
}
// CTRL-D
> sum(3, 4)
res0: Int = 7

To leave the REPL, use :q

Scala App

There are two ways to build a simple scala application. The first is
similar to Java and should be familiar if you know Java:

// HelloWorld.scala
object HelloWorld {
  def main(args: Array[String]) {
    println("Hello, world!")
  }
}

Throw this into IntelliJ and you should be able to run it. You can also
use the command line:

$ scalac HelloWorld.scala
$ scala HelloWorld
Hello, world!

The other way (and simpler way) is simply to extend App:

// HelloWorld.scala
object HelloWorld extends App {
  println("Hello, world!")
}

The {} block for the object simply acts as a constructor, so when you
extend App it treats the block as the application starting point.

Documentation

It is pretty useful to be able to look up API docs for Scala.

Located here: http://www.scala-lang.org/api/2.11.8/

For example, try searching for "List" and look through some of the
methods.

Variables

To create an immutable (constant) value in Scala:

val x = 5

x cannot be reassigned after this.

To create a variable:

var y = 5
y = 6

It is idiomatic in Scala to use val whenever possible and avoid var.

Output

Scala has print and println. println simply adds a newline at the
end.

print("Name: ")
println("Martin Odersky")

Control Structures & Flow

Conditionals

if (counter == 5) {
  println("It's five!")
} else if (counter == 10) {
  println("It's ten!")
} else {
  println("I don't know what it is!")
}

if statements actually return a value in Scala. In this way, they can
replace ternaries:

val greeting = if (isGuest) { "Welcome, stranger!" } else { "Welcome back!" }

You can even remove the braces in this case:

val greeting = if (isGuest) "Welcome, stranger!" else "Welcome back!"

Loops

Scala has both for and while.

for (i <- 1 to 10) {
  println(i)
}

1 to 10 actually translates to 1.to(10) (explained later) and
generates a sequence of the numbers from 1 to 10. The for loop iterates
through this sequence one number at a time, assigning each to i.

While loop:

var counter = 0
while (counter < 10) {
  println(counter)
  counter += 1
}

Loops are certainly useful, but typically there are better options in
Scala (explained later).

Functions

Functions are first-class citizens in Scala. They can be defined,
assigned to variables, passed as a function parameter, and returned
from a function.

Without using any syntactic sugar:

def sum(x: Int, y: Int): Int = {
    x + y
}

The last statement in a function block is the returned value for the
function. So return is not necessary. However, return still exists
in certain situations - such as breaking from a loop inside a function.

Also, notice that the definition is assigned to the block using an equals
sign. The = can be omitted for functions that don't return a value.
With these functions the return type would be Unit.

However, you can remove the function type if Scala can infer it. You
can also remove the braces if the function is a single expression:

def sum(x: Int, y: Int) = x + y

Call the function like this:

val summed = sum(3, 4) // 7

You can also assign an anonymous function to a variable:

val isLong = { (input: String) => input.length > 5 }
isLong("Hi") // false
isLong("Hello, world!") // true

The => syntax is the separator between parameters and function body.
In the previous example, you can also remove the braces for the function
definition.

Function parameters must always be listed with their type. Scala can't
infer this.

Functions can have default parameters:

def say(statement: String, name: String = "dude"): Unit = {
  println(statement + ", " + name)
}

Since the function is Unit type, it can be written without the type
and without the equals sign:

def say(statement: String, name: String = "dude") {
  println(statement + ", " + name)
}

Called like this:

say("Welcome home", "Nick") // Welcome home, Nick

or like this:

say("What's up") // What's up, dude

or even like this:

say("C'mon", name = "Scoob") // C'mon, Scoob

With named parameters, you have the ability to choose which default
arguments to provide if there is more than one argument that has a
default.

Collections

List

List is the fundamental immutable collection in Scala.

Create:

val empty = List[Int]()     // empty list (type sometimes needed)
val integers = List(1,2,3)  // with values (type optional)

Access:

val two = integers(1)

Appending/Prepending:

val list1 = List(1) :+ 2       // List(1, 2)
val list2 = 1 :: List(2)       // List(1, 2)
val list3 = List(1) ++ List(2) // List(1, 2)

Note that all these operations create a new list, since the original list
is immutable. Mutable collections also exist, but will be covered later.

There are also many ways to manipulate a collection. The most used are
mapping, filtering, and folding.

Mapping is used to modify elements in a list by applying a function to
each element.

val nums = List(1, 2, 3, 4, 5)
val countByTwo = nums.map({ (num: Int) => num * 2 }) // 2, 4, 6, 8, 10

map takes a function and the => notation defines one in place. However,
there are other options. You can use an underscore instead of defining
a parameter. You can also remove the parentheses or the braces (but only one!)

val words = List("Tom", "Jerry", "Richard")
val lengths = words.map(_.length) // 3, 5, 7

Using an underscore only works if you are using the parameter one time.

Filtering is used to create a new list with a subset of the source list.

val words = List("Car", "Vehicle", "TV", "Television")
val longWords = words.filter { _.length > 5 } // Vehicle, Television

Folding is used to reduce a list to a single value (or collection).
It is extremely powerful and is incredibly common in the functional
paradigm. Folding can do anything a while loop can do.

val nums = List(1, 2, 3, 4, 5)
val sum = nums.foldLeft(0) { (res, n) => res + n } // 15

foldLeft is a curried function, meaning it takes more than one set
of parameters. The first set is just the value where you want the fold
to start. The second set is a binary function (takes two arguments)
that produces a single value (the final result, iteratively).

So, to peek into the steps of the fold for this example:

Step 1: res = 0, n = 1, res + n = 1, remaining = 2, 3, 4, 5
Step 2: res = 1, n = 2, res + n = 3, remaining = 3, 4, 5
Step 3: res = 3, n = 3, res + n = 6, remaining = 4, 5
Step 4: res = 6, n = 4, res + n = 10, remaining = 5
Step 5: res = 10, n = 5, res + n = 15, remaining = Nil

The final value (15) is what is returned from the fold. This fold can also
be put much more simply:

(((((0 + 1) + 2) + 3) + 4) + 5)

The 0 value comes from the first parameter set to the function. It can
also be omitted if you want to use the first index of the list as the
initial value. Use reduceLeft for this. For example:

val nums = List(1, 2, 3, 4, 5)
val sum = nums.reduceLeft { (res, n) => res + n }

This time, the first call to our function has the value 1 for res.
Which looks like this:

((((1 + 2) + 3) + 4) + 5)

You can also change out the => notation and use underscores. Also,
a fold can return any type, including another list.

val words = List("Mary", "had", "a", "little", "magnanimous", "lamb")
val allShort: Boolean = words.foldLeft(true) { _ && _.length < 10 } // false :(

Here, the first underscore represents the value from the previous
iteration (starting with true). The second parameter is the next value
(one of the strings in words). If you remove "magnanimous" from the
list, the result should be true.

An example of returning another list:

val words = List("Bob", "Frank", "Tim")
val lengths = words.foldLeft(List[Int]()) { (col, el) => col :+ el.length } // List(3, 5, 3)

Of course, this is a contrived example. It would be much simpler to just
use map. You can also substitute the => notation here, but this is
hopefully less confusing.

In this example, the first time the function is called, it is called
with res equal to the new integer list (List[Int]()) and el equal to
the first element in words ("Bob"). It then creates a new list with
the length of "Bob" appended to the original list and returns it (List(3))
and the process continues - adding the lengths of "Frank" and "Tim"
to the list in the next iterations.

Also note that if you want to return a value that is a different type
than the elements in the list, you will need to use foldLeft instead
of reduceLeft.

Note: There is also a reduceRight and a foldRight that works by
starting from the end of the sequence.

Map

Scala has both mutable and immutable Map options. The default Map is
immutable, but you can access the other one with collection.mutable.Map.

Create:

val empty = Map[Int, String]()
val map = Map(1 -> "Hare", 2 -> "Turtle")

Access:

map(1) // Hare

Append:

val threePlaces = map + (3 -> "Cricket")                                // (1 -> Hare, 2 -> Turtle, 3 -> Cricket)
val fourPlaces  = map + (3 -> "Cricket", 4 -> "Fox")                    // (1 -> Hare, 2 -> Turtle, 3 -> Cricket, 4 -> Fox)
val fivePlaces  = map ++ List(3 -> "Cricket", 4 -> "Fox", 5 -> "Snake") // (1 -> Hare, 2 -> Turtle, 3 -> Cricket, 4 -> Fox)

Notice the double plus (++) when using a List.

Update:

val updated = map + (2 -> "Fox") // (1 -> Hare, 2 -> Fox)

When adding a key to a map, it will overwrite the previous key if it
exists.

Remove:

val noWinner = updated - 1            // (2 -> Turtle, 3 -> Fox)
val oneWinner = updated - (2, 3)      // (1 -> Hare)
val justThird = updated -- List(1, 2) // (3 -> Fox)

Notice the double minus (--) when using a List.

With a collection.mutable.Map you can update an item like this:

map(2) = "Bear" // (1 -> Hare, 2 -> Bear)

Tuples

A tuple is any number of values surrounded by parenthesis and separated
by commas.

val item = ("Chips", 1.25)
val price = item._2  // access an index (1-indexed, not 0-indexed)
val (name, _) = item // name assigned to "Chips" by pattern matching

Classes

Intermediate

Zipping

Set

Exceptions

Lazy Values

List Comprehensions

for (i < -1 to 10) yield i * 2

for (i <- 1 to 10; j <- 11 to 20) { }
// guards, yield, etc...

Variable Arguments

def sum(nums: Int*) = nums.sum

Implicit

Semicolons

Advanced

:_* operator