Scala/Getting Started
First steps
[edit | edit source]In this very short tutorial on Scala, some very basic programs will be described and explained. Some parts will be explained in more detail in later articles.
The simple "Hello World!" program is shown below.
println("Hello World!") //Prints "Hello World!".
"println" is a function, which takes some input, prints it, and appends a newline. The input is given as an argument, enclosed by parenthesis, and is in this case the string "Hello World!". After the closing parenthesis comes a one-line comment, which begins with two slashes. Everything after the slashes are considered comments, and are ignored by the compiler.
In the previous example, the input is consumed immediately. What if we want to store it? One way is to declare a value and assign the input to that value:
val helloWorldString = "Hello World!"
println(helloWorldString) //Prints "Hello World!".
In the first line, we use "val" followed by some name, here "helloWorldString", to declare a value. We use "=" followed by some expression, here the string "HelloWorld", to assign it. Values are like constant variables; they must always be assigned, and they cannot be reassigned an expression. In the following line, "println" uses the value of "helloWorldString", and prints "Hello World!".
In order to reassign a variable, the keyword "var" has to be used instead of "val":
var number = 24
println(number) //Prints "24".
number = 42
println(number) //Prints "42".
In the first line, "var" followed by some name, here "number", declares a variable. "=" followed by some expression, here the number 24, assigns some value to the variable. In line 2, we print the number, which is 24. In line 3, the variable "number" has already been declared, so we don't have to declare it again by using "var" if we want to assign it an expression. This time, we assign the number "42", which replaces the previous value, and when we print it in line 4, we print the current value of "number", which is 42. It is considered good style to prefer values over variables, since knowing that the identifier we are using always refers to the same value and never changes makes it easier to reason about our code.
Simply assigning and printing simple expressions isn't very exciting. Let us try some arithmetic:
val number2 = 24*3 + 100 - 130
println(number2) //Prints "42".
In the first line, we declare a value named "number2", and assign it a more complex expression. The expression is calculated, and the result of the expression is stored in "number2". In line 2, we print the resulting value, which is 42. Note that the operators "*", "+" and "-" follows normal mathematical precedence for numbers; ie. "*" has higher precedence than "+" and "-".
It is possible to combine numbers and strings. This is done using the "+" method:
val number3 = 124
println("The value of 'number3' is: " + number3 + ".") //Prints "The value of 'number3' is: 124.".
If-then-else expressions
[edit | edit source]So far, our programs have only had one control flow, ie. one way to go through the program when executing. This makes it difficult to change behaviour based on the state of the program. One way to change this is to use a "if-then-else" expression:
if (4 > 3) println("4 > 3 is true") else println("4 > 3 is false") //Prints "4 > 3 is true".
In the first line, "if" is used to start the "if-then-else" expression. It is followed by a pair of parenthesis, which contained a test expression resulting in a truth value, here 4 > 3. This is the first branch of the "if-then-else". After the closing parenthesis comes some expression, here "println("4 > 3 is true")". This may be followed by "else" and yet another expression, here "println("4 > 3 is false")". This is the second branch of the "if-then-else". If the test expression in parenthesis is true, the first branch is taken, which in our case it is, and "4 > 3 is true" is printed (which luckily is indeed true). If the expression 4 > 3 had not been true, the second branch is taken. Note that the second branch is optional; if there is no second branch and the test expression yields false, nothing happens:
if (1 < 0) println("1 < 0 is true")
//Nothing happens.
Since we only have one branch, and the test expression is false, nothing happens.
In Scala, if-then-else expressions have a resulting value. This can often make our programs shorter:
println(if (4 > 3) "4 > 3 is true" else "4 > 3 is false") //Prints "4 > 3 is true".
The if-then-else expression is evaluated, yielding a string ("4 > 3 is true"), which is printed by the "println" function, resulting in the same printing as before. Note that we only use "println" once instead of twice.
Blocks
[edit | edit source]A block is an expression that contains a number of consecutive expressions. An example of using blocks is shown below:
val area = { val r = 3.5 math.Pi * r * r } println(math.round(area)) // Prints "38".
In the first line, we declare a val named "area" and assigns a block to it. The block is started by the "{", after which a number of expressions follows, here "val r = 3.5" and "math.Pi*r*r", and is ended by the "}". The first expression assigns 3.5 to the value named "r", and the second expression contains an expression that calculates the area of a circle given "r" indicates the circle's radius. The last expression in the block gives the result of the block, which in this case results in a number close to 38. This number is assigned to the value named "area". In the final line, we round and print the value of "area", and 38 is printed.
Note that values and variables declared inside a scope are not visible outside the scope. This means that we cannot access "r" outside the scope. In some cases, hiding temporary values and variables inside scopes can help us to avoid cluttering our program with too many names.
While loops
[edit | edit source]If-then-else expressions allow us to let the program execute different code based on its state. Another control flow modification that is useful is repeating the same code a number of times. The while-loop can be used to accomplish that:
var i = 0
while (i < 10) {print(i + " "); i += 1} //Prints "0 1 2 3 4 5 6 7 8 9 ".
The first line declare a variable named "i" and assigns 0 to it. The second line declares a while-loop by starting with a "while", followed by some test expression in parenthesis, here "i < 10", followed by a main expression, in this case a scope. The scope contains two statements, namely "print(i + " ")" and "i += 1". ";" is used to separate the two statements, such that we can write the two statements on one line.
The execution of the while-loop goes as follow: First the test expression is evaluated. If it is not true, execution of the while-loop stops, and the control flow continues after the while-loop. If it is true, the main expression of the while-loop is executed, here a scope. Once the scope is done executing, the control flow goes back to the test expression, and the process is repeated.
In our case, we evaluate whether "i" is below 10. To start with, "i" is 0, so this is true. Therefore, we evaluate the main expression, which prints the value of "i" and adds a space, and increments the value of "i". We then go back to our test expression again, and evaluate whether "i" is below 10. Since "i" is now 1, the expression is still true, so we go into the main expression again, print the value of "i" and add a space, and increment "i". This continues until the value of "i" becomes 10, after which we stop the execution of the while-loop and continue executing the program after it.
For-expressions
[edit | edit source]Scala offers syntactical support for working with collections through the use of "for-expressions", also known as for-comprehension and sequence comprehensions. For the formal background, see the corresponding explanation for list comprehensions: http://en.wikipedia.org/wiki/List_comprehension#Overview.
In order to work with for-expressions, we have to use some collection. One option is a range:
println(1 to 10) //Prints "Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)".
In the first line, the method "to" is called on 1 with argument 10, and produces the range of numbers in order from 1 inclusive to 10 inclusive. Now, let us print each number squared using a for-expression:
for (i <- 1 to 10) print(i*i + " ") //Prints "1 4 9 16 25 36 49 64 81 100 ".
We first declare the for-expression using "for". This is followed by a pair of parenthesis, containing "i <- 1 to 10". "<-" is built-in syntax for indicating that some name on the left refers to an element in the collection on the right. So, in this case, the name "i" refers to an element in the collection "1 to 10". After the pair of parenthesis follows some output expression. This expression is executed once for each element in the collection. For each execution, the name "i" refers to a new element. So, "i*i + " "" is repeatedly printed, with a new value for "i" each time.
What if we don't want to print all the elements? In this case, we use a filter:
for (i <- 1 to 10 if i % 2 == 0) print(i*i + " ") //Prints "4 16 36 64 100 ".
This time, we have added "if i % 2 == 0" after "i <- 1 to 10". "if" indicates the start of a filter, and "i % 2 == 0" is a test expression, that here tests whether "i" is even. Only those elements that makes the test expression true are executed in the output expression. So, this time, only the even numbers are printed.
In the above examples, the numbers are always printed. However, the for-expression can yield a new collection from the output expression:
val evenNumbersSquared = for (i <- 1 to 10 if i % 2 == 0) yield i*i
println(evenNumbersSquared.toList) //Prints "List(4, 16, 36, 64, 100)".
In the first line, two things have changed. First, we assign the result of the for-expression to the value "evenNumbersSquared". Second, we now use the "yield" keyword before the output expression "i*i". This means that, instead of doing nothing with the output expression, the output expression is used to construct a new collection by indicating what the next element should be. In the second line, we convert the resulting collection "evenNumbersSquared" to a list, and print it.
It is possible to use several collections in a for-expression:
for (i <- 1 to 4; a <- 1 to 4 if i < a) print(i + ":" + a + " ") //Prints "1:2 1:3 1:4 2:3 2:4 3:4 ".
We start the for-expression using "for" again. In the parenthesis, we now have two names that are bound to collection elements: "i <- 1 to 4" and "a <- 1 to 4". ";" is used to separate the two parts. As before, this is followed by a test expression, "i < a", which this time tests for whether "i" is smaller than "a". Finally, we print those pairs of "i" and "a" that passed the filter, and end up printing all the pairs of numbers from one to 4 where the first number is smaller than the second.
It should be noted that for-expressions are syntactic sugar for calling specific higher-order functions. Why then use for-expressions? For-expressions are sometimes clearer and simpler than using their respective higher-order functions.
It should also be noted that for-expression isn't limited to collections. Any class that supports the right higher-order functions can be used with for-expressions. Examples in the standard library besides the collections include Option, Either and parser-combinator Parser.