Variables, Types, and Functions
Declare immutable bindings with val and mutable ones with var — val x: Int = 5 fixes x forever, while var y = 5; y += 1 allows reassignment; Scala's type inference means you can usually drop the type annotation, val x = 5 infers Int, except on public method signatures, where explicit return types are considered good practice for readability and faster compilation. Functions are declared with def name(params): ReturnType = body, and because the last expression in a block is automatically its return value, there's no return keyword needed in idiomatic code — def square(n: Int): Int = n * n is a complete, valid method, and default parameter values, def greet(name: String = "World"): String = s"Hello, $name!", let callers omit arguments that have sensible defaults.
Cricket analogy: It's like a fixed net session length announced before play, val, unchangeable, versus a flexible practice slot the coach can extend on the day, var, reassignable — a quick-reference sheet is your dressing-room whiteboard reminder of which is which before you walk out to bat.
// Variables
val name: String = "Scala" // immutable
var counter = 0 // mutable, type inferred as Int
counter += 1
// Functions
def square(n: Int): Int = n * n
def greet(name: String = "World"): String =
s"Hello, $name!"
// Multi-line function body
def describe(n: Int): String = {
val parity = if (n % 2 == 0) "even" else "odd"
s"$n is $parity"
}
// Anonymous functions / lambdas
val double: Int => Int = x => x * 2
val add = (a: Int, b: Int) => a + bCollections Cheat Sheet
List is Scala's default immutable singly-linked sequence, List(1, 2, 3), Vector gives effectively constant-time random access and updates for larger collections, Set stores unique elements, Set(1, 2, 2, 3) becomes Set(1, 2, 3), and Map[K, V] stores key-value pairs, Map("a" -> 1, "b" -> 2); all four have both immutable, default, scala.collection.immutable, and mutable, scala.collection.mutable, variants. The core transformation methods to memorize are map, transform each element, filter, keep elements matching a predicate, flatMap, transform and flatten, fold/foldLeft/foldRight, reduce to a single value with a starting accumulator, sortBy/sorted, order elements, and groupBy, partition into a Map[K, List[A]] by a key function — nearly every everyday data-wrangling task in Scala is some composition of these six operations.
Cricket analogy: It's like a scoring app's toolbar having exactly six buttons you use constantly — filter by boundaries, sort by run rate, group by bowler — a quick-reference sheet listing map/filter/flatMap/fold/sortBy/groupBy is that same toolbar for collections.
val nums = List(5, 3, 8, 1, 9, 2)
nums.map(_ * 2) // List(10, 6, 16, 2, 18, 4)
nums.filter(_ > 4) // List(5, 8, 9)
nums.flatMap(n => List(n, -n)) // List(5, -5, 3, -3, 8, -8, ...)
nums.fold(0)(_ + _) // 28
nums.foldLeft(0)(_ + _) // 28
nums.sortBy(n => -n) // List(9, 8, 5, 3, 2, 1)
nums.groupBy(_ % 2 == 0) // Map(false -> List(5, 3, 1, 9), true -> List(8, 2))
val scores = Map("alice" -> 90, "bob" -> 85)
scores.getOrElse("carol", 0) // 0
scores.updated("carol", 78) // Map("alice" -> 90, "bob" -> 85, "carol" -> 78)
val uniqueTags = Set("scala", "jvm", "scala") // Set("scala", "jvm")Pattern Matching and Control Flow
match is Scala's structural switch and the single most-used control construct beyond simple if: value match { case pattern => result } can match literals, types, case s: String => ..., case class shapes with destructuring, case Point(x, y) => ..., and can add a boolean guard, case n if n > 0 => "positive"; an underscore case _ => ... catches anything unmatched, and omitting it causes a MatchError at runtime if no case fits. for-comprehensions provide a uniform syntax for iterating, for (x <- list) println(x), filtering, for (x <- list if x > 0) yield x, and composing multiple monadic values, for { a <- optionA; b <- optionB } yield a + b, which short-circuits to None if either is empty — remembering that any for with a yield desugars to map/flatMap/withFilter calls demystifies most 'why doesn't this compile' questions.
Cricket analogy: It's like an umpire's decision tree — an lbw shout matches one protocol, a caught-behind appeal matches another, and 'not out, no shot offered' is the catch-all default — Scala's match expression works the same way, with case _ as that final catch-all ruling.
sealed trait Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(width: Double, height: Double) extends Shape
case object Unknown extends Shape
def area(shape: Shape): Double = shape match {
case Circle(r) if r > 0 => math.Pi * r * r
case Rectangle(w, h) => w * h
case Unknown => 0.0
}
// for-comprehension over Option (short-circuits on None)
def safeDivide(a: Int, b: Int): Option[Int] =
if (b == 0) None else Some(a / b)
val result: Option[Int] = for {
x <- safeDivide(10, 2)
y <- safeDivide(x, 0) // yields None, short-circuiting the whole block
} yield x + y
// result == NoneForgetting a case _ fallback in a match that isn't over a sealed trait is a common source of runtime MatchError crashes — the compiler can only warn you about missing cases when it knows the full closed set of possible types, which is exactly what sealed trait hierarchies give you.
- val is immutable, var is reassignable — default to val in idiomatic Scala.
- def name(params): ReturnType = body defines a function; the last expression is its return value.
- List, Vector, Set, and Map are immutable by default; mutable variants live in scala.collection.mutable.
- map, filter, flatMap, fold/foldLeft/foldRight, sortBy, and groupBy cover most everyday collection tasks.
- match supports literal, type, and case-class patterns with optional guards and a case _ catch-all.
- Omitting case _ risks a MatchError at runtime if no case fits the value.
- for-comprehensions with yield desugar to map/flatMap/withFilter calls.
Practice what you learned
1. What is the difference between val and var in Scala?
2. What happens if a match expression has no matching case and no case _ fallback?
3. Which method transforms each element and flattens one level of nesting?
4. What does a for-comprehension with yield desugar into?
5. Which collection type gives effectively constant-time random access and updates for larger sizes, unlike List?
Was this page helpful?
You May Also Like
Scala vs Java
A practical comparison of Scala and Java covering syntax, type systems, concurrency, and when to choose one over the other on the JVM.
Scala Best Practices
Idiomatic patterns for writing clean, maintainable, and performant Scala code, from immutability to error handling to project structure.
Scala Interview Questions
Frequently asked Scala interview topics — from core language features to collections, concurrency, and functional programming concepts — with the reasoning interviewers look for.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics