Start learning free

LESSON 14 OF 414 · VARIABLES

val, var and Immutability in Scala

Lesson Read the full explanation

Every time you create a name in Scala, the language makes you answer one question first: will this value ever change? There are two keywords, one for each answer:

  • val declares a read-only name — it is assigned exactly once and can never be reassigned.
  • var declares a variable — the value is allowed to change later.

This is not just style. When you write val, the compiler guarantees the value never changes — if any line of your program tries to reassign it, the program simply refuses to compile. Whole categories of bugs disappear before the code even runs. That is why Scala programmers reach for val by default and only use var when a value genuinely needs to change — a counter, a running total, a position that moves:

val pi = 3.14159            // fixed forever — perfect for val
var lives = 3               // will go down when the player is hit
lives -= 1                  // OK — lives was declared with var
println(s"lives = $lives")  // prints: lives = 2

Two things to notice. First, there are no type annotations: Scala infers that pi is a Double and lives is an Int from the values you assign. Second, the s in front of the quotes turns the string into an interpolator — $lives drops the current value straight into the text (without the s, Scala would print the literal characters $lives).


The trap:

Sooner or later you will try to reassign a val:

val maxLives = 3
maxLives = 5   // trying to change the rules mid-game

The compiler stops you with this exact error — read it carefully, because it underlines the precise line that tried to do the changing:

-- [E052] Type Error --------------------------------------------------------
  |  maxLives = 5
  |  ^^^^^^^^^^^^
  |  Reassignment to val maxLives
  |
  | longer explanation available when compiling with `-explain`
1 error found

The carets underline the exact assignment that broke the rule, and the message names the guilty binding: Reassignment to val maxLives. The two honest ways out are: keep val and stop reassigning, or switch the declaration to var because the value really does need to change. Compound operators hit the same wall — maxLives += 1 is rejected too (the compiler reports value += is not a member of Int), because += is just a reassignment in disguise.


Assignment
Follow the Coding Tutorial to build a tiny game setup with one constant and one variable:

  • Declare a read-only maxLives equal to 3 — the rules of the game never change — and print it.
  • Declare a variable score starting at 0, add 10 and then 5 to it with +=, and print the total.

The exact expected output is:

maxLives = 3
score = 15

Hint
Use println(s"maxLives = $maxLives")-style interpolated strings: the s prefix lets $name drop a value into the middle of the text. And if the compiler says Reassignment to val, the name you are changing was declared with val — the one value that must change here is score, so declare it with var.


STEP 1 OF 6
    Ctrl + Enter to check

    SCALA · VARIABLES

    Val Var Immutability in Scala, explained

    The question Scala asks

    Every time you create a name in Scala you choose between two keywords, and the choice answers one question: will this value ever change?

    • val declares a read-only name. It is assigned once, and the compiler refuses any later assignment.
    • var declares a variable. Its value can be replaced as often as needed.

    Because a val never changes, you can read the code with less effort: wherever you see the name, it has the same value it was given at the start. That is why Scala code uses val by default and var only where a value truly changes, such as a counter, a running total or a position that moves.

    val pi = 3.14159
    var lives = 3
    lives -= 1
    println(s"lives = $lives")
    
    lives = 2
    

    There are no type annotations here. Scala infers Double for pi and Int for lives from the values. The s before the quotes makes the string an interpolator, so $lives is replaced by the current value. Without the s, the output would contain the characters $lives.

    What happens when you reassign a val

    This is the mistake the lesson wants you to recognise:

    val maxLives = 3
    maxLives = 5
    
    -- [E052] Type Error ------------------------------------------------------
      |  maxLives = 5
      |  ^^^^^^^^^^^^
      |  Reassignment to val maxLives
    

    The carets underline the assignment that broke the rule, and the message names the culprit. There are two honest fixes: stop reassigning and keep the val, or change the declaration to var because the value must change.

    Compound operators are assignments too

    The operator += reads the variable, adds to it and stores the result, so it is also a reassignment. On a val that holds an Int it fails as well, although the compiler's wording differs. Scala first looks for a += method on Int, doesn't find one, and reports:

      |  maxLives += 1
      |  ^^^^^^^^^^^^^
      |  value += is not a member of Int
    

    Either way, the cure is the same: a value that gets updated must be a var.

    The exercise step by step

    The program sets up a tiny game: a fixed number of lives and a changing score. The expected output is two lines, maxLives = 3 and score = 15.

    1. Declare the constant. val maxLives = 3. The rules of the game never change, so val is right, and Scala infers Int.
    2. Print it. println(s"maxLives = $maxLives"). Read-only means you can't change the value, but you can read it as often as you like.
    3. Declare the score. var score = 0. A running total has to change, and var allows it.
    4. Add the first points. score += 10 is short for score = score + 10. It compiles only because score is a var.
    5. Add the bonus. score += 5 works the same way and starts from the 10 stored by the previous line.
    6. Print the total. Another interpolated string shows score = 15.

    The solution in Scala

    val maxLives = 3
    println(s"maxLives = $maxLives")
    var score = 0
    score += 10
    score += 5
    println(s"score = $score")
    

    The output is:

    maxLives = 3
    score = 15
    

    Notice what each keyword allows. maxLives stays at 3 from the first line to the last. score goes from 0 to 10 to 15, one assignment at a time.

    Immutability beyond val

    A val stops you from re-pointing a name, but the object behind it may still be changeable. A val holding an ArrayBuffer can't be assigned a different buffer, yet buf += 3 adds an element to the same one. The standard immutable collections, such as List, Vector and Map, can't be changed at all, so combining val with them gives the strongest guarantee.

    Common mistakes

    • Declaring the score with val. The first += then fails to compile. The error points at the update line, not the declaration, so read it back to the keyword.
    • Declaring everything with var. It compiles, but it throws away the guarantee that makes val useful. Start with val, and change it only when the compiler tells you a value needs to change.
    • Forgetting the s. println("maxLives = $maxLives") prints the literal text maxLives = $maxLives.
    • Redeclaring instead of updating. Writing var score = 10 a second time doesn't update the first variable. The compiler rejects it because the name is already defined.

    Key takeaways

    • val is assigned once, var can be reassigned.
    • Reassigning a val is a compile error: Reassignment to val.
    • Updates such as += need a var.
    • Prefer val and use var when a value must change.
    Keep going with ScalaAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free