Skip to content

Instantly share code, notes, and snippets.

@florabtw
Last active November 11, 2016 07:21
Show Gist options
  • Save florabtw/a44bb6c21ac4be153d1b90d325917883 to your computer and use it in GitHub Desktop.
Save florabtw/a44bb6c21ac4be153d1b90d325917883 to your computer and use it in GitHub Desktop.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment