Scala from the ground up

Values, variables and types

val for a name you set once and var for one you change, the four types you will use daily, and what division really does to whole numbers.

Part 2 of 12

Beginner14 min readBack to Courses

Two ways to give something a name

val score = 10 gives the value 10 a name, and that name now stands for it everywhere below. val means the name will not be pointed at anything else: reassigning it is a compile error rather than a runtime surprise. In Scala the default choice is val, and most of a well-written program is built from them.

var count = 0 is the other one. The name may be pointed at a new value later, which is what you reach for when something genuinely accumulates: a running total, a loop counter, a value being built up. Using var where val would do is the first habit worth breaking, because a value that cannot change is a value that cannot be changed by accident.

The types you will use every day

Scala checks types, and most of the time it works them out for you: val n = 3 is an Int because 3 is one. When you want to be explicit you write the type after the name with a colon — val n: Int = 3 — which is also how you document what a value is meant to hold.

The four are Int for whole numbers, Double for numbers with a decimal point, String for text, and Boolean for the two values true and false. A String is written in double quotes, and a single character is just a short string rather than a type of its own. println will print any of them, and + works on numbers and on text.

Division, and the surprise of whole numbers

Two whole numbers divide to a whole number: 7 / 2 is 3, with the half thrown away rather than rounded. This is not a bug and it catches everyone once. When you want the fraction, make one side a Double — 7.0 / 2 — and the answer is 3.5.

The arithmetic order is the one from school: multiplication and division before addition and subtraction, and round brackets when you want to say it differently. 6 * 7 is 42, and (1 + 2) * 3 is 9. Boolean values combine with && for and, || for or, and ! to invert, which is how a program asks two questions at once.

Where the type comes from

Every literal already has a type: 3 is an Int, 3.0 is a Double, "3" is a String, and true is a Boolean. When you write val n = 3, the compiler reads the right side and gives n that same type. From then on the name is fixed: n = "three" fails because the name was not declared to hold text.

A written annotation is checked rather than merely decorative. val n: Int = 3 is fine, and val n: Int = 3.5 is a compile error, because the compiler will not quietly cut the decimal off for you. Annotations are worth writing where the inferred type would be a surprise, and where a reader needs the contract more than the initial value.

Arithmetic combines the types in a predictable way: Int with Int stays Int, and one Double on either side makes the answer a Double. Joining with + is different, because a String on the left turns the whole expression into text. A var changes the value it points at, never the type it was given.

A name is not a box

A val is a name for a value, not a container that holds one. val a = 3 and val b = a give two names for the same number, and neither can be changed afterwards. A var is the one that can be pointed somewhere else, which is why the compiler can reason about everything built from vals.

Three slips account for most early type errors: writing = where a comparison needs ==, expecting 7 / 2 to produce 3.5, and expecting 1 / 2 to produce anything other than 0. The first is caught by the compiler; the other two compile happily and give the wrong number.

Try these next

  • Declare one name of each of the four types, then print all four on separate lines.
  • Write val half: Double = 7 / 2 and explain why it stores 3.0.
  • Change a val to a var and give it a new number, then try to give it a string.

Names, types, and the arithmetic

val count: Int = 3
var score = 10

score = score + 5

val half = 7 / 2
val ratio = 7.0 / 2
val big = 6 * 7
val total = (1 + 2) * 3

println("count: " + count)
println("score: " + score)
println("half: " + half)
println("ratio: " + ratio)
println("big: " + big)
println("total: " + total)

val ready: Boolean = true
println("ready: " + ready)
println("not ready: " + !ready)
println("both: " + (ready && false))

A till roll

Run it and follow how the total grows. Then add a fourth item, and print the average price by making one side of the division a Double.

Output
Click "Run" to execute your code

A temperature table

Run it and check how the two averages differ. Then change the two readings and add a line that converts the average back to Celsius with `(f - 32) * 5 / 9` as a Double.

Output
Click "Run" to execute your code

A val cannot be reassigned: writing val n = 1 and then n = 2 later is a compile error, not a silent change. The compiler is doing you a favour — a name that never changes is one less thing to track while reading.

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

When should you reach for var instead of val?