Variables and Types in Scala
Learn about variables, immutability, type inference, and the Scala type system with practical Docker-ready examples
Scala’s type system is one of its most distinctive features: it is statically and strongly typed, yet rarely forces you to write type annotations by hand. The compiler infers types throughout your code, eliminating boilerplate while catching errors at compile time. This combination—safety without ceremony—sits at the heart of the Scala philosophy.
Beyond inference, Scala makes a meaningful distinction that many languages skip: val for immutable bindings and var for mutable variables. Functional programming in Scala favors val heavily, and you will find that the majority of well-written Scala code reaches for val by default and only introduces var when mutation is genuinely necessary.
In this tutorial you will explore Scala’s core types, the val/var split, type inference and explicit annotations, type conversions, string interpolation, and Option as a safe replacement for null.
val vs var: Immutability First
Scala provides two keywords for naming values:
val— an immutable binding. Once assigned, the name cannot be rebound to a different value. This is the default choice in idiomatic Scala.var— a mutable variable. It can be reassigned after its initial assignment.
Create a file named variables.scala:
| |
The @main annotation marks variables as the program entry point — the Scala 3 equivalent of wrapping everything in object Main { def main(...) }. Scala CLI discovers and runs it automatically.
Scala’s Core Types
Scala’s primitive-equivalent types map directly to JVM primitives at runtime, so there is no boxing overhead for simple numeric operations:
| Type | Description | Example literal |
|---|---|---|
Int | 32-bit signed integer | 42 |
Long | 64-bit signed integer | 9876543210L |
Double | 64-bit floating point | 3.14159 |
Float | 32-bit floating point | 3.14f |
Boolean | true or false | true |
Char | Single Unicode character | 'S' |
String | Sequence of characters (JVM) | "Scala" |
Unit | No meaningful return value (void) | () |
Unlike Java, Scala has no distinction between primitives and wrapper types in source code. You always write Int, never int or Integer. The compiler decides the JVM representation.
Type Conversions and String Interpolation
Scala does not perform implicit numeric widening — converting between numeric types always requires an explicit call. This prevents accidental precision loss. String interpolation, on the other hand, is built into the language with three distinct modes.
Create a file named type_conversions.scala:
| |
The three interpolator styles:
s"..."evaluates$nameand${expression}— the workhorse of Scala string building.f"..."adds C-style format specifiers after the variable (%.2f,%5d).raw"..."treats backslashes literally — useful for regex patterns and Windows paths.
Option: Null Safety Built In
Scala does not encourage using null. Instead, values that might be absent are wrapped in Option[T], a type with exactly two possible values: Some(value) when something is present and None when it is absent. This forces callers to handle the missing case explicitly, eliminating an entire class of null-pointer errors at compile time.
Create a file named option_types.scala:
| |
Option participates fully in Scala’s type system: map, flatMap, filter, and foreach all work on it, making it a natural fit for for-comprehensions and functional pipelines.
Running with Docker
| |
Scala CLI compiles each file on first run and caches the result, so subsequent runs of the same file are significantly faster.
Expected Output
Running variables.scala:
Language: Scala
Born: 2004
Version: 3.7
Statically typed: true
Count: 42, Pi: 3.14159265, Initial: S
Message: Hello, Scala!
Counter: 2
Running type_conversions.scala:
Int: 42
Double: 42.0
Long: 42
String: "42"
Parsed Int: 100
Parsed Double: 2.718
Scala version 3
SCALA was released in 2016 (approx)
Pi to 2 places: 3.14
Padded integer: 3
Newline literal: \n stays as-is
Running option_types.scala:
Found: Scala
Value is absent
Present with default: Scala
Absent with default: default
Mapped present: Some(SCALA)
Mapped absent: None
Inferred type holds: Some(2004)
Key Concepts
- Prefer
valovervar— immutable bindings are the default in idiomatic Scala; reach forvaronly when you have a clear reason to mutate. - Type inference is pervasive — you rarely need to write type annotations; the compiler deduces them from the right-hand side expression.
- Explicit type annotations clarify intent — annotating public APIs and complex expressions improves readability even when the compiler does not require it.
- No implicit numeric conversions — Scala requires explicit
.toDouble,.toInt, etc., preventing silent precision loss across numeric boundaries. - Three string interpolators —
s"..."for general use,f"..."for formatted output,raw"..."for literal backslash handling. Option[T]replaces null — the type system encodes the possibility of absence, forcing safe handling at compile time rather than crashing at runtime.- All types are objects —
Int,Double, andBooleanhave methods (.toDouble,.toString,.abs) because Scala unifies primitive and reference types in its type hierarchy. Unitis a real type — functions that perform side effects (likeprintln) returnUnit, making effect-ful code visible in the type signature.
Running Today
All examples can be run using Docker:
docker pull virtuslab/scala-cli:latest
Comments
Loading comments...
Leave a Comment