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.
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 / 2and explain why it stores 3.0. - Change a
valto avarand 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.
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.
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?
