📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
---
|
||||
name: scala
|
||||
description: "Language-specific super-code guidelines for scala."
|
||||
risk: safe
|
||||
source: community
|
||||
date_added: "2026-06-16"
|
||||
---
|
||||
# Scala: Idiomatic Efficiency Reference
|
||||
|
||||
## Table of Contents
|
||||
1. [Collections & Functional Transforms](#collections)
|
||||
2. [Pattern Matching](#patterns)
|
||||
3. [Case Classes & ADTs](#case-classes)
|
||||
4. [Option & Error Handling](#option)
|
||||
5. [Implicits & Given/Using](#implicits)
|
||||
6. [Concurrency](#concurrency)
|
||||
7. [Anti-patterns specific to Scala](#antipatterns)
|
||||
|
||||
---
|
||||
|
||||
## 1. Collections & Functional Transforms {#collections}
|
||||
|
||||
```scala
|
||||
// ❌ Imperative accumulation
|
||||
val result = new ArrayBuffer[String]()
|
||||
for (item <- items) {
|
||||
if (item.isActive) result += item.name.toUpperCase
|
||||
}
|
||||
|
||||
// ✅
|
||||
val result = items.filter(_.isActive).map(_.name.toUpperCase)
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ Manual grouping
|
||||
val grouped = mutable.Map[String, List[Item]]()
|
||||
for (item <- items) {
|
||||
grouped(item.category) = grouped.getOrElse(item.category, Nil) :+ item
|
||||
}
|
||||
|
||||
// ✅
|
||||
val grouped = items.groupBy(_.category)
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ Manual fold when sum/product works
|
||||
var total = 0
|
||||
for (o <- orders) total += o.amount
|
||||
|
||||
// ✅
|
||||
val total = orders.map(_.amount).sum
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ Using head on potentially empty collection
|
||||
val first = items.head // throws on empty
|
||||
|
||||
// ✅
|
||||
val first = items.headOption // returns Option[T]
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ Chaining filter + head for find
|
||||
val found = items.filter(_.id == targetId).head
|
||||
|
||||
// ✅
|
||||
val found = items.find(_.id == targetId) // returns Option[T]
|
||||
```
|
||||
|
||||
**Use `view` for lazy evaluation on large collections to avoid intermediate allocations.**
|
||||
|
||||
---
|
||||
|
||||
## 2. Pattern Matching {#patterns}
|
||||
|
||||
```scala
|
||||
// ❌ if-else chain for type dispatch
|
||||
if (shape.isInstanceOf[Circle]) {
|
||||
val c = shape.asInstanceOf[Circle]
|
||||
c.radius * c.radius * Math.PI
|
||||
} else if (shape.isInstanceOf[Rect]) { ... }
|
||||
|
||||
// ✅
|
||||
shape match {
|
||||
case Circle(r) => r * r * Math.PI
|
||||
case Rect(w, h) => w * h
|
||||
}
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ Nested match with identical fallthrough
|
||||
x match {
|
||||
case 1 => "low"
|
||||
case 2 => "low"
|
||||
case 3 => "mid"
|
||||
case _ => "high"
|
||||
}
|
||||
|
||||
// ✅
|
||||
x match {
|
||||
case 1 | 2 => "low"
|
||||
case 3 => "mid"
|
||||
case _ => "high"
|
||||
}
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ Match to extract then use
|
||||
val result = opt match {
|
||||
case Some(x) => x.toString
|
||||
case None => "N/A"
|
||||
}
|
||||
|
||||
// ✅
|
||||
val result = opt.map(_.toString).getOrElse("N/A")
|
||||
// or:
|
||||
val result = opt.fold("N/A")(_.toString)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Case Classes & ADTs {#case-classes}
|
||||
|
||||
```scala
|
||||
// ❌ Regular class for data
|
||||
class User(val name: String, val age: Int) {
|
||||
override def equals(obj: Any): Boolean = ...
|
||||
override def hashCode(): Int = ...
|
||||
override def toString: String = ...
|
||||
}
|
||||
|
||||
// ✅
|
||||
case class User(name: String, age: Int)
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ Sealed trait with unrelated case objects
|
||||
sealed trait Result
|
||||
case class Success(value: Int) extends Result
|
||||
case class Failure(error: String) extends Result
|
||||
case object Unknown extends Result // what does "Unknown" mean?
|
||||
|
||||
// ✅ — each variant should carry the data it represents
|
||||
sealed trait Result[+A]
|
||||
case class Success[A] (value: A) extends Result[A]
|
||||
case class Failure(error: Throwable) extends Result[Nothing]
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ (Scala 3) Verbose enum
|
||||
sealed trait Color
|
||||
object Color {
|
||||
case object Red extends Color
|
||||
case object Green extends Color
|
||||
case object Blue extends Color
|
||||
}
|
||||
|
||||
// ✅ (Scala 3)
|
||||
enum Color { case Red, Green, Blue }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Option & Error Handling {#option}
|
||||
|
||||
```scala
|
||||
// ❌ Null checks
|
||||
val name: String = if (user != null) user.name else "Unknown"
|
||||
|
||||
// ✅
|
||||
val name = Option(user).map(_.name).getOrElse("Unknown")
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ .get on Option (defeats the purpose)
|
||||
val name = userOpt.get // throws if None
|
||||
|
||||
// ✅
|
||||
val name = userOpt.getOrElse("default")
|
||||
// or: userOpt.map(process).getOrElse(fallback)
|
||||
// or: userOpt match { case Some(u) => ... case None => ... }
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ Try with .get
|
||||
val result = Try(parse(input)).get // throws on failure
|
||||
|
||||
// ✅
|
||||
val result = Try(parse(input)) match {
|
||||
case Success(v) => v
|
||||
case Failure(e) => handleError(e)
|
||||
}
|
||||
// or: Try(parse(input)).getOrElse(default)
|
||||
// or: Try(parse(input)).toEither
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ Using exceptions for expected failures
|
||||
def findUser(id: String): User = {
|
||||
val user = db.query(id)
|
||||
if (user == null) throw new NotFoundException(id)
|
||||
user
|
||||
}
|
||||
|
||||
// ✅ — Option for absence, Either for expected errors
|
||||
def findUser(id: String): Option[User] = db.query(id)
|
||||
// or:
|
||||
def findUser(id: String): Either[AppError, User]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Implicits & Given/Using {#implicits}
|
||||
|
||||
```scala
|
||||
// ❌ (Scala 2) Implicit conversion that hides bugs
|
||||
implicit def stringToInt(s: String): Int = s.toInt
|
||||
|
||||
// ✅ — extension methods instead of implicit conversions
|
||||
extension (s: String)
|
||||
def toIntSafe: Option[Int] = s.toIntOption
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ (Scala 2) Implicit parameter with broad type
|
||||
def query(sql: String)(implicit conn: Connection): ResultSet
|
||||
|
||||
// ✅ (Scala 3)
|
||||
def query(sql: String)(using conn: Connection): ResultSet
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ Importing implicits from everywhere
|
||||
import com.lib.implicits._
|
||||
|
||||
// ✅ — import only what you need
|
||||
import com.lib.given
|
||||
// or specific: import com.lib.{given ExecutionContext}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Concurrency {#concurrency}
|
||||
|
||||
```scala
|
||||
// ❌ Thread.sleep in production code
|
||||
Thread.sleep(5000)
|
||||
|
||||
// ✅ — use scheduler / timer abstraction
|
||||
import scala.concurrent.duration._
|
||||
system.scheduler.scheduleOnce(5.seconds)(doWork())
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ Blocking inside Future
|
||||
Future {
|
||||
val result = blockingHttpCall() // starves thread pool
|
||||
process(result)
|
||||
}
|
||||
|
||||
// ✅
|
||||
Future {
|
||||
blocking { val result = blockingHttpCall() }
|
||||
// or use a dedicated blocking ExecutionContext
|
||||
}
|
||||
```
|
||||
|
||||
```scala
|
||||
// ❌ Awaiting futures in a loop
|
||||
for (f <- futures) Await.result(f, Duration.Inf)
|
||||
|
||||
// ✅
|
||||
val all = Future.sequence(futures)
|
||||
all.map(results => process(results))
|
||||
```
|
||||
|
||||
**Prefer `Future.sequence`/`Future.traverse` over manual await loops.**
|
||||
|
||||
---
|
||||
|
||||
## 7. Anti-patterns specific to Scala {#antipatterns}
|
||||
|
||||
| Anti-pattern | Preferred |
|
||||
|---|---|
|
||||
| `.get` on Option/Try | `.getOrElse` / pattern match |
|
||||
| `null` | `Option` |
|
||||
| `isInstanceOf` + `asInstanceOf` | pattern matching |
|
||||
| Implicit conversions (Scala 2) | extension methods (Scala 3) |
|
||||
| `var` for accumulation | `val` + functional transforms |
|
||||
| `return` keyword | last expression is the return value |
|
||||
| Mutable collections by default | immutable collections |
|
||||
| `Any` / `AnyRef` parameters | generics with type bounds |
|
||||
| Deeply nested `for` comprehensions | break into named values |
|
||||
| Tuple instead of case class | case class for anything with semantic meaning |
|
||||
| `Await.result` in production | compose with `map`/`flatMap` |
|
||||
|
||||
|
||||
|
||||
|
||||
## Limitations
|
||||
- These are language-specific guidelines and do not cover overall architectural decisions.
|
||||
- Over-compression might reduce readability; apply judgement.
|
||||
Reference in New Issue
Block a user