Scala — Reference

Source: https://docs.scala-lang.org/

Scala

  • Created: 2004 by Martin Odersky at EPFL (early prototypes from 2003)
  • Latest stable: Scala 3 → 3.8.3 (2026-03-31) is “Scala Next”; Scala 3.3 LTS → 3.3.7 is the recommended publishing baseline; Scala 2 → 2.13.18 (2025-11-24) still actively maintained
  • Paradigms: Multi-paradigm — strong functional programming heritage (HM-style inference, ADTs, pattern matching), full object orientation (everything is an object), expressive type-level programming
  • Typing: Static, strong, structural-where-needed, with extensive inference. Higher-kinded types, path-dependent types, type classes via given/using (Scala 3) or implicit (Scala 2), match types, opaque types, intersection (A & B) / union (A | B) types
  • Memory: Garbage-collected on the JVM (whichever GC the JVM uses); Scala Native uses the Boehm GC by default with Immix and Commix optional concurrent collectors
  • Compilation: AOT to JVM bytecode (default, via scalac for 2 / dotty alias for 3); Scala.js to JS; Scala Native to native code via LLVM
  • Primary domains: Big data (Spark, Flink, Kafka Streams), backend services (Akka/Pekko, ZIO, http4s), data engineering, fintech, ML pipelines, compiler/PL research
  • Official docs: https://docs.scala-lang.org/

