Free Handbook · Every example compiled & verified

Fundamentals

val versus var, type inference, the basic types, string interpolation with s, f and raw, operators as methods, keyboard input and safe conversions.

0 / 142 lessons🔥 0 day streak
ShareXLinkedIn

Module 01 · what you'll be able to do

  • Choose between val and var and explain why Scala code defaults to val
  • Read an inferred type and know when to write the type yourself
  • Use Int, Long, Double, Boolean, Char and String and predict integer division and overflow
  • Build strings with the s, f and raw interpolators and multi-line strings
  • Read input with scala.io.StdIn and convert text safely with toInt and toIntOption
01

val and var

Scala has two ways to name a value. val creates an immutable binding — once assigned, it can never point at anything else. var creates a mutable variable you can reassign. Idiomatic Scala uses val almost everywhere and reaches for var only for a local counter or accumulator, because code where names never change is easier to read, test and run in parallel.

scalaMain.scala
@main def run(): Unit =
  val name = "Asha"
  var visits = 1
  println(s"$name has visited $visits time")

  visits = visits + 1
  visits += 1
  println(s"$name has visited $visits times")
Outputcompiled & run with real Scala
Asha has visited 1 time
Asha has visited 3 times
Your turn

Try adding name = "Ravi" after the first println and read the compiler error — then undo it.

Error you will hit

Reassignment to val

scala
@main def run(): Unit =
  val score = 10
  score = 20
  println(score)
-- [E052] Type Error: Main.scala:3:8
3 |  score = 20
  |  ^^^^^^^^^^
  |  Reassignment to val score
1 error found
Compilation failed
Why the compiler said that

score was declared with val, so the compiler forbids pointing it at a new value. This is caught before the program ever runs.

The fix

If the value genuinely changes, declare it with var. More often the better fix is a new name: val finalScore = score + 10.

scala
@main def run(): Unit =
  var score = 10
  score = 20
  println(score)
val is not "deep" immutability
val fixes the name, not the object. val buf = scala.collection.mutable.ArrayBuffer(1) can still have items appended; you just cannot make buf point at a different buffer. Real immutability comes from immutable types like List and case classes — Module 06 covers the difference.

There is also lazy val: its right-hand side is not evaluated until the first time the value is used, and then the result is cached.

scalaMain.scala
@main def run(): Unit =
  lazy val config =
    println("loading config...")
    "prod"
  println("program started")
  println(config)
  println(config)
Outputcompiled & run with real Scala
program started
loading config...
prod
prod

The body ran once, at the first use — not at the declaration, and not again on the second use.

02

Basic types and type inference

Every value in Scala has a type known at compile time, but you rarely have to write it: the compiler infers it from the right-hand side. val n = 42 is an Int; val d = 4.2 is a Double. Write the type yourself with a colon when you want to document intent or force a wider type: val big: Long = 42.

TypeSizeLiteralNotes
Int32-bit42, 1_000_000Default for whole numbers; about ±2.1 billion
Long64-bit42LIDs, timestamps, byte counts
Double64-bit float3.14, 1e6Default for decimals; not exact (use BigDecimal for money)
Float32-bit float3.14fRare outside graphics and ML
Boolean—true, false
Char16-bit'a'Single quotes
String—"hi"Double quotes; a java.lang.String
Byte / Short8 / 16-bit—Binary data, rarely used directly
Unit—()The "no useful value" type; see Module 02
scalaMain.scala
@main def run(): Unit =
  val count = 42
  val price = 19.99
  val ok = true
  val grade = 'A'
  val big: Long = 3_000_000_000L
  println(count.getClass.getSimpleName)
  println(price.getClass.getSimpleName)
  println(s"$count $price $ok $grade $big")
  println(Int.MaxValue)
  println(Int.MaxValue + 1)
Outputcompiled & run with real Scala
int
double
42 19.99 true A 3000000000
2147483647
-2147483648
Your turn

Change the last line to Int.MaxValue + 1L. The L makes the sum a Long — predict what prints.

Int overflow wraps silently
Int.MaxValue + 1 does not throw — it wraps round to the most negative Int. Counting rows, bytes or milliseconds in a data job can blow past 2.1 billion easily; use Long for those.

getClass printed int and double because at runtime Scala's Int and Double compile to fast JVM primitives. In your code they are still full objects with methods: 42.max(7), 3.7.round, 'a'.isLetter.

Error you will hit

A Double does not fit in an Int

scala
@main def run(): Unit =
  val age: Int = "42"
  println(age)
-- [E007] Type Mismatch Error: Main.scala:2:17
2 |  val age: Int = "42"
  |                 ^^^^
  |                 Found:    ("42" : String)
  |                 Required: Int
1 error found
Compilation failed
Why the compiler said that

