{"kind":"AgentDefinition","metadata":{"namespace":"community","name":"scala2","version":"0.1.0"},"spec":{"agents_md":"---\ndescription: 'Scala 2.12/2.13 programming language coding conventions and best practices following Databricks style guide for functional programming, type safety, and production code quality.'\napplyTo: '**/*.scala, **/build.sbt, **/build.sc'\n---\n\n# Scala Best Practices\n\nBased on the [Databricks Scala Style Guide](https://github.com/databricks/scala-style-guide)\n\n## Core Principles\n\n### Write Simple Code\nCode is written once but read and modified multiple times. Optimize for long-term readability and maintainability by writing simple code.\n\n### Immutability by Default\n- Always prefer `val` over `var`\n- Use immutable collections from `scala.collection.immutable`\n- Case class constructor parameters should NOT be mutable\n- Use copy constructor to create modified instances\n\n```scala\n// Good - Immutable case class\ncase class Person(name: String, age: Int)\n\n// Bad - Mutable case class\ncase class Person(name: String, var age: Int)\n\n// To change values, use copy constructor\nval p1 = Person(\"Peter\", 15)\nval p2 = p1.copy(age = 16)\n\n// Good - Immutable collections\nval users = List(User(\"Alice\", 30), User(\"Bob\", 25))\nval updatedUsers = users.map(u =\u003e u.copy(age = u.age + 1))\n```\n\n### Pure Functions\n- Functions should be deterministic and side-effect free\n- Separate pure logic from effects\n- Use explicit types for methods with effects\n\n```scala\n// Good - Pure function\ndef calculateTotal(items: List[Item]): BigDecimal =\n  items.map(_.price).sum\n\n// Bad - Impure function with side effects\ndef calculateTotal(items: List[Item]): BigDecimal = {\n  println(s\"Calculating total for ${items.size} items\")  // Side effect\n  val total = items.map(_.price).sum\n  saveToDatabase(total)  // Side effect\n  total\n}\n```\n\n## Naming Conventions\n\n### Classes and Objects\n\n```scala\n// Classes, traits, objects - PascalCase\nclass ClusterManager\ntrait Expression\nobject Configuration\n\n// Packages - all lowercase ASCII\npackage com.databricks.resourcemanager\n\n// Methods/functions - camelCase\ndef getUserById(id: Long): Option[User]\ndef processData(input: String): Result\n\n// Constants - uppercase in companion object\nobject Configuration {\n  val DEFAULT_PORT = 10000\n  val MAX_RETRIES = 3\n  val TIMEOUT_MS = 5000L\n}\n```\n\n### Variables and Parameters\n\n```scala\n// Variables - camelCase, self-evident names\nval serverPort = 1000\nval clientPort = 2000\nval maxRetryAttempts = 3\n\n// One-character names OK in small, localized scope\nfor (i \u003c- 0 until 10) {\n  // ...\n}\n\n// Do NOT use \"l\" (Larry) - looks like \"1\", \"|\", \"I\"\n```\n\n### Enumerations\n\n```scala\n// Enumeration object - PascalCase\n// Values - UPPER_CASE with underscores\nprivate object ParseState extends Enumeration {\n  type ParseState = Value\n\n  val PREFIX,\n      TRIM_BEFORE_SIGN,\n      SIGN,\n      VALUE,\n      UNIT_BEGIN,\n      UNIT_END = Value\n}\n```\n\n## Syntactic Style\n\n### Line Length and Spacing\n\n```scala\n// Limit lines to 100 characters\n// One space before and after operators\ndef add(int1: Int, int2: Int): Int = int1 + int2\n\n// One space after commas\nval list = List(\"a\", \"b\", \"c\")\n\n// One space after colons\ndef getConf(key: String, defaultValue: String): String = {\n  // code\n}\n\n// Use 2-space indentation\nif (true) {\n  println(\"Wow!\")\n}\n\n// 4-space indentation for long parameter lists\ndef newAPIHadoopFile[K, V, F \u003c: NewInputFormat[K, V]](\n    path: String,\n    fClass: Class[F],\n    kClass: Class[K],\n    vClass: Class[V],\n    conf: Configuration = hadoopConfiguration): RDD[(K, V)] = {\n  // method body\n}\n\n// Class with long parameters\nclass Foo(\n    val param1: String,  // 4 space indent\n    val param2: String,\n    val param3: Array[Byte])\n  extends FooInterface  // 2 space indent\n  with Logging {\n\n  def firstMethod(): Unit = { ... }  // blank line above\n}\n```\n\n### Rule of 30\n\n- A method should contain less than 30 lines of code\n- A class should contain less than 30 methods\n\n### Curly Braces\n\n```scala\n// Always use curly braces for multi-line blocks\nif (true) {\n  println(\"Wow!\")\n}\n\n// Exception: one-line ternary (side-effect free)\nval result = if (condition) value1 else value2\n\n// Always use braces for try-catch\ntry {\n  foo()\n} catch {\n  case e: Exception =\u003e handle(e)\n}\n```\n\n### Long Literals\n\n```scala\n// Use uppercase L for long literals\nval longValue = 5432L  // Do this\nval badValue = 5432l   // Don't do this - hard to see\n```\n\n### Parentheses\n\n```scala\n// Methods with side-effects - use parentheses\nclass Job {\n  def killJob(): Unit = { ... }  // Correct - changes state\n  def getStatus: JobStatus = { ... }  // Correct - no side-effect\n}\n\n// Callsite should match declaration\nnew Job().killJob()  // Correct\nnew Job().getStatus  // Correct\n```\n\n### Imports\n\n```scala\n// Avoid wildcard imports unless importing 6+ entities\nimport scala.collection.mutable.{Map, HashMap, ArrayBuffer}\n\n// OK to use wildcard for implicits or 6+ items\nimport scala.collection.JavaConverters._\nimport java.util.{Map, HashMap, List, ArrayList, Set, HashSet}\n\n// Always use absolute paths\nimport scala.util.Random  // Good\n// import util.Random     // Don't use relative\n\n// Import order (with blank lines):\nimport java.io.File\nimport javax.servlet.http.HttpServlet\n\nimport scala.collection.mutable.HashMap\nimport scala.util.Random\n\nimport org.apache.spark.SparkContext\nimport org.apache.spark.rdd.RDD\n\nimport com.databricks.MyClass\n```\n\n### Pattern Matching\n\n```scala\n// Put match on same line if method is entirely pattern match\ndef test(msg: Message): Unit = msg match {\n  case TextMessage(text) =\u003e handleText(text)\n  case ImageMessage(url) =\u003e handleImage(url)\n}\n\n// Single case closures - same line\nlist.zipWithIndex.map { case (elem, i) =\u003e\n  // process\n}\n\n// Multiple cases - indent and wrap\nlist.map {\n  case a: Foo =\u003e processFoo(a)\n  case b: Bar =\u003e processBar(b)\n  case _ =\u003e handleDefault()\n}\n\n// Match on type only - don't expand all args\ncase class Pokemon(name: String, weight: Int, hp: Int, attack: Int, defense: Int)\n\n// Bad - brittle when fields change\ntargets.foreach {\n  case Pokemon(_, _, hp, _, defense) =\u003e\n    // error prone\n}\n\n// Good - match on type\ntargets.foreach {\n  case p: Pokemon =\u003e\n    val loss = math.min(0, myAttack - p.defense)\n    p.copy(hp = p.hp - loss)\n}\n```\n\n### Anonymous Functions\n\n```scala\n// Avoid excessive parentheses\n// Correct\nlist.map { item =\u003e\n  transform(item)\n}\n\n// Correct\nlist.map(item =\u003e transform(item))\n\n// Wrong - unnecessary braces\nlist.map(item =\u003e {\n  transform(item)\n})\n\n// Wrong - excessive nesting\nlist.map({ item =\u003e ... })\n```\n\n### Infix Methods\n\n```scala\n// Avoid infix for non-symbolic methods\nlist.map(func)  // Correct\nlist map func   // Wrong\n\n// OK for operators\narrayBuffer += elem\n```\n\n## Language Features\n\n### Avoid apply() on Classes\n\n```scala\n// Avoid apply on classes - hard to trace\nclass TreeNode {\n  def apply(name: String): TreeNode = { ... }  // Don't do this\n}\n\n// OK on companion objects as factory\nobject TreeNode {\n  def apply(name: String): TreeNode = new TreeNode(name)  // OK\n}\n```\n\n### override Modifier\n\n```scala\n// Always use override - even for abstract methods\ntrait Parent {\n  def hello(data: Map[String, String]): Unit\n}\n\nclass Child extends Parent {\n  // Without override, this might not actually override!\n  override def hello(data: Map[String, String]): Unit = {\n    println(data)\n  }\n}\n```\n\n### Avoid Destructuring in Constructors\n\n```scala\n// Don't use destructuring binds in constructors\nclass MyClass {\n  // Bad - creates non-transient Tuple2\n  @transient private val (a, b) = someFuncThatReturnsTuple2()\n\n  // Good\n  @transient private val tuple = someFuncThatReturnsTuple2()\n  @transient private val a = tuple._1\n  @transient private val b = tuple._2\n}\n```\n\n### Avoid Call-by-Name\n\n```scala\n// Avoid call-by-name parameters\n// Bad - caller can't tell if executed once or many times\ndef print(value: =\u003e Int): Unit = {\n  println(value)\n  println(value + 1)\n}\n\n// Good - explicit function type\ndef print(value: () =\u003e Int): Unit = {\n  println(value())\n  println(value() + 1)\n}\n```\n\n### Avoid Multiple Parameter Lists\n\n```scala\n// Avoid multiple parameter lists (except for implicits)\n// Bad\ncase class Person(name: String, age: Int)(secret: String)\n\n// Good\ncase class Person(name: String, age: Int, secret: String)\n\n// Exception: separate list for implicits (but avoid implicits!)\ndef foo(x: Int)(implicit ec: ExecutionContext): Future[Int]\n```\n\n### Symbolic Methods\n\n```scala\n// Only use for arithmetic operators\nclass Vector {\n  def +(other: Vector): Vector = { ... }  // OK\n  def -(other: Vector): Vector = { ... }  // OK\n}\n\n// Don't use for other methods\n// Bad\nchannel ! msg\nstream1 \u003e\u003e= stream2\n\n// Good\nchannel.send(msg)\nstream1.join(stream2)\n```\n\n### Type Inference\n\n```scala\n// Always type public methods\ndef getUserById(id: Long): Option[User] = { ... }\n\n// Always type implicit methods\nimplicit def stringToInt(s: String): Int = s.toInt\n\n// Type variables when not obvious (3 second rule)\nval user: User = complexComputation()\n\n// OK to omit when obvious\nval count = 5\nval name = \"Alice\"\n```\n\n### Return Statements\n\n```scala\n// Avoid return in closures - uses exceptions under the hood\ndef receive(rpc: WebSocketRPC): Option[Response] = {\n  tableFut.onComplete { table =\u003e\n    if (table.isFailure) {\n      return None  // Don't do this - wrong thread!\n    }\n  }\n}\n\n// Use return as guard to simplify control flow\ndef doSomething(obj: Any): Any = {\n  if (obj eq null) {\n    return null\n  }\n  // do something\n}\n\n// Use return to break loops early\nwhile (true) {\n  if (cond) {\n    return\n  }\n}\n```\n\n### Recursion and Tail Recursion\n\n```scala\n// Avoid recursion unless naturally recursive (trees, graphs)\n// Use @tailrec for tail-recursive methods\n@scala.annotation.tailrec\ndef max0(data: Array[Int], pos: Int, max: Int): Int = {\n  if (pos == data.length) {\n    max\n  } else {\n    max0(data, pos + 1, if (data(pos) \u003e max) data(pos) else max)\n  }\n}\n\n// Prefer explicit loops for clarity\ndef max(data: Array[Int]): Int = {\n  var max = Int.MinValue\n  for (v \u003c- data) {\n    if (v \u003e max) {\n      max = v\n    }\n  }\n  max\n}\n```\n\n### Implicits\n\n```scala\n// Avoid implicits unless:\n// 1. Building a DSL\n// 2. Implicit type parameters (ClassTag, TypeTag)\n// 3. Private type conversions within your class\n\n// If you must use them, don't overload\nobject ImplicitHolder {\n  // Bad - can't selectively import\n  def toRdd(seq: Seq[Int]): RDD[Int] = { ... }\n  def toRdd(seq: Seq[Long]): RDD[Long] = { ... }\n}\n\n// Good - distinct names\nobject ImplicitHolder {\n  def intSeqToRdd(seq: Seq[Int]): RDD[Int] = { ... }\n  def longSeqToRdd(seq: Seq[Long]): RDD[Long] = { ... }\n}\n```\n\n## Type Safety\n\n### Algebraic Data Types\n\n```scala\n// Sum types - sealed traits with case classes\nsealed trait PaymentMethod\ncase class CreditCard(number: String, cvv: String) extends PaymentMethod\ncase class PayPal(email: String) extends PaymentMethod\ncase class BankTransfer(account: String, routing: String) extends PaymentMethod\n\ndef processPayment(payment: PaymentMethod): Either[Error, Receipt] = payment match {\n  case CreditCard(number, cvv) =\u003e chargeCreditCard(number, cvv)\n  case PayPal(email) =\u003e chargePayPal(email)\n  case BankTransfer(account, routing) =\u003e chargeBankAccount(account, routing)\n}\n\n// Product types - case classes\ncase class User(id: Long, name: String, email: String, age: Int)\ncase class Order(id: Long, userId: Long, items: List[Item], total: BigDecimal)\n```\n\n### Option over null\n\n```scala\n// Use Option instead of null\ndef findUserById(id: Long): Option[User] = {\n  database.query(id)\n}\n\n// Use Option() to guard against nulls\ndef myMethod1(input: String): Option[String] = Option(transform(input))\n\n// Don't use Some() - it won't protect against null\ndef myMethod2(input: String): Option[String] = Some(transform(input)) // Bad\n\n// Pattern matching on Option\ndef processUser(id: Long): String = findUserById(id) match {\n  case Some(user) =\u003e s\"Found: ${user.name}\"\n  case None =\u003e \"User not found\"\n}\n\n// Don't call get() unless absolutely sure\nval user = findUserById(123).get  // Dangerous!\n\n// Use getOrElse, map, flatMap, fold instead\nval name = findUserById(123).map(_.name).getOrElse(\"Unknown\")\n```\n\n### Error Handling with Either\n\n```scala\nsealed trait ValidationError\ncase class InvalidEmail(email: String) extends ValidationError\ncase class InvalidAge(age: Int) extends ValidationError\ncase class MissingField(field: String) extends ValidationError\n\ndef validateUser(data: Map[String, String]): Either[ValidationError, User] = {\n  for {\n    name \u003c- data.get(\"name\").toRight(MissingField(\"name\"))\n    email \u003c- data.get(\"email\").toRight(MissingField(\"email\"))\n    validEmail \u003c- validateEmail(email)\n    ageStr \u003c- data.get(\"age\").toRight(MissingField(\"age\"))\n    age \u003c- ageStr.toIntOption.toRight(InvalidAge(-1))\n  } yield User(name, validEmail, age)\n}\n```\n\n### Try vs Exceptions\n\n```scala\n// Don't return Try from APIs\n// Bad\ndef getUser(id: Long): Try[User]\n\n// Good - explicit throws\n@throws(classOf[DatabaseConnectionException])\ndef getUser(id: Long): Option[User]\n\n// Use NonFatal for catching exceptions\nimport scala.util.control.NonFatal\n\ntry {\n  dangerousOperation()\n} catch {\n  case NonFatal(e) =\u003e\n    logger.error(\"Operation failed\", e)\n  case e: InterruptedException =\u003e\n    // handle interruption\n}\n```\n\n## Collections\n\n### Prefer Immutable Collections\n\n```scala\nimport scala.collection.immutable._\n\n// Good\nval numbers = List(1, 2, 3, 4, 5)\nval doubled = numbers.map(_ * 2)\nval evens = numbers.filter(_ % 2 == 0)\n\nval userMap = Map(\n  1L -\u003e \"Alice\",\n  2L -\u003e \"Bob\"\n)\nval updated = userMap + (3L -\u003e \"Charlie\")\n\n// Use Stream (Scala 2.12) or LazyList (Scala 2.13) for lazy sequences\nval fibonacci: LazyList[BigInt] =\n  BigInt(0) #:: BigInt(1) #:: fibonacci.zip(fibonacci.tail).map { case (a, b) =\u003e a + b }\n\nval first10 = fibonacci.take(10).toList\n```\n\n### Monadic Chaining\n\n```scala\n// Avoid chaining more than 3 operations\n// Break after flatMap\n// Don't chain with if-else blocks\n\n// Bad - too complex\ndatabase.get(name).flatMap { elem =\u003e\n  elem.data.get(\"address\").flatMap(Option.apply)\n}\n\n// Good - more readable\ndef getAddress(name: String): Option[String] = {\n  if (!database.contains(name)) {\n    return None\n  }\n\n  database(name).data.get(\"address\") match {\n    case Some(null) =\u003e None\n    case Some(addr) =\u003e Option(addr)\n    case None =\u003e None\n  }\n}\n\n// Don't chain with if-else\n// Bad\nif (condition) {\n  Seq(1, 2, 3)\n} else {\n  Seq(1, 2, 3)\n}.map(_ + 1)\n\n// Good\nval seq = if (condition) Seq(1, 2, 3) else Seq(4, 5, 6)\nseq.map(_ + 1)\n```\n\n## Performance\n\n### Use while Loops\n\n```scala\n// For performance-critical code, use while instead of for/map\nval arr = Array.fill(1000)(Random.nextInt())\n\n// Slow\nval newArr = arr.zipWithIndex.map { case (elem, i) =\u003e\n  if (i % 2 == 0) 0 else elem\n}\n\n// Fast\nval newArr = new Array[Int](arr.length)\nvar i = 0\nwhile (i \u003c arr.length) {\n  newArr(i) = if (i % 2 == 0) 0 else arr(i)\n  i += 1\n}\n```\n\n### Option vs null\n\n```scala\n// For performance-critical code, prefer null over Option\nclass Foo {\n  @javax.annotation.Nullable\n  private[this] var nullableField: Bar = _\n}\n```\n\n### Use private[this]\n\n```scala\n// private[this] generates fields, not accessor methods\nclass MyClass {\n  private val field1 = ...        // Might use accessor\n  private[this] val field2 = ...  // Direct field access\n\n  def perfSensitiveMethod(): Unit = {\n    var i = 0\n    while (i \u003c 1000000) {\n      field2  // Guaranteed field access\n      i += 1\n    }\n  }\n}\n```\n\n### Java Collections\n\n```scala\n// For performance, prefer Java collections\nimport java.util.{ArrayList, HashMap}\n\nval list = new ArrayList[String]()\nval map = new HashMap[String, Int]()\n```\n\n## Concurrency\n\n### Prefer ConcurrentHashMap\n\n```scala\n// Use java.util.concurrent.ConcurrentHashMap\nprivate[this] val map = new java.util.concurrent.ConcurrentHashMap[String, String]\n\n// Or synchronized map for low contention\nprivate[this] val map = java.util.Collections.synchronizedMap(\n  new java.util.HashMap[String, String]\n)\n```\n\n### Explicit Synchronization\n\n```scala\nclass Manager {\n  private[this] var count = 0\n  private[this] val map = new java.util.HashMap[String, String]\n\n  def update(key: String, value: String): Unit = synchronized {\n    map.put(key, value)\n    count += 1\n  }\n\n  def getCount: Int = synchronized { count }\n}\n```\n\n### Atomic Variables\n\n```scala\nimport java.util.concurrent.atomic._\n\n// Prefer Atomic over @volatile\nval initialized = new AtomicBoolean(false)\n\n// Clearly express only-once execution\nif (!initialized.getAndSet(true)) {\n  initialize()\n}\n```\n\n## Testing\n\n### Intercept Specific Exceptions\n\n```scala\nimport org.scalatest._\n\n// Bad - too broad\nintercept[Exception] {\n  thingThatThrows()\n}\n\n// Good - specific type\nintercept[IllegalArgumentException] {\n  thingThatThrows()\n}\n```\n\n## SBT Configuration\n\n```scala\n// build.sbt\nThisBuild / version := \"0.1.0-SNAPSHOT\"\nThisBuild / scalaVersion := \"2.13.12\"\nThisBuild / organization := \"com.example\"\n\nlazy val root = (project in file(\".\"))\n  .settings(\n    name := \"my-application\",\n\n    libraryDependencies ++= Seq(\n      \"org.typelevel\" %% \"cats-core\" % \"2.10.0\",\n      \"org.typelevel\" %% \"cats-effect\" % \"3.5.2\",\n\n      // Testing\n      \"org.scalatest\" %% \"scalatest\" % \"3.2.17\" % Test,\n      \"org.scalatestplus\" %% \"scalacheck-1-17\" % \"3.2.17.0\" % Test\n    ),\n\n    scalacOptions ++= Seq(\n      \"-encoding\", \"UTF-8\",\n      \"-feature\",\n      \"-unchecked\",\n      \"-deprecation\",\n      \"-Xfatal-warnings\"\n    )\n  )\n```\n\n## Miscellaneous\n\n### Use nanoTime\n\n```scala\n// Use nanoTime for durations, not currentTimeMillis\nval start = System.nanoTime()\ndoWork()\nval elapsed = System.nanoTime() - start\n\nimport java.util.concurrent.TimeUnit\nval elapsedMs = TimeUnit.NANOSECONDS.toMillis(elapsed)\n```\n\n### URI over URL\n\n```scala\n// Use URI instead of URL (URL.equals does DNS lookup!)\nval uri = new java.net.URI(\"http://example.com\")\n// Not: val url = new java.net.URL(\"http://example.com\")\n```\n\n## Summary\n\n1. **Write simple code** - Optimize for readability and maintainability\n2. **Use immutable data** - val, immutable collections, case classes\n3. **Avoid language features** - Limit implicits, avoid symbolic methods\n4. **Type public APIs** - Explicit types for methods and fields\n5. **Prefer explicit over implicit** - Clear is better than concise\n6. **Use standard libraries** - Don't reinvent the wheel\n7. **Follow naming conventions** - PascalCase, camelCase, UPPER_CASE\n8. **Keep methods small** - Rule of 30\n9. **Handle errors explicitly** - Option, Either, exceptions with @throws\n10. **Profile before optimizing** - Measure, don't guess\n\nFor complete details, see the [Databricks Scala Style Guide](https://github.com/databricks/scala-style-guide).\n","description":"Scala 2.12/2.13 programming language coding conventions and best practices following Databricks style guide for functional programming, type safety, and production code quality.","import":{"commit_sha":"541b7819d8c3545c6df122491af4fa1eae415779","imported_at":"2026-05-18T20:05:35Z","license_text":"MIT License\n\nCopyright GitHub, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.","owner":"github","repo":"github/awesome-copilot","source_url":"https://github.com/github/awesome-copilot/blob/541b7819d8c3545c6df122491af4fa1eae415779/instructions/scala2.instructions.md"},"manifest":{}},"content_hash":[48,6,40,51,190,40,154,64,232,99,209,99,76,11,108,105,92,193,132,122,215,120,169,169,228,176,84,61,67,92,226,111],"trust_level":"unsigned","yanked":false}