At a glance

  • Steward: Scala Center at EPFL + Lightbend/Akka (now Akka Inc.) historically; the Scala 3 governance is community-led with EPFL + VirtusLab + community SIPs (Scala Improvement Proposals).
  • Scala 3 (Dotty) shipped 2021-05-14 with a redesigned typer (DOT calculus foundation), simplified implicits, new metaprogramming, Python-style indentation syntax.
  • LTS strategy (formalized in 2023): a Scala 3.3.x LTS track receives bug-fixes for years; “Scala Next” (currently 3.8.x) gets new features. Library publishers are advised to target 3.3 LTS. (https://www.scala-lang.org/blog/2024/02/29/long-term-compatibility-plans.html)
  • Reference impls: Scala 2 compiler (nsc — “new Scala compiler”) is the historic Java-based impl; Scala 3 is Dotty (dotc). Both produce JVM .class files.
  • License: Apache 2.0.

Getting started

  • Install: cs setup — the Coursier installer is the recommended path on all platforms (https://www.scala-lang.org/download/). It installs scala, scalac, sbt, scala-cli, JDK if missing. Alternatives: SDKMAN! (sdk install scala 3.8.3, sdk install sbt), Homebrew, asdf.

  • Hello world (modern, scala-cli): Hello.scala:

    @main def hello() = println("Hello, world!")

    Run: scala-cli run Hello.scala (or just scala Hello.scala with the new CLI which is a thin alias for scala-cli since 3.5).

  • Project layout (sbt): build.sbt at root, project/build.properties (sbt version), project/plugins.sbt, sources in src/main/scala/, tests in src/test/scala/. For Scala 3 you can also use indentation-based syntax in any file.

  • Build tool: sbt (Scala Build Tool — interactive shell with incremental compile via Zinc) is canonical. Mill (gaining traction; faster, more imperative config), Maven (via scala-maven-plugin), Gradle (with gradle-scala-plugin), Bazel, and scala-cli (single-file/script-friendly, since 2022) for small scripts. The new scala CLI (3.5+) wraps scala-cli.

  • REPL: scala opens the REPL; scala-cli repl; in sbt the console task drops into a REPL with the project on classpath. Scala 3 REPL supports rich completion (TabComplete) and :doc for ScalaDoc lookups.

Basics

  • Types/literals: Int, Long, Short, Byte, Double, Float, Boolean, Char, String, Unit, Null, Nothing, Any, AnyRef, AnyVal. BigInt, BigDecimal. Numeric literals: 1_000_000 (Scala 2.13+), 0xFF, 1.5e3, 1L, 1f. Tuples ((1, "x", true)).
  • Variables: val (immutable), var (mutable), lazy val (memoized). def is a method (re-evaluated). Type inference: val x = 1 infers Int.
  • Scoping: Lexical, block-scoped. Top-level definitions allowed in Scala 3 (Scala 2 required wrapping in object). Indentation-based syntax (Scala 3) replaces curly braces optionally — end if/end def markers for clarity.
  • Control flow: if/else is an expression; match is the workhorse — pattern matching on literals, types, ADTs, tuples, extractors (unapply), and match types (Scala 3, type-level). for comprehensions desugar to flatMap/map/withFilter/foreach. while/do-while. No break/continue — use scala.util.boundary (Scala 3.3+) or recursion.
  • Functions: def add(a: Int, b: Int): Int = a + b. First-class — (a: Int, b: Int) => a + b is a Function2[Int, Int, Int]. Methods auto-eta-expand to functions where needed. Currying (def add(a: Int)(b: Int)), default args, named args, by-name params (x: => Int), context functions (Context ?=> A, Scala 3).
  • Strings: "hello"; multi-line """...""" with .stripMargin. String interpolation via prefixes: s"Hello $name", f"$pi%.2f" (printf-style), raw"\n" (no escape), and user-defined interpolators (def sql"...").
  • Collections: List, Vector, Seq, Set, Map, Array, Range, LazyList (Scala 2.13+, replaces Stream), ArraySeq, Queue, BitSet. Both immutable (default — scala.collection.immutable._) and mutable (scala.collection.mutable._). The 2.13 redesign unified the hierarchy; Scala 3 inherits it.

Intermediate

  • Type system depth: parametric polymorphism with bounded (T <: Upper, T >: Lower) and view bounds, variance annotations (+T covariant, -T contravariant), higher-kinded types (F[_]), type lambdas ([X] =>> F[X, Int] in 3 / ({type L[X] = F[X, Int]})#L in 2), abstract type members (type T <: Bound), path-dependent types (a.T), self types (this: Foo =>).
  • Type classes: Scala 2 used implicit def / implicit val / implicit class. Scala 3 redesigned this as given instances + using parameters + extension methods + type class derivation (derives Show). Cleaner, less ambiguity.
  • Modules: package foo.bar, import foo.bar.{Baz, Qux => Q}. Object as module: object MyModule { def f = ... }. Companion objects pair with classes for static-like members. Scala 3 introduces export clauses (export myModule.{f, g}) for re-exporting members.
  • Error handling: Exceptions exist (throw/try/catch/finally) but idiomatic FP code uses Try[T], Either[E, A] (right-biased since 2.12), Option[T], or effect types (IO[T] from Cats Effect, Task[T] from ZIO). Scala 3 Option#getOrElse + for comprehensions for railway-oriented code. scala.util.boundary (Scala 3.3+) for early-return / non-local control without exceptions.
  • Concurrency primitives:
    • JVM threads, java.util.concurrent.*, Future (scala.concurrent.Future — Scala-flavored, takes implicit ExecutionContext).
    • Effect systems: Cats Effect 3 (IO), ZIO 2 — fiber-based, structured concurrency, fearless cancellation.
    • Pekko (Apache fork of Akka after Akka changed its license in 2022) for actors + streams.
    • Ox (Scala 3, structured concurrency, JDK 21+ virtual threads).
    • Loom integration: Scala 3 with JDK 21+ benefits from virtual threads (Thread.startVirtualThread).
  • I/O: Java NIO is fully available; scala.io.Source.fromFile(...) for quick reads; fs2 (functional streaming, Cats Effect), ZIO Streams, Pekko Streams for production. os-lib (Li Haoyi) is a popular subprocess/path library.
  • Stdlib highlights: Option, Either, Try, collections (rich map/filter/fold/groupBy/scanLeft/zipWithIndex), Ordering/Ordered, Numeric, Equiv, concurrent.Future + Promise, concurrent.duration._ (5.seconds), util.Random, util.matching.Regex, xml (deprecated, use scala-xml lib), reflect.

Advanced

  • Memory/GC: On the JVM, you get the JVM’s GC (G1 default; ZGC for low-pause; Shenandoah). Scala Native offers Boehm-Demers-Weiser (default, conservative), Immix (single-threaded mark-region), Commix (concurrent Immix). (https://scala-native.org/en/stable/user/lang.html#garbage-collectors)
  • Concurrency deep dive:
    • Cats Effect 3 runtime: a work-stealing fiber scheduler (IORuntime) with two thread pools — compute (CPU count) and blocking (elastic). IO.cede voluntarily yields. (https://typelevel.org/cats-effect/docs/concepts)
    • ZIO 2 runtime: also fiber-based, with built-in Schedule, Hub, Queue, STM, structured supervision; Runtime.default. (https://zio.dev/reference/concurrency/)
    • Akka/Pekko: actor model with mailboxes; Akka Cluster + Sharding for distributed actors.
    • On JDK 21+, fibers can be backed by virtual threads (Dispatcher.parasitic / IORuntime.global config).
  • FFI: Scala Native has direct C FFI via @extern annotations and extern object blocks. JVM Scala uses Java’s JNI/Panama (java.lang.foreign). Scala.js uses js.native / @JSImport for JS interop.
  • Reflection: scala.reflect.runtime.universe (heavyweight, mostly Scala 2 — discouraged in 3); TypeTag/ClassTag for runtime type recovery; Scala 3 metaprogramming via inline + quotes is the modern replacement.
  • Performance tools: JMH via sbt-jmh plugin (microbenchmarks), async-profiler + JFR + VisualVM, YourKit / JProfiler, IntelliJ profiler. For Spark: Spark UI + history server. For Cats Effect / ZIO: built-in tracing and metrics integration with OpenTelemetry.

God mode

  • Scala 3 macros (quotes/splices): inline def + ${ ... } (splice) + '{ ... } (quote). The macro author writes a function Expr[T] => Expr[U] and the compiler runs it during typer. Type-class derivation, Mirror (compile-time info about ADTs), summonFrom (compile-time conditional). Far simpler than Scala 2’s def macro mess. (https://docs.scala-lang.org/scala3/guides/macros/)
  • Scala 2 def macros (deprecated in 3): the old c.universe-based macro API — powerful, fragile, IDE-hostile. Scala 3 introduced whitebox- and blackbox-equivalent patterns via inline + transparent inline.
  • Implicits / given/using: Scala 2 had one keyword implicit doing five jobs (parameter, conversion, class, value, evidence). Scala 3 split it into given/using (instances + params), extension (methods), Conversion type class (explicit conversions), import foo.given for explicit instance imports. (https://docs.scala-lang.org/scala3/book/ca-given-using-clauses.html)
  • Type lambdas (Scala 3): [X] =>> F[X, Int] first-class — no more “kind projector” plugin needed. Used heavily in Cats / typelevel libs.
  • Match types (Scala 3): type-level pattern match — type Elem[X] = X match { case String => Char; case Array[t] => t; case Iterable[t] => t }. Foundation for compile-time computation.
  • Opaque types (Scala 3): zero-cost newtypes — opaque type UserId = Long is Long at runtime, distinct at compile time. Replaces value class (which had boxing edge cases).
  • Dependent function types (Scala 3): (x: Foo) => x.T — function whose return type depends on the argument value.
  • scalac plugin API: both 2 and 3 expose plugin APIs. Plugins like kind-projector (Scala 2), bm4 (better-monadic-for), scalafix rules, scalapb (protobuf codegen). Scala 3 plugins integrate into the compiler phases via StandardPlugin.
  • Scala Native: AOT-compiles Scala to a single binary via LLVM. Supports interop with C, multi-threading on JVM-like memory model. Used for serverless / CLI tools where JVM startup is too slow. (https://scala-native.org/)
  • Scala.js: compiles Scala to highly optimized JavaScript with full type safety. Used by Slinky (React), Laminar (FRP), the Scala.js compiler itself ships in CI for many libraries.
  • ZIO / Cats Effect runtime internals: fiber scheduler is a work-stealing pool with deque-per-worker; IO.async/ZIO.async register callbacks; cancellation propagates via interruptible regions. Both runtimes lift to virtual threads on JDK 21+.
  • GADT support (Scala 3): generalized algebraic data types — pattern matching narrows type parameters. Scala 2 had partial support; 3 has it more uniformly.

Idioms & style

  • Naming: camelCase for methods/values, PascalCase for types/objects, SCREAMING_SNAKE_CASE discouraged — use final val MaxConnections (PascalCase) per the official style guide. Test names use backticks: def \returns 200 on success`(): Unit`.
  • Formatter / linter: Scalafmt (https://scalameta.org/scalafmt/) is the standard formatter; Scalafix for refactoring + linting (custom rules + dotty migration rules). Compiler flags: -Wunused, -Wvalue-discard, -Werror. Wartremover for additional lint rules. Official guide at https://docs.scala-lang.org/style/.
  • Idiomatic patterns: prefer val over var, immutable collections over mutable, Option over null, exhaustive pattern match (sealed traits + -Wnonexhaustive-match), small ADTs, type-class-based polymorphism over inheritance, effect types over raw Future for production code, for comprehensions for monadic chaining. Tagless final (parametric in F[_]) for testable effectful code in Cats; ZIO services + layers as the equivalent in ZIO-land.
  • Expert review focus: implicit/given resolution conflicts, accidental Future lifecycle leaks, blocking calls inside IO/ZIO without IO.blocking, null returned from Java interop without wrapping in Option, partial functions on incomplete pattern matches, accidental Any widening from inheritance, Scala 2 → 3 migration foot-guns (implicits, kind projector, : _**).

Ecosystem

  • Web / API: http4s (functional, Cats Effect), ZIO HTTP, Pekko HTTP (Akka HTTP fork), Tapir (endpoint description DSL — backend for any of the above), Play Framework (full-stack, MVC).
  • Big data / streaming: Apache Spark (Scala-native; the reason most enterprises encounter Scala), Flink (Java + Scala API), Kafka Streams, Beam (Scio for Scala).
  • DB: Doobie (FP JDBC), Slick (FRM), Quill (compile-time query gen), Skunk (Postgres, FP), ScalikeJDBC.
  • Effect systems / FP: Cats (type-class library), Cats Effect 3 (runtime), ZIO 2, Monix, Scalaz (older, less active).
  • Testing: MUnit (Scalameta-backed, fast, replaces ScalaTest for many), ScalaTest (Spec/FunSuite styles), Specs2, ScalaCheck (property-based — original of QuickCheck for JVM), weaver-test (Cats Effect-native).
  • Docs: scaladoc is bundled (sbt doc); Scala 3’s scaladoc rewrite is markdown-friendly. Hosting at https://javadoc.io for libraries.
  • Notable users: Twitter (heavy use historically; migrated some services to Scala 3), Netflix (Spark, Atlas), LinkedIn (Kafka, Samza), Apple (Spark), Disney+, Stripe (Spark + Akka), Spotify (Scio for Beam), Databricks (Spark + commercial Scala 3 push), Goldman Sachs.

Gotchas

  • Scala 2 vs 3: the language is mostly compatible source-level, but implicits → givens, macro APIs, and some collection methods differ. Use scalafix migration rules. Targeting Scala 3.3 LTS for libraries is the safe call.
  • Implicit resolution in Scala 2 can be opaque — error messages improved in 3 but still hard. scalac -Vimplicits / -Vtype-diffs for debugging.
  • Variance footguns: List[+A] is covariant; mutating containers (Array[T], mutable.ListBuffer) must be invariant. Mistakes lead to ClassCastException at runtime.
  • Future is eagerFuture { sideEffect() } runs immediately on the implicit ExecutionContext. IO/Task are referentially transparent; no surprise execution.
  • Option.get and head on empty collections throw — use getOrElse, headOption, find.
  • Type inference + recursion: the compiler can’t always infer a recursive method’s return type; provide it explicitly.
  • == is value equality (calls equals); eq/ne is reference identity. Java interop: prefer Objects.equals.
  • Pattern match on type parameters is unchecked (erasure) — case xs: List[String] warns. Use ClassTag or pattern-match the values instead.
  • for comprehensions desugar based on what methods exist (map, flatMap, withFilter, foreach); type errors deep inside can be cryptic.
  • sbt has its own DSL inside build.sbttaskKey, :=, <<= (deprecated). Plugins live in project/plugins.sbt. Steep learning curve.
  • Compile times are notoriously slow vs Java/Kotlin; Scala 3 is faster than 2 in many cases. Use bloop for incremental + parallel compile.
  • Akka licensing: since Sept 2022 commercial Akka requires BSL/license fees over a revenue threshold. Pekko (Apache fork) is the OSS path forward.
  • Tagless final / effect tower debates — pick a stack (Cats Effect or ZIO) per project; mixing is painful.

Citations