Syntax, Case Classes & Pattern Matching
Basics
val name: String = "Alice" // immutable — reassignment is a compile error
var counter: Int = 0 // mutable — favor val, reach for var only when needed
def add(a: Int, b: Int): Int = a + b
s"Hello, $name! You are ${counter + 1}." // string interpolation
// Option — the idiomatic null-free way to represent absence
val maybeUser: Option[User] = users.find(_.id == id)
maybeUser match {
case Some(user) => println(user.name)
case None => println("not found")
}
maybeUser.map(_.name).getOrElse("unknown")Case Classes & Immutable Updates
case class User(name: String, age: Int, email: String)
val alice = User("Alice", 30, "alice@example.com")
val olderAlice = alice.copy(age = 31) // NEW instance — original stays unchanged
alice == User("Alice", 30, "alice@example.com") // true — compared by value, not reference
// sealed trait + case classes — exhaustive, compiler-checked pattern matching
sealed trait Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(width: Double, height: Double) extends Shape
def area(shape: Shape): Double = shape match {
case Circle(r) => math.Pi * r * r
case Rectangle(w, h) => w * h
// adding a new Shape subtype and forgetting a case here triggers a
// compiler warning/error — this is the "expression problem" solvedCollections & Higher-Order Functions
val numbers = List(1, 2, 3, 4, 5)
numbers.map(_ * 2) // List(2, 4, 6, 8, 10)
numbers.filter(_ % 2 == 0) // List(2, 4)
numbers.reduce(_ + _) // 15
// flatMap flattens the result — map alone would nest it
List(1, 2).flatMap(n => List(n, n * 10)) // List(1, 10, 2, 20)
// for comprehension — sugar over map/flatMap/filter, short-circuits on None
for {
a <- Some(3)
b <- Some(4)
} yield a + b // Some(7) — if EITHER were None, the whole result is NoneKeep your own version of these notes — editable, searchable, and organised by your stack.
Start free