Scala never converts between unrelated types behind your back. A String is not an Int, even if it contains digits; the same goes for putting a Double into an Int, which would lose the fraction.

The fix

Convert explicitly: "42".toInt for text, 3.9.toInt (truncates to 3) or 3.9.round for decimals.

scala
@main def run(): Unit =
  val age: Int = "42".toInt
  println(age)
03

Strings and interpolators

A Scala String is a Java string with extra methods. To put values inside a string, prefix the literal with an interpolator: s"..." substitutes $name or any expression inside ${...}; f"..." also takes printf-style formats like %.2f and checks them against the types at compile time; raw"..." leaves backslashes alone.

scalaMain.scala
@main def run(): Unit =
  val item = "coffee"
  val qty = 3
  val price = 2.5
  println(s"$qty x $item")
  println(s"total: ${qty * price}")
  println(f"total: $$${qty * price}%.2f")
  println(f"$item%-8s|$qty%4d|")
  println("tab:\tend")
  println(raw"tab:\tend")
Outputcompiled & run with real Scala
3 x coffee
total: 7.5
total: $7.50
coffee  |   3|
tab:	end
tab:\tend
Your turn

Print the price with three decimal places and padded to width 10 using f"$price%10.3f".

Triple quotes make a multi-line string where nothing needs escaping. stripMargin removes everything up to and including a leading | on each line, so you can indent the literal with your code.

scalaMain.scala
@main def run(): Unit =
  val sql =
    """SELECT name
      |FROM users
      |WHERE active = true""".stripMargin
  println(sql)

  val s = "  Hello, Scala  "
  println(s.trim.toUpperCase)
  println(s.trim.length)
  println(s.contains("Scala"))
  println("a,b,c".split(",").toList)
  println("scala" * 2)
  println("hello".capitalize.reverse)
Outputcompiled & run with real Scala
SELECT name
FROM users
WHERE active = true
HELLO, SCALA
12
true
List(a, b, c)
scalascala
olleH

Strings are immutable: trim, toUpperCase and reverse all return a new string and leave s unchanged.

In real jobs
Triple-quoted strings with stripMargin are how Spark code embeds SQL (spark.sql("""...""")) and how tests embed JSON fixtures. f"..." is what you use for log lines with fixed-width numbers.
04

Operators are methods

In Scala, 1 + 2 is really a method call: 1.+(2). Any method with one parameter can be written in this infix style, which is why a max b and list contains 3 are legal. It also means operators follow ordinary method rules — there is nothing special about + except its name.

scalaMain.scala
@main def run(): Unit =
  println(1 + 2)
  println(1.+(2))
  println(7 / 2)
  println(7.0 / 2)
  println(7 % 3)
  println(-7 % 3)
  println(10 max 3)
  println(2 + 3 * 4)
  println(math.pow(2, 10))
  println(math.abs(-5))
Outputcompiled & run with real Scala
3
3
3
3.5
1
-1
10
14
1024.0
5
Your turn

Predict 7 / 2 * 2 and 7.0 / 2 * 2 before you run them.

KindOperatorsNotes
Arithmetic+ - * / %Int / Int truncates toward zero; % keeps the sign of the left side
Comparison== != < > <= >=== compares values, even for strings and case classes
Logical&& || !Short-circuit: the right side runs only if needed
Bitwise& | ^ ~ << >> >>>On Int and Long
Assignment= += -= *= /=Only on vars; there is no ++ or --
scalaMain.scala
@main def run(): Unit =
  val a = "scala"
  val b = "SCALA".toLowerCase  // built at run time: a different object
  println(a == b)
  println(a eq b)
  println(3 == 3.0)
  val age = 20
  println(age >= 18 && age < 65)
  println(!(age > 30))
Outputcompiled & run with real Scala
true
false
true
true
true

== in Scala calls equals, so two strings with the same text are equal. eq checks whether they are the very same object — which you almost never want. This is the opposite of Java, where == on strings compares references.

There is no ++
Scala deliberately has no i++ or ++i. Write i += 1. In Scala ++ means "concatenate two collections".
05

Reading keyboard input

scala.io.StdIn.readLine() reads one line typed by the user (without the newline) and returns it as a String. readInt() and readDouble() read a whole line and parse it as a number. The example below was run with the input shown under "stdin" fed to it.

scalaMain.scala
import scala.io.StdIn.readLine

@main def run(): Unit =
  print("Your name: ")
  val name = readLine()
  print("Your age: ")
  val age = readLine().trim.toInt
  println()
  println(s"Hi $name, next year you will be ${age + 1}.")
Outputcompiled & run with real Scala
Your name: Your age: 
Hi Meera, next year you will be 28.

When input comes from a file or pipe the typed text is not echoed, so the two prompts appear on one line. In a terminal you would see your typing after each prompt.

Your turn

Ask for a second number and print the sum of both ages.

