Tour of Scala
In this page, we will cover basics of Scala.
If you want to execute the code examples in this tour interactively, the easiest way to get started is to run the tour as Jupyter notebooks on Binder. That way, you can play with the examples and try new things in your browser without having to install anything locally.
To execute a cell of code, just click on it and press control + enter or use the menu.
For more information how this works or how to run it locally see the README of this project or the Almond documentation.
All content is derived from https://docs.scala-lang.org/tour/tour-of-scala.html
Expressions are computable statements.
1 + 1
res0: Int = 2
You can output results of expressions using println
.
println(1) // 1
println(1 + 1) // 2
println("Hello!") // Hello!
println("Hello," + " world!") // Hello, world!
1 2 Hello! Hello, world!
You can name results of expressions with the val
keyword.
val x = 1 + 1
println(x) // 2
2
x: Int = 2
Named results, such as x
here, are called values. Referencing
a value does not re-compute it.
Values cannot be re-assigned.
x = 3 // This does not compile.
cmd3.sc:1: reassignment to val val res3 = x = 3 // This does not compile. ^Compilation Failed
Compilation Failed
Types of values can be inferred, but you can also explicitly state the type, like this:
val x: Int = 1 + 1
x: Int = 2
Notice how the type declaration Int
comes after the identifier x
. You also need a :
.
Variables are like values, except you can re-assign them. You can define a variable with the var
keyword.
var x = 1 + 1
x = 3 // This compiles because "x" is declared with the "var" keyword.
println(x * x) // 9
9
x: Int = 3
As with values, you can explicitly state the type if you want:
var x: Int = 1 + 1
x: Int = 2
You can combine expressions by surrounding them with {}
. We call this a block.
The result of the last expression in the block is the result of the overall block, too.
println({
val x = 1 + 1
x + 1
}) // 3
3
Functions are expressions that take parameters.
You can define an anonymous function (i.e. no name) that returns a given integer plus one:
(x: Int) => x + 1
res7: Int => Int = ammonite.$sess.cmd7$Helper$$Lambda$2461/875549557@23cd7091
On the left of =>
is a list of parameters. On the right is an expression involving the parameters.
You can also name functions.
val addOne = (x: Int) => x + 1
println(addOne(1)) // 2
2
addOne: Int => Int = ammonite.$sess.cmd8$Helper$$Lambda$2475/452780641@232887cf
Functions may take multiple parameters.
val add = (x: Int, y: Int) => x + y
println(add(1, 2)) // 3
3
add: (Int, Int) => Int = ammonite.$sess.cmd9$Helper$$Lambda$2482/741665633@3d3644c1
Or it can take no parameters.
val getTheAnswer = () => 42
println(getTheAnswer()) // 42
42
getTheAnswer: () => Int = ammonite.$sess.cmd10$Helper$$Lambda$2490/1374863475@f01186c
Methods look and behave very similar to functions, but there are a few key differences between them.
Methods are defined with the def
keyword. def
is followed by a name, parameter lists, a return type, and a body.
def add(x: Int, y: Int): Int = x + y
println(add(1, 2)) // 3
3
defined function add
Notice how the return type is declared after the parameter list and a colon : Int
.
Methods can take multiple parameter lists.
def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier
println(addThenMultiply(1, 2)(3)) // 9
9
defined function addThenMultiply
Or no parameter lists at all.
def name: String = System.getProperty("user.name")
println("Hello, " + name + "!")
Hello, brunksn!
defined function name
There are some other differences, but for now, you can think of them as something similar to functions.
Methods can have multi-line expressions as well.
def getSquareString(input: Double): String = {
val square = input * input
square.toString
}
defined function getSquareString
The last expression in the body is the method's return value. (Scala does have a return
keyword, but it's rarely used.)
You can define classes with the class
keyword followed by its name and constructor parameters.
class Greeter(prefix: String, suffix: String) {
def greet(name: String): Unit =
println(prefix + name + suffix)
}
defined class Greeter
The return type of the method greet
is Unit
, which says there's nothing meaningful to return. It's used similarly to void
in Java and C. (A difference is that because every Scala expression must have some value, there is actually a singleton value of type Unit, written (). It carries no information.)
You can make an instance of a class with the new
keyword.
val greeter = new Greeter("Hello, ", "!")
greeter.greet("Scala developer") // Hello, Scala developer!
Hello, Scala developer!
greeter: Greeter = ammonite.$sess.cmd15$Helper$Greeter@61c125d6
case class Point(x: Int, y: Int)
defined class Point
You can instantiate case classes without new
keyword.
val point = Point(1, 2)
val anotherPoint = Point(1, 2)
val yetAnotherPoint = Point(2, 2)
point: Point = Point(1, 2) anotherPoint: Point = Point(1, 2) yetAnotherPoint: Point = Point(2, 2)
And they are compared by value.
if (point == anotherPoint) {
println(point + " and " + anotherPoint + " are the same.")
} else {
println(point + " and " + anotherPoint + " are different.")
} // Point(1,2) and Point(1,2) are the same.
if (point == yetAnotherPoint) {
println(point + " and " + yetAnotherPoint + " are the same.")
} else {
println(point + " and " + yetAnotherPoint + " are different.")
} // Point(1,2) and Point(2,2) are different.
Point(1,2) and Point(1,2) are the same. Point(1,2) and Point(2,2) are different.
There is a lot more to case classes that we'd like to introduce, and we are convinced you will fall in love with them! We will cover them in depth later.
Objects are single instances of their own definitions. You can think of them as singletons of their own classes.
You can define objects with the object
keyword.
object IdFactory {
private var counter = 0
def create(): Int = {
counter += 1
counter
}
}
defined object IdFactory
You can access an object by referring to its name.
val newId: Int = IdFactory.create()
println(newId) // 1
val newerId: Int = IdFactory.create()
println(newerId) // 2
1 2
newId: Int = 1 newerId: Int = 2
trait Greeter {
def greet(name: String): Unit
}
defined trait Greeter
Traits can also have default implementations.
trait Greeter {
def greet(name: String): Unit =
println("Hello, " + name + "!")
}
defined trait Greeter
You can extend traits with the extends
keyword and override an implementation with the override
keyword.
class DefaultGreeter extends Greeter
class CustomizableGreeter(prefix: String, postfix: String) extends Greeter {
override def greet(name: String): Unit = {
println(prefix + name + postfix)
}
}
val greeter = new DefaultGreeter()
greeter.greet("Scala developer") // Hello, Scala developer!
val customGreeter = new CustomizableGreeter("How are you, ", "?")
customGreeter.greet("Scala developer") // How are you, Scala developer?
Hello, Scala developer! How are you, Scala developer?
defined class DefaultGreeter defined class CustomizableGreeter greeter: DefaultGreeter = ammonite.$sess.cmd24$Helper$DefaultGreeter@56c4afe7 customGreeter: CustomizableGreeter = ammonite.$sess.cmd24$Helper$CustomizableGreeter@5b523d34
Here, DefaultGreeter
extends only a single trait, but it could extend multiple traits.
We will cover traits in depth later.
The main method is an entry point of a program. The Java Virtual
Machine requires a main method to be named main
and take one
argument, an array of strings.
Using an object, you can define a main method as follows:
object Main {
def main(args: Array[String]): Unit =
println("Hello, Scala developer!")
}
defined object Main