Why Scala?
This is a working list — features I reach for regularly and would miss in any other language.
Rich type system
Unit and Nothing do real work. Nothing is the bottom type: an Exception has type Nothing, which means it can be used in place of any return type without a cast. This is not just a curiosity — it’s what makes throw expressions compose correctly with the type checker.
Pattern matching on almost anything
List(1,2,3) match {
case Nil => println("The list is empty")
case x @ h :: _ => println(s"$x starts from: $h")
}
// List(1, 2, 3) starts from: 1
Pattern matching works on case classes, sealed hierarchies, tuples, collections, and arbitrary types via custom unapply. The @ binding captures the whole structure while also destructuring it.
Case classes replace Java boilerplate
In Java:
class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
// equals, hashcode, toString...
}
Point p = new Point(10, 20);
In Scala:
case class Point(x: Int, y: Int)
val p = Point(10, 20)
// p: Point = Point(x = 10, y = 20)
equals, hashcode, toString, unapply, and copy are generated by the compiler. No annotation processor, no framework — it is part of the language.
Compiler exhaustiveness checks
Seal a trait and the compiler tracks every case. Forget one and it is a compile error:
sealed trait Color
case object Red extends Color
case object Yellow extends Color
case object Green extends Color
def show(c: Color): String = c match {
case Red => "Red"
case Green => "Green"
}
// error: match may not be exhaustive.
// It would fail on the following input: Yellow
// Applicable -Wconf / @nowarn filters for this fatal warning: msg=<part of the message>, cat=other-match-analysis, site=repl.MdocSession.MdocApp.show
// def show(c: Color): String = c match {
// ^
This is not a runtime warning — it is caught at compile time.
Other features worth naming
- Currying and partial application — functions can be partially applied to produce new functions
- Higher-order functions — functions are first-class values
- Tail recursion — the compiler optimizes tail-recursive methods to loops via
@tailrec - Implicits and extension methods — add behavior to existing types without subclassing
- Generics and higher-kinded types — abstract over type constructors, not just types
- Immutable collections — the standard library defaults to immutable; mutable variants are opt-in
- Sealed traits — model closed algebras with compiler-enforced exhaustiveness
- Macros and derivation — generate code at compile time from type information
- sbt plugins — build configuration is Scala code, not XML
- JVM and JS targets — the same source can compile to the JVM or Scala.js
- JVM interop — full access to the Java ecosystem without ceremony
- Unchecked exceptions — no forced try/catch; error handling is done through types
- Compiler optimizations —
final valand@inlinegive low-level control when needed _syntax — wildcard for imports, anonymous function parameter, existential type; context determines meaning- Imports anywhere — imports can appear inside methods, expressions, or blocks, scoped to where they’re needed
Will add more once recalled.