Reading many numbers from one line is a common interview and competitive-programming pattern: split on whitespace and convert each piece.

scalaMain.scala
import scala.io.StdIn.readLine

@main def run(): Unit =
  val nums = readLine().trim.split("\\s+").map(_.toInt)
  println(s"count=${nums.length} sum=${nums.sum} max=${nums.max}")
Outputcompiled & run with real Scala
count=6 sum=108 max=42

"\\s+" in the source is the regex \s+, one or more whitespace characters, so double spaces do not produce empty pieces.

06

Converting between types

Numbers convert with toInt, toLong, toDouble and friends. Anything converts to text with toString. Text converts to numbers with "42".toInt — but that throws if the text is not a number. The safe version, toIntOption, returns Some(42) or None instead, and you decide what to do with the failure.

scalaMain.scala
@main def run(): Unit =
  println(3.99.toInt)
  println(3.99.round)
  println(7.toDouble / 2)
  println(65.toChar)
  println('A'.toInt)
  println(42.toString + "!")
  println("123".toInt + 1)
  println("12a".toIntOption)
  println("123".toIntOption)
  println("12a".toIntOption.getOrElse(0))
Outputcompiled & run with real Scala
3
4
3.5
A
65
42!
124
None
Some(123)
0
Your turn

Read a line with readLine() and print "not a number" if toIntOption returns None. You will learn the clean way to do this with match in Module 02.

VisualizeWhere the fraction goesStep 1 / 5
val price = 3.99
val whole = price.toInt
val rounded = price.round
val parsed = "12a".toIntOption
val safe = parsed.getOrElse(0)
Line 1

A decimal literal is inferred as Double.

Variables now
price3.99
All 5 steps as a table
StepLineWhat happenedVariables now
11A decimal literal is inferred as Double.price = 3.99
22toInt truncates toward zero — it drops the fraction, it does not round.price = 3.99 whole = 3
33round rounds to the nearest whole number and returns a Long.price = 3.99 whole = 3 rounded = 4
44"12a" is not a valid integer, so toIntOption returns None instead of throwing.parsed = None
55getOrElse unwraps a Some or falls back to the default for None.parsed = None safe = 0
Error you will hit

NumberFormatException — toInt on text that is not a number

scala
@main def run(): Unit =
  val n = "4x2".toInt
  println(n)
Exception in thread "main" java.lang.NumberFormatException: For input string: "4x2"
	at java.base/java.lang.NumberFormatException.forInputString(Unknown Source)
	at java.base/java.lang.Integer.parseInt(Unknown Source)
	at scala.collection.StringOps$.toInt$extension(StringOps.scala:971)
	at Main$package$.run(Main.scala:2)
	at run.main(Main.scala:1)
Why the compiler said that

This one compiles — the compiler cannot know what text will arrive at runtime — and crashes when it runs. Read a JVM stack trace from the top for what went wrong (the exception and message), then scan down for the first line in your file (Main.scala:2) for where.

The fix

Use toIntOption for any text from a user, file or network, and handle None.

scala
@main def run(): Unit =
  val n = "4x2".toIntOption.getOrElse(0)
  println(n)
val
An immutable binding: the name always refers to the same value.
var
A mutable variable that can be reassigned with =.
lazy val
A val whose right-hand side is evaluated on first use and then cached.
Type inference
The compiler working out a type from the value, so you do not have to write it.
String interpolator
A prefix like s, f or raw before a string literal that substitutes $values into it.
Infix notation
Calling a one-argument method without dot and parentheses: a max b means a.max(b).
toIntOption
Parses text to Some(number), or None if it is not a valid Int — the non-throwing toInt.
Unit
The type with one value, (), returned by code run only for its side effect.
Quick check

What does println(7 / 2 + 7 % 2) print?

Quick check

Which line compiles?

Frequently asked questions

When should I use var in Scala?
Rarely — for a local counter or accumulator inside one method, where a loop is clearer than a fold. Never for fields shared between threads or for values returned from a function. If you reach for var, first ask whether a new val or a collection method (sum, foldLeft, map) says it better.
What is the difference between == and eq in Scala?
== compares values by calling equals (and handles null safely), so two strings or case classes with the same contents are equal. eq checks reference identity — whether both names point at the same object in memory. Use ==.
How do I read an integer from the keyboard in Scala?
Use scala.io.StdIn.readLine() to get the line, then .trim.toIntOption to convert it safely. StdIn.readInt() also works but throws NumberFormatException on bad input.

Finish the Scala handbook, then get hired

Sit the exam for your certificate, run your resume through the ATS checker, and see the jobs that ask for exactly this.

Check my resume
Found this course useful? Share it.
ShareXLinkedIn

Comments

0

Join the conversation. Sign in to leave a comment — we'd love to hear your thoughts.