# Idiomatic Scala and Common Pitfalls — Scala

Source: https://www.geekswithgeeks.com/en/scala/s-idioms

> Write clear Scala and avoid frequent mistakes.

## Writing Scala that others can read

Idiomatic Scala favours **immutability** (`val`, immutable collections, case classes), **expressions** over statements, **ADTs** and **pattern matching** for domain modelling, **Option/Either** instead of `null` and exceptions, **small pure functions** and explicit types on public APIs. Format consistently with **scalafmt**, and use **scalafix** and compiler warnings (`-Wunused:all`, `-Werror` in CI) to catch problems early. Common pitfalls: calling `.get` on `Option` or `.head` on possibly empty lists (use `getOrElse`, `headOption` or pattern matching); overusing implicits or givens so code becomes hard to follow; writing "Haskell in Scala" or "Java in Scala" inconsistently across a team instead of agreeing on a style; forgetting that `Future` runs eagerly (and creating futures inside a for comprehension, which makes them sequential); blocking inside the default execution context; using `return` in lambdas; overly clever type-level code that slows compilation and confuses colleagues; using `List` for random access; and comparing case classes containing `Array` (arrays use reference equality). Prefer the simplest feature that solves the problem.

## Unidiomatic versus idiomatic

The same lookup written two ways.

```scala
case class Customer(id: String, email: String, tier: String)

// unidiomatic: nulls, var and .get
def findEmailBad(customers: List[Customer], id: String): String =
  var result: String = null
  for c <- customers do
    if c.id == id then result = c.email
  if result == null then throw new RuntimeException("not found")
  result.toLowerCase

// idiomatic: Option and an expression
def findEmail(customers: List[Customer], id: String): Option[String] =
  customers.find(_.id == id).map(_.email.toLowerCase)

val customers = List(Customer("c1", "ASHA@example.com", "gold"))
val greeting = findEmail(customers, "c1") match
  case Some(email) => s"Sending offer to $email"
  case None        => "No such customer"

// pitfall: .head on an empty list throws; prefer headOption
val firstGold: Option[Customer] = customers.filter(_.tier == "gold").headOption

// pitfall: arrays compare by reference inside case classes
case class Batch(ids: Array[String])
println(Batch(Array("a")) == Batch(Array("a")))      // false
case class SafeBatch(ids: Vector[String])
println(SafeBatch(Vector("a")) == SafeBatch(Vector("a")))   // true
```

## Agree on a team style

Scala supports many styles, from "better Java" to pure functional programming. Teams are most productive when they agree on one level (for example, "Option and Either, Cats Effect for I/O, no custom type-level code") and enforce it in reviews.

**Quiz:** Why can calling .get on an Option be dangerous?

- [x] It throws an exception when the Option is None
- [ ] It is slow
- [ ] It changes the Option
- [ ] It returns null

*Answer:* It throws an exception when the Option is None. Option.get on None throws NoSuchElementException; use getOrElse, fold or pattern matching.
