[{"data":1,"prerenderedAt":1652},["ShallowReactive",2],{"page-\u002Fscala\u002F01-getting-started":3},{"id":4,"title":5,"body":6,"description":1645,"extension":1646,"meta":1647,"navigation":139,"path":1648,"seo":1649,"stem":1650,"__hash__":1651},"content\u002Fscala\u002F01-getting-started.md","Scala — Toolchain, Project Layout & JVM Execution Model",{"type":7,"value":8,"toc":1633},"minimark",[9,14,24,93,161,165,321,389,393,718,776,780,1034,1096,1100,1262,1266,1336,1373,1377,1386,1425,1435,1484,1493,1497,1510,1529,1542,1571,1575,1582,1629],[10,11,13],"h1",{"id":12},"_01-toolchain-project-layout-jvm-execution-model","01 — Toolchain, Project Layout & JVM Execution Model",[15,16,18,19,23],"h2",{"id":17},"compiler-pipeline-scala-jvm-bytecode","Compiler Pipeline: ",[20,21,22],"code",{},".scala"," → JVM Bytecode",[25,26,31],"pre",{"className":27,"code":28,"language":29,"meta":30,"style":30},"language-scala shiki shiki-themes github-light github-dark","\u002F\u002F scalac pipeline (what actually happens):\n\u002F\u002F   .scala → [parser] → AST → [typer] → typed AST → [uncurry] → [lazyVals]\n\u002F\u002F          → [cleanup] → [jvm] → .class bytecode\n\u002F\u002F\n\u002F\u002F Each phase is observable:\n\u002F\u002F   scalac -Xshow-class Simple.scala       \u002F\u002F show class output\n\u002F\u002F   scalac -Xprint:typer Simple.scala      \u002F\u002F dump typed AST after typer phase\n\u002F\u002F   scalac -Xprint:delambdafy Simple.scala \u002F\u002F see how lambdas become classes\n","scala","",[20,32,33,42,48,54,60,66,75,84],{"__ignoreMap":30},[34,35,38],"span",{"class":36,"line":37},"line",1,[34,39,41],{"class":40},"sdCPZ","\u002F\u002F scalac pipeline (what actually happens):\n",[34,43,45],{"class":36,"line":44},2,[34,46,47],{"class":40},"\u002F\u002F   .scala → [parser] → AST → [typer] → typed AST → [uncurry] → [lazyVals]\n",[34,49,51],{"class":36,"line":50},3,[34,52,53],{"class":40},"\u002F\u002F          → [cleanup] → [jvm] → .class bytecode\n",[34,55,57],{"class":36,"line":56},4,[34,58,59],{"class":40},"\u002F\u002F\n",[34,61,63],{"class":36,"line":62},5,[34,64,65],{"class":40},"\u002F\u002F Each phase is observable:\n",[34,67,69,72],{"class":36,"line":68},6,[34,70,71],{"class":40},"\u002F\u002F   scalac -Xshow-class Simple.scala",[34,73,74],{"class":40},"       \u002F\u002F show class output\n",[34,76,78,81],{"class":36,"line":77},7,[34,79,80],{"class":40},"\u002F\u002F   scalac -Xprint:typer Simple.scala",[34,82,83],{"class":40},"      \u002F\u002F dump typed AST after typer phase\n",[34,85,87,90],{"class":36,"line":86},8,[34,88,89],{"class":40},"\u002F\u002F   scalac -Xprint:delambdafy Simple.scala",[34,91,92],{"class":40}," \u002F\u002F see how lambdas become classes\n",[94,95,97],"code-wrapper",{"language":96},"bash",[25,98,101],{"className":99,"code":100,"language":96,"meta":30,"style":30},"language-bash shiki shiki-themes github-light github-dark","# Inspect what scalac emits — object becomes a final class with MODULE$ singleton\nscalac -Xprint:jvm Hello.scala 2>&1 | head -40\n\n# Decompilation with javap reveals the JVM-level representation:\n#   object Config  →  public final class Config$  with static MODULE$ field\n#   def greet      →  public String greet(String)\n#   val timeout    →  private final int timeout; public int timeout()\n",[20,102,103,108,135,141,146,151,156],{"__ignoreMap":30},[34,104,105],{"class":36,"line":37},[34,106,107],{"class":40},"# Inspect what scalac emits — object becomes a final class with MODULE$ singleton\n",[34,109,110,114,118,122,126,129,132],{"class":36,"line":44},[34,111,113],{"class":112},"sIsaT","scalac",[34,115,117],{"class":116},"snvgF"," -Xprint:jvm",[34,119,121],{"class":120},"sJ6F3"," Hello.scala",[34,123,125],{"class":124},"svdQ7"," 2>&1",[34,127,128],{"class":124}," |",[34,130,131],{"class":112}," head",[34,133,134],{"class":116}," -40\n",[34,136,137],{"class":36,"line":50},[34,138,140],{"emptyLinePlaceholder":139},true,"\n",[34,142,143],{"class":36,"line":56},[34,144,145],{"class":40},"# Decompilation with javap reveals the JVM-level representation:\n",[34,147,148],{"class":36,"line":62},[34,149,150],{"class":40},"#   object Config  →  public final class Config$  with static MODULE$ field\n",[34,152,153],{"class":36,"line":68},[34,154,155],{"class":40},"#   def greet      →  public String greet(String)\n",[34,157,158],{"class":36,"line":77},[34,159,160],{"class":40},"#   val timeout    →  private final int timeout; public int timeout()\n",[15,162,164],{"id":163},"scala-cli-single-file-scripting","scala-cli — Single-File & Scripting",[94,166,167],{"language":29},[25,168,170],{"className":27,"code":169,"language":29,"meta":30,"style":30},"\u002F\u002F> using scala 3.3.3\n\u002F\u002F> using dep com.typesafe::config:1.4.3\n\nimport com.typesafe.config.ConfigFactory\n\n@main def run(path: String = \"application.conf\"): Unit =\n  val cfg = ConfigFactory.load(path)\n  println(cfg.getString(\"app.name\"))\n  \u002F\u002F scala-cli uses 'using' directives at file top — pinned Scala version,\n  \u002F\u002F deps, and JVM version are resolved via Coursier under the hood.\n  \u002F\u002F No build.sbt needed; ideal for CLIs, scripts, prototyping.\n",[20,171,172,186,198,202,227,231,276,292,303,309,315],{"__ignoreMap":30},[34,173,174,177,180,183],{"class":36,"line":37},[34,175,176],{"class":40},"\u002F\u002F> ",[34,178,179],{"class":124},"using",[34,181,182],{"class":112}," scala",[34,184,185],{"class":116}," 3.3.3\n",[34,187,188,190,192,195],{"class":36,"line":44},[34,189,176],{"class":40},[34,191,179],{"class":124},[34,193,194],{"class":112}," dep",[34,196,197],{"class":120}," com.typesafe::config:1.4.3\n",[34,199,200],{"class":36,"line":50},[34,201,140],{"emptyLinePlaceholder":139},[34,203,204,207,210,214,217,219,222,224],{"class":36,"line":56},[34,205,206],{"class":124},"import",[34,208,209],{"class":112}," com",[34,211,213],{"class":212},"ssxIu",".",[34,215,216],{"class":112},"typesafe",[34,218,213],{"class":212},[34,220,221],{"class":112},"config",[34,223,213],{"class":212},[34,225,226],{"class":112},"ConfigFactory\n",[34,228,229],{"class":36,"line":62},[34,230,140],{"emptyLinePlaceholder":139},[34,232,233,236,239,242,245,248,252,255,258,261,264,267,270,273],{"class":36,"line":68},[34,234,235],{"class":124},"@",[34,237,238],{"class":212},"main ",[34,240,241],{"class":124},"def",[34,243,244],{"class":112}," run",[34,246,247],{"class":212},"(",[34,249,251],{"class":250},"sCrzJ","path",[34,253,254],{"class":212},": ",[34,256,257],{"class":112},"String",[34,259,260],{"class":124}," =",[34,262,263],{"class":120}," \"application.conf\"",[34,265,266],{"class":212},")",[34,268,269],{"class":124},":",[34,271,272],{"class":112}," Unit",[34,274,275],{"class":124}," =\n",[34,277,278,281,284,286,289],{"class":36,"line":77},[34,279,280],{"class":124},"  val",[34,282,283],{"class":250}," cfg",[34,285,260],{"class":124},[34,287,288],{"class":112}," ConfigFactory",[34,290,291],{"class":212},".load(path)\n",[34,293,294,297,300],{"class":36,"line":86},[34,295,296],{"class":212},"  println(cfg.getString(",[34,298,299],{"class":120},"\"app.name\"",[34,301,302],{"class":212},"))\n",[34,304,306],{"class":36,"line":305},9,[34,307,308],{"class":40},"  \u002F\u002F scala-cli uses 'using' directives at file top — pinned Scala version,\n",[34,310,312],{"class":36,"line":311},10,[34,313,314],{"class":40},"  \u002F\u002F deps, and JVM version are resolved via Coursier under the hood.\n",[34,316,318],{"class":36,"line":317},11,[34,319,320],{"class":40},"  \u002F\u002F No build.sbt needed; ideal for CLIs, scripts, prototyping.\n",[94,322,323],{"language":96},[25,324,326],{"className":99,"code":325,"language":96,"meta":30,"style":30},"scala-cli run .                        # run all .scala in directory\nscala-cli run . -- --watch             # recompile on change\nscala-cli package . --executable -o app # fat JAR \u002F native-image GraalVM\nscala-cli repl                         # REPL with project deps on classpath\n",[20,327,328,341,358,379],{"__ignoreMap":30},[34,329,330,333,335,338],{"class":36,"line":37},[34,331,332],{"class":112},"scala-cli",[34,334,244],{"class":120},[34,336,337],{"class":120}," .",[34,339,340],{"class":40},"                        # run all .scala in directory\n",[34,342,343,345,347,349,352,355],{"class":36,"line":44},[34,344,332],{"class":112},[34,346,244],{"class":120},[34,348,337],{"class":120},[34,350,351],{"class":116}," --",[34,353,354],{"class":116}," --watch",[34,356,357],{"class":40},"             # recompile on change\n",[34,359,360,362,365,367,370,373,376],{"class":36,"line":50},[34,361,332],{"class":112},[34,363,364],{"class":120}," package",[34,366,337],{"class":120},[34,368,369],{"class":116}," --executable",[34,371,372],{"class":116}," -o",[34,374,375],{"class":120}," app",[34,377,378],{"class":40}," # fat JAR \u002F native-image GraalVM\n",[34,380,381,383,386],{"class":36,"line":56},[34,382,332],{"class":112},[34,384,385],{"class":120}," repl",[34,387,388],{"class":40},"                         # REPL with project deps on classpath\n",[15,390,392],{"id":391},"sbt-multi-module-build","sbt — Multi-Module Build",[94,394,395],{"language":29},[25,396,398],{"className":27,"code":397,"language":29,"meta":30,"style":30},"\u002F\u002F build.sbt — production layout with cross-building and strict settings\nThisBuild \u002F scalaVersion := \"3.3.3\"\nThisBuild \u002F organization := \"com.acme\"\nThisBuild \u002F version      := sys.env.getOrElse(\"APP_VERSION\", \"0.1.0-SNAPSHOT\")\n\n\u002F\u002F Compile-time fatal warnings, unused-symbol detection\nThisBuild \u002F scalacOptions ++= Seq(\n  \"-deprecation\", \"-feature\", \"-unchecked\",\n  \"-Wunused:all\", \"-Werror\",        \u002F\u002F fail build on warnings\n  \"-explain\",                        \u002F\u002F human-readable type error explanations\n  \"-new-syntax\", \"-indent\"           \u002F\u002F Scala 3 braces-optional syntax\n)\n\n\u002F\u002F Parallel cross-build across Scala 2.13 + 3.x for library publishing\nlazy val core = crossProject(JVMPlatform, JSPlatform)\n  .crossType(CrossType.Pure)\n  .settings(\n    libraryDependencies ++= Seq(\n      \"org.typelevel\" %% \"cats-core\" % \"2.10.0\",\n      \"org.scalameta\" %% \"munit\"     % \"1.0.0\" % Test\n    )\n  )\n\nlazy val app = project\n  .dependsOn(core.jvm)\n  .enablePlugins(JavaAppPackaging)   \u002F\u002F sbt-native-packager → Debian\u002FRPM\u002FDocker\n",[20,399,400,405,422,436,462,466,471,489,507,523,534,547,552,557,563,590,606,612,624,644,666,672,678,683,697,703],{"__ignoreMap":30},[34,401,402],{"class":36,"line":37},[34,403,404],{"class":40},"\u002F\u002F build.sbt — production layout with cross-building and strict settings\n",[34,406,407,410,413,416,419],{"class":36,"line":44},[34,408,409],{"class":112},"ThisBuild",[34,411,412],{"class":124}," \u002F",[34,414,415],{"class":212}," scalaVersion ",[34,417,418],{"class":124},":=",[34,420,421],{"class":120}," \"3.3.3\"\n",[34,423,424,426,428,431,433],{"class":36,"line":50},[34,425,409],{"class":112},[34,427,412],{"class":124},[34,429,430],{"class":212}," organization ",[34,432,418],{"class":124},[34,434,435],{"class":120}," \"com.acme\"\n",[34,437,438,440,442,445,447,450,453,456,459],{"class":36,"line":56},[34,439,409],{"class":112},[34,441,412],{"class":124},[34,443,444],{"class":212}," version      ",[34,446,418],{"class":124},[34,448,449],{"class":212}," sys.env.getOrElse(",[34,451,452],{"class":120},"\"APP_VERSION\"",[34,454,455],{"class":212},", ",[34,457,458],{"class":120},"\"0.1.0-SNAPSHOT\"",[34,460,461],{"class":212},")\n",[34,463,464],{"class":36,"line":62},[34,465,140],{"emptyLinePlaceholder":139},[34,467,468],{"class":36,"line":68},[34,469,470],{"class":40},"\u002F\u002F Compile-time fatal warnings, unused-symbol detection\n",[34,472,473,475,477,480,483,486],{"class":36,"line":77},[34,474,409],{"class":112},[34,476,412],{"class":124},[34,478,479],{"class":212}," scalacOptions ",[34,481,482],{"class":124},"++=",[34,484,485],{"class":112}," Seq",[34,487,488],{"class":212},"(\n",[34,490,491,494,496,499,501,504],{"class":36,"line":86},[34,492,493],{"class":120},"  \"-deprecation\"",[34,495,455],{"class":212},[34,497,498],{"class":120},"\"-feature\"",[34,500,455],{"class":212},[34,502,503],{"class":120},"\"-unchecked\"",[34,505,506],{"class":212},",\n",[34,508,509,512,514,517,520],{"class":36,"line":305},[34,510,511],{"class":120},"  \"-Wunused:all\"",[34,513,455],{"class":212},[34,515,516],{"class":120},"\"-Werror\"",[34,518,519],{"class":212},",        ",[34,521,522],{"class":40},"\u002F\u002F fail build on warnings\n",[34,524,525,528,531],{"class":36,"line":311},[34,526,527],{"class":120},"  \"-explain\"",[34,529,530],{"class":212},",                        ",[34,532,533],{"class":40},"\u002F\u002F human-readable type error explanations\n",[34,535,536,539,541,544],{"class":36,"line":317},[34,537,538],{"class":120},"  \"-new-syntax\"",[34,540,455],{"class":212},[34,542,543],{"class":120},"\"-indent\"",[34,545,546],{"class":40},"           \u002F\u002F Scala 3 braces-optional syntax\n",[34,548,550],{"class":36,"line":549},12,[34,551,461],{"class":212},[34,553,555],{"class":36,"line":554},13,[34,556,140],{"emptyLinePlaceholder":139},[34,558,560],{"class":36,"line":559},14,[34,561,562],{"class":40},"\u002F\u002F Parallel cross-build across Scala 2.13 + 3.x for library publishing\n",[34,564,566,569,572,575,577,580,583,585,588],{"class":36,"line":565},15,[34,567,568],{"class":124},"lazy",[34,570,571],{"class":124}," val",[34,573,574],{"class":250}," core",[34,576,260],{"class":124},[34,578,579],{"class":212}," crossProject(",[34,581,582],{"class":112},"JVMPlatform",[34,584,455],{"class":212},[34,586,587],{"class":112},"JSPlatform",[34,589,461],{"class":212},[34,591,593,596,599,601,604],{"class":36,"line":592},16,[34,594,595],{"class":212},"  .crossType(",[34,597,598],{"class":112},"CrossType",[34,600,213],{"class":212},[34,602,603],{"class":112},"Pure",[34,605,461],{"class":212},[34,607,609],{"class":36,"line":608},17,[34,610,611],{"class":212},"  .settings(\n",[34,613,615,618,620,622],{"class":36,"line":614},18,[34,616,617],{"class":212},"    libraryDependencies ",[34,619,482],{"class":124},[34,621,485],{"class":112},[34,623,488],{"class":212},[34,625,627,630,633,636,639,642],{"class":36,"line":626},19,[34,628,629],{"class":120},"      \"org.typelevel\"",[34,631,632],{"class":124}," %%",[34,634,635],{"class":120}," \"cats-core\"",[34,637,638],{"class":124}," %",[34,640,641],{"class":120}," \"2.10.0\"",[34,643,506],{"class":212},[34,645,647,650,652,655,658,661,663],{"class":36,"line":646},20,[34,648,649],{"class":120},"      \"org.scalameta\"",[34,651,632],{"class":124},[34,653,654],{"class":120}," \"munit\"",[34,656,657],{"class":124},"     %",[34,659,660],{"class":120}," \"1.0.0\"",[34,662,638],{"class":124},[34,664,665],{"class":112}," Test\n",[34,667,669],{"class":36,"line":668},21,[34,670,671],{"class":212},"    )\n",[34,673,675],{"class":36,"line":674},22,[34,676,677],{"class":212},"  )\n",[34,679,681],{"class":36,"line":680},23,[34,682,140],{"emptyLinePlaceholder":139},[34,684,686,688,690,692,694],{"class":36,"line":685},24,[34,687,568],{"class":124},[34,689,571],{"class":124},[34,691,375],{"class":250},[34,693,260],{"class":124},[34,695,696],{"class":212}," project\n",[34,698,700],{"class":36,"line":699},25,[34,701,702],{"class":212},"  .dependsOn(core.jvm)\n",[34,704,706,709,712,715],{"class":36,"line":705},26,[34,707,708],{"class":212},"  .enablePlugins(",[34,710,711],{"class":112},"JavaAppPackaging",[34,713,714],{"class":212},")   ",[34,716,717],{"class":40},"\u002F\u002F sbt-native-packager → Debian\u002FRPM\u002FDocker\n",[94,719,720],{"language":96},[25,721,723],{"className":99,"code":722,"language":96,"meta":30,"style":30},"sbt compile              # incremental compile — only recompiles changed closure\nsbt ~compile             # watch mode — recompile on save\nsbt \"coreJS\u002FfastLinkJS\"  # Scala.js fast-opt for dev\nsbt release              # cross-publish with signed artifacts (sbt-release)\nsbt dependencyTree       # visualize transitive dependency graph\n",[20,724,725,736,746,756,766],{"__ignoreMap":30},[34,726,727,730,733],{"class":36,"line":37},[34,728,729],{"class":112},"sbt",[34,731,732],{"class":120}," compile",[34,734,735],{"class":40},"              # incremental compile — only recompiles changed closure\n",[34,737,738,740,743],{"class":36,"line":44},[34,739,729],{"class":112},[34,741,742],{"class":120}," ~compile",[34,744,745],{"class":40},"             # watch mode — recompile on save\n",[34,747,748,750,753],{"class":36,"line":50},[34,749,729],{"class":112},[34,751,752],{"class":120}," \"coreJS\u002FfastLinkJS\"",[34,754,755],{"class":40},"  # Scala.js fast-opt for dev\n",[34,757,758,760,763],{"class":36,"line":56},[34,759,729],{"class":112},[34,761,762],{"class":120}," release",[34,764,765],{"class":40},"              # cross-publish with signed artifacts (sbt-release)\n",[34,767,768,770,773],{"class":36,"line":62},[34,769,729],{"class":112},[34,771,772],{"class":120}," dependencyTree",[34,774,775],{"class":40},"       # visualize transitive dependency graph\n",[15,777,779],{"id":778},"bytecode-reality-scala-constructs-on-the-jvm","Bytecode Reality: Scala Constructs on the JVM",[94,781,782],{"language":29},[25,783,785],{"className":27,"code":784,"language":29,"meta":30,"style":30},"\u002F\u002F What Scala emits at the bytecode level — critical for interop & perf:\n\nobject Config:                          \u002F\u002F → final class Config$ with static MODULE$\n  val timeout: Int = 5000               \u002F\u002F → private static int + static getter\n  def url: String = \"https:\u002F\u002Fapi.x\"     \u002F\u002F → public static String url()\n\ncase class Point(x: Int, y: Int)        \u002F\u002F → public final class Point\n  \u002F\u002F Auto-generated: apply, unapply, copy, equals, hashCode, toString, productElementNames\n  \u002F\u002F equals() uses field-by-field comparison — no reference identity\n  \u002F\u002F hashCode() uses MurmurHash3.productHash — consistent with equals\n\nenum Color(val hex: Int):               \u002F\u002F → sealed abstract class Color with nested finals\n  case Red extends Color(0xFF0000)      \u002F\u002F → public static final Color Red\n  case Blue extends Color(0x0000FF)\n\nextension (s: String)                   \u002F\u002F → static method with String as first param\n  def slug: String = s.toLowerCase.replaceAll(\"[^a-z0-9]+\", \"-\")\n  \u002F\u002F From Java: StringOps$.MODULE$.slug$extension(\"Hello World\")\n",[20,786,787,792,796,809,829,850,854,890,895,900,905,909,936,960,978,982,1003,1029],{"__ignoreMap":30},[34,788,789],{"class":36,"line":37},[34,790,791],{"class":40},"\u002F\u002F What Scala emits at the bytecode level — critical for interop & perf:\n",[34,793,794],{"class":36,"line":44},[34,795,140],{"emptyLinePlaceholder":139},[34,797,798,801,804,806],{"class":36,"line":50},[34,799,800],{"class":124},"object",[34,802,803],{"class":112}," Config",[34,805,269],{"class":124},[34,807,808],{"class":40},"                          \u002F\u002F → final class Config$ with static MODULE$\n",[34,810,811,813,816,818,821,823,826],{"class":36,"line":56},[34,812,280],{"class":124},[34,814,815],{"class":250}," timeout",[34,817,269],{"class":124},[34,819,820],{"class":112}," Int",[34,822,260],{"class":124},[34,824,825],{"class":116}," 5000",[34,827,828],{"class":40},"               \u002F\u002F → private static int + static getter\n",[34,830,831,834,837,839,842,844,847],{"class":36,"line":62},[34,832,833],{"class":124},"  def",[34,835,836],{"class":112}," url",[34,838,269],{"class":124},[34,840,841],{"class":112}," String",[34,843,260],{"class":124},[34,845,846],{"class":120}," \"https:\u002F\u002Fapi.x\"",[34,848,849],{"class":40},"     \u002F\u002F → public static String url()\n",[34,851,852],{"class":36,"line":68},[34,853,140],{"emptyLinePlaceholder":139},[34,855,856,859,862,865,867,870,872,875,877,880,882,884,887],{"class":36,"line":77},[34,857,858],{"class":124},"case",[34,860,861],{"class":124}," class",[34,863,864],{"class":112}," Point",[34,866,247],{"class":212},[34,868,869],{"class":250},"x",[34,871,254],{"class":212},[34,873,874],{"class":112},"Int",[34,876,455],{"class":212},[34,878,879],{"class":250},"y",[34,881,254],{"class":212},[34,883,874],{"class":112},[34,885,886],{"class":212},")        ",[34,888,889],{"class":40},"\u002F\u002F → public final class Point\n",[34,891,892],{"class":36,"line":86},[34,893,894],{"class":40},"  \u002F\u002F Auto-generated: apply, unapply, copy, equals, hashCode, toString, productElementNames\n",[34,896,897],{"class":36,"line":305},[34,898,899],{"class":40},"  \u002F\u002F equals() uses field-by-field comparison — no reference identity\n",[34,901,902],{"class":36,"line":311},[34,903,904],{"class":40},"  \u002F\u002F hashCode() uses MurmurHash3.productHash — consistent with equals\n",[34,906,907],{"class":36,"line":317},[34,908,140],{"emptyLinePlaceholder":139},[34,910,911,914,917,919,922,925,927,929,931,933],{"class":36,"line":549},[34,912,913],{"class":124},"enum",[34,915,916],{"class":112}," Color",[34,918,247],{"class":212},[34,920,921],{"class":124},"val",[34,923,924],{"class":250}," hex",[34,926,269],{"class":124},[34,928,820],{"class":112},[34,930,266],{"class":212},[34,932,269],{"class":124},[34,934,935],{"class":40},"               \u002F\u002F → sealed abstract class Color with nested finals\n",[34,937,938,941,944,947,949,951,954,957],{"class":36,"line":554},[34,939,940],{"class":124},"  case",[34,942,943],{"class":112}," Red",[34,945,946],{"class":124}," extends",[34,948,916],{"class":112},[34,950,247],{"class":212},[34,952,953],{"class":116},"0xFF0000",[34,955,956],{"class":212},")      ",[34,958,959],{"class":40},"\u002F\u002F → public static final Color Red\n",[34,961,962,964,967,969,971,973,976],{"class":36,"line":559},[34,963,940],{"class":124},[34,965,966],{"class":112}," Blue",[34,968,946],{"class":124},[34,970,916],{"class":112},[34,972,247],{"class":212},[34,974,975],{"class":116},"0x0000FF",[34,977,461],{"class":212},[34,979,980],{"class":36,"line":565},[34,981,140],{"emptyLinePlaceholder":139},[34,983,984,987,990,993,995,997,1000],{"class":36,"line":592},[34,985,986],{"class":124},"extension",[34,988,989],{"class":212}," (",[34,991,992],{"class":250},"s",[34,994,254],{"class":212},[34,996,257],{"class":112},[34,998,999],{"class":212},")                   ",[34,1001,1002],{"class":40},"\u002F\u002F → static method with String as first param\n",[34,1004,1005,1007,1010,1012,1014,1016,1019,1022,1024,1027],{"class":36,"line":608},[34,1006,833],{"class":124},[34,1008,1009],{"class":112}," slug",[34,1011,269],{"class":124},[34,1013,841],{"class":112},[34,1015,260],{"class":124},[34,1017,1018],{"class":212}," s.toLowerCase.replaceAll(",[34,1020,1021],{"class":120},"\"[^a-z0-9]+\"",[34,1023,455],{"class":212},[34,1025,1026],{"class":120},"\"-\"",[34,1028,461],{"class":212},[34,1030,1031],{"class":36,"line":614},[34,1032,1033],{"class":40},"  \u002F\u002F From Java: StringOps$.MODULE$.slug$extension(\"Hello World\")\n",[94,1035,1036],{"language":96},[25,1037,1039],{"className":99,"code":1038,"language":96,"meta":30,"style":30},"# Verify what Java sees when calling Scala — critical for cross-language teams\njavap -p -c target\u002Fscala-3.3.3\u002Fclasses\u002Fcom\u002Facme\u002FPoint.class | head -60\n\n# javap output shows:\n#   public int x();                    — getter (Scala 3: no () on val access)\n#   public Point copy(int, int);       — generated copy\n#   public boolean equals(java.lang.Object); — value-based equals\n#   public int hashCode();             — MurmurHash3\n",[20,1040,1041,1046,1067,1071,1076,1081,1086,1091],{"__ignoreMap":30},[34,1042,1043],{"class":36,"line":37},[34,1044,1045],{"class":40},"# Verify what Java sees when calling Scala — critical for cross-language teams\n",[34,1047,1048,1051,1054,1057,1060,1062,1064],{"class":36,"line":44},[34,1049,1050],{"class":112},"javap",[34,1052,1053],{"class":116}," -p",[34,1055,1056],{"class":116}," -c",[34,1058,1059],{"class":120}," target\u002Fscala-3.3.3\u002Fclasses\u002Fcom\u002Facme\u002FPoint.class",[34,1061,128],{"class":124},[34,1063,131],{"class":112},[34,1065,1066],{"class":116}," -60\n",[34,1068,1069],{"class":36,"line":50},[34,1070,140],{"emptyLinePlaceholder":139},[34,1072,1073],{"class":36,"line":56},[34,1074,1075],{"class":40},"# javap output shows:\n",[34,1077,1078],{"class":36,"line":62},[34,1079,1080],{"class":40},"#   public int x();                    — getter (Scala 3: no () on val access)\n",[34,1082,1083],{"class":36,"line":68},[34,1084,1085],{"class":40},"#   public Point copy(int, int);       — generated copy\n",[34,1087,1088],{"class":36,"line":77},[34,1089,1090],{"class":40},"#   public boolean equals(java.lang.Object); — value-based equals\n",[34,1092,1093],{"class":36,"line":86},[34,1094,1095],{"class":40},"#   public int hashCode();             — MurmurHash3\n",[15,1097,1099],{"id":1098},"project-layout-production-structure","Project Layout — Production Structure",[94,1101,1102],{"language":96},[25,1103,1105],{"className":99,"code":1104,"language":96,"meta":30,"style":30},"# Standard multi-module sbt project\nmy-service\u002F\n├── build.sbt\n├── project\u002F\n│   ├── build.properties               # sbt version pin: sbt.version=1.9.8\n│   └── plugins.sbt                    # sbt-native-packager, sbt-jmh, etc.\n├── modules\u002F\n│   ├── core\u002F\n│   │   └── src\u002F{main,test}\u002Fscala\u002Fcom\u002Facme\u002Fcore\u002F\n│   ├── domain\u002F\n│   │   └── src\u002Fmain\u002Fscala\u002Fcom\u002Facme\u002Fdomain\u002F   # pure ADTs, no I\u002FO deps\n│   └── infra\u002F\n│       └── src\u002Fmain\u002Fscala\u002Fcom\u002Facme\u002Finfra\u002F    # DB, HTTP clients, side-effects\n├── app\u002F\n│   └── src\u002Fmain\u002Fscala\u002Fcom\u002Facme\u002FMain.scala    # thin composition root\n└── .jvmopts                                    # -Xmx2g -XX:+UseZGC\n",[20,1106,1107,1112,1117,1125,1132,1146,1159,1166,1175,1187,1196,1210,1219,1232,1239,1251],{"__ignoreMap":30},[34,1108,1109],{"class":36,"line":37},[34,1110,1111],{"class":40},"# Standard multi-module sbt project\n",[34,1113,1114],{"class":36,"line":44},[34,1115,1116],{"class":112},"my-service\u002F\n",[34,1118,1119,1122],{"class":36,"line":50},[34,1120,1121],{"class":112},"├──",[34,1123,1124],{"class":120}," build.sbt\n",[34,1126,1127,1129],{"class":36,"line":56},[34,1128,1121],{"class":112},[34,1130,1131],{"class":120}," project\u002F\n",[34,1133,1134,1137,1140,1143],{"class":36,"line":62},[34,1135,1136],{"class":112},"│",[34,1138,1139],{"class":120},"   ├──",[34,1141,1142],{"class":120}," build.properties",[34,1144,1145],{"class":40},"               # sbt version pin: sbt.version=1.9.8\n",[34,1147,1148,1150,1153,1156],{"class":36,"line":68},[34,1149,1136],{"class":112},[34,1151,1152],{"class":120},"   └──",[34,1154,1155],{"class":120}," plugins.sbt",[34,1157,1158],{"class":40},"                    # sbt-native-packager, sbt-jmh, etc.\n",[34,1160,1161,1163],{"class":36,"line":77},[34,1162,1121],{"class":112},[34,1164,1165],{"class":120}," modules\u002F\n",[34,1167,1168,1170,1172],{"class":36,"line":86},[34,1169,1136],{"class":112},[34,1171,1139],{"class":120},[34,1173,1174],{"class":120}," core\u002F\n",[34,1176,1177,1179,1182,1184],{"class":36,"line":305},[34,1178,1136],{"class":112},[34,1180,1181],{"class":120},"   │",[34,1183,1152],{"class":120},[34,1185,1186],{"class":120}," src\u002F{main,test}\u002Fscala\u002Fcom\u002Facme\u002Fcore\u002F\n",[34,1188,1189,1191,1193],{"class":36,"line":311},[34,1190,1136],{"class":112},[34,1192,1139],{"class":120},[34,1194,1195],{"class":120}," domain\u002F\n",[34,1197,1198,1200,1202,1204,1207],{"class":36,"line":317},[34,1199,1136],{"class":112},[34,1201,1181],{"class":120},[34,1203,1152],{"class":120},[34,1205,1206],{"class":120}," src\u002Fmain\u002Fscala\u002Fcom\u002Facme\u002Fdomain\u002F",[34,1208,1209],{"class":40},"   # pure ADTs, no I\u002FO deps\n",[34,1211,1212,1214,1216],{"class":36,"line":549},[34,1213,1136],{"class":112},[34,1215,1152],{"class":120},[34,1217,1218],{"class":120}," infra\u002F\n",[34,1220,1221,1223,1226,1229],{"class":36,"line":554},[34,1222,1136],{"class":112},[34,1224,1225],{"class":120},"       └──",[34,1227,1228],{"class":120}," src\u002Fmain\u002Fscala\u002Fcom\u002Facme\u002Finfra\u002F",[34,1230,1231],{"class":40},"    # DB, HTTP clients, side-effects\n",[34,1233,1234,1236],{"class":36,"line":559},[34,1235,1121],{"class":112},[34,1237,1238],{"class":120}," app\u002F\n",[34,1240,1241,1243,1245,1248],{"class":36,"line":565},[34,1242,1136],{"class":112},[34,1244,1152],{"class":120},[34,1246,1247],{"class":120}," src\u002Fmain\u002Fscala\u002Fcom\u002Facme\u002FMain.scala",[34,1249,1250],{"class":40},"    # thin composition root\n",[34,1252,1253,1256,1259],{"class":36,"line":592},[34,1254,1255],{"class":112},"└──",[34,1257,1258],{"class":120}," .jvmopts",[34,1260,1261],{"class":40},"                                    # -Xmx2g -XX:+UseZGC\n",[15,1263,1265],{"id":1264},"spot-the-bug-incremental-compilation-staleness","🔍 Spot the Bug — Incremental Compilation Staleness",[94,1267,1268],{"language":29},[25,1269,1271],{"className":27,"code":1270,"language":29,"meta":30,"style":30},"\u002F\u002F Symptom: sbt says \"compiling 0 files\" but you changed a trait method\n\u002F\u002F signature that downstream modules use. Tests fail with NoSuchMethodError.\n\n\u002F\u002F Root cause: sbt's incremental compiler tracks API signatures, but\n\u002F\u002F renaming a method while keeping the same erased signature (e.g.\n\u002F\u002F def process(items: List[Int]) → def process(items: Seq[Int]))\n\u002F\u002F can miss the downstream recompile because the erased signature is\n\u002F\u002F identical (List → Seq both erase to scala.collection.immutable.Seq).\n\n\u002F\u002F Fix: force clean for erased-signature changes\n\u002F\u002F   sbt clean compile\n\u002F\u002F Or use Zinc's stronger analysis:\n\u002F\u002F   incOptions ~= (_.withRecompileAllFraction(1.0))\n",[20,1272,1273,1278,1283,1287,1292,1297,1302,1307,1312,1316,1321,1326,1331],{"__ignoreMap":30},[34,1274,1275],{"class":36,"line":37},[34,1276,1277],{"class":40},"\u002F\u002F Symptom: sbt says \"compiling 0 files\" but you changed a trait method\n",[34,1279,1280],{"class":36,"line":44},[34,1281,1282],{"class":40},"\u002F\u002F signature that downstream modules use. Tests fail with NoSuchMethodError.\n",[34,1284,1285],{"class":36,"line":50},[34,1286,140],{"emptyLinePlaceholder":139},[34,1288,1289],{"class":36,"line":56},[34,1290,1291],{"class":40},"\u002F\u002F Root cause: sbt's incremental compiler tracks API signatures, but\n",[34,1293,1294],{"class":36,"line":62},[34,1295,1296],{"class":40},"\u002F\u002F renaming a method while keeping the same erased signature (e.g.\n",[34,1298,1299],{"class":36,"line":68},[34,1300,1301],{"class":40},"\u002F\u002F def process(items: List[Int]) → def process(items: Seq[Int]))\n",[34,1303,1304],{"class":36,"line":77},[34,1305,1306],{"class":40},"\u002F\u002F can miss the downstream recompile because the erased signature is\n",[34,1308,1309],{"class":36,"line":86},[34,1310,1311],{"class":40},"\u002F\u002F identical (List → Seq both erase to scala.collection.immutable.Seq).\n",[34,1313,1314],{"class":36,"line":305},[34,1315,140],{"emptyLinePlaceholder":139},[34,1317,1318],{"class":36,"line":311},[34,1319,1320],{"class":40},"\u002F\u002F Fix: force clean for erased-signature changes\n",[34,1322,1323],{"class":36,"line":317},[34,1324,1325],{"class":40},"\u002F\u002F   sbt clean compile\n",[34,1327,1328],{"class":36,"line":549},[34,1329,1330],{"class":40},"\u002F\u002F Or use Zinc's stronger analysis:\n",[34,1332,1333],{"class":36,"line":554},[34,1334,1335],{"class":40},"\u002F\u002F   incOptions ~= (_.withRecompileAllFraction(1.0))\n",[1337,1338,1339,1343,1356],"details",{},[1340,1341,1342],"summary",{},"Answer",[1344,1345,1346,1347,1350,1351,1355],"p",{},"The ",[20,1348,1349],{},"NoSuchMethodError"," at runtime happens because Zinc (sbt's incremental compiler) uses ",[1352,1353,1354],"strong",{},"name + erased signature"," for staleness detection. When two different source-level signatures erase to the same bytecode signature, Zinc doesn't detect the change in downstream modules.",[1344,1357,1358,1361,1362,1365,1366,1369,1370,213],{},[1352,1359,1360],{},"Fix",": Run ",[20,1363,1364],{},"sbt clean compile"," for signature-level refactors, or configure ",[20,1367,1368],{},"incOptions"," with stricter recompile triggers in ",[20,1371,1372],{},"build.sbt",[15,1374,1376],{"id":1375},"tips-tricks","💡 Tips & Tricks",[1344,1378,1379,1385],{},[1352,1380,1381,1384],{},[20,1382,1383],{},"-Xprint:typer"," for debugging type inference failures",": The compiler dumps the fully-typed AST — you see exactly what types it inferred at each step, exposing where inference diverges from your expectation.",[94,1387,1388],{"language":96},[25,1389,1391],{"className":99,"code":1390,"language":96,"meta":30,"style":30},"# Show typed AST to debug \"found vs required\" errors\nscala-cli compile . --scalac-option \"-Xprint:typer\" 2>&1 | grep -A5 \"myMethod\"\n",[20,1392,1393,1398],{"__ignoreMap":30},[34,1394,1395],{"class":36,"line":37},[34,1396,1397],{"class":40},"# Show typed AST to debug \"found vs required\" errors\n",[34,1399,1400,1402,1404,1406,1409,1412,1414,1416,1419,1422],{"class":36,"line":44},[34,1401,332],{"class":112},[34,1403,732],{"class":120},[34,1405,337],{"class":120},[34,1407,1408],{"class":116}," --scalac-option",[34,1410,1411],{"class":120}," \"-Xprint:typer\"",[34,1413,125],{"class":124},[34,1415,128],{"class":124},[34,1417,1418],{"class":112}," grep",[34,1420,1421],{"class":116}," -A5",[34,1423,1424],{"class":120}," \"myMethod\"\n",[1344,1426,1427,1430,1431,1434],{},[1352,1428,1429],{},"Coursier for reproducible toolchain",": Use ",[20,1432,1433],{},"cs"," (Coursier) to bootstrap Scala, sbt, and scalafmt — avoids Homebrew version drift across CI machines.",[94,1436,1437],{"language":96},[25,1438,1440],{"className":99,"code":1439,"language":96,"meta":30,"style":30},"# Pin everything via Coursier — CI uses identical versions\ncs install scala3 sbt scalafmt\ncs launch scalafmt:3.8.3 -- --test src\u002F   # format check in CI\n",[20,1441,1442,1447,1463],{"__ignoreMap":30},[34,1443,1444],{"class":36,"line":37},[34,1445,1446],{"class":40},"# Pin everything via Coursier — CI uses identical versions\n",[34,1448,1449,1451,1454,1457,1460],{"class":36,"line":44},[34,1450,1433],{"class":112},[34,1452,1453],{"class":120}," install",[34,1455,1456],{"class":120}," scala3",[34,1458,1459],{"class":120}," sbt",[34,1461,1462],{"class":120}," scalafmt\n",[34,1464,1465,1467,1470,1473,1475,1478,1481],{"class":36,"line":50},[34,1466,1433],{"class":112},[34,1468,1469],{"class":120}," launch",[34,1471,1472],{"class":120}," scalafmt:3.8.3",[34,1474,351],{"class":116},[34,1476,1477],{"class":116}," --test",[34,1479,1480],{"class":120}," src\u002F",[34,1482,1483],{"class":40},"   # format check in CI\n",[1344,1485,1486,1492],{},[1352,1487,1488,1491],{},[20,1489,1490],{},"-explain"," in Scala 3 gives human-readable type errors",": Far more actionable than the terse Scala 2 messages.",[15,1494,1496],{"id":1495},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[1344,1498,1499,1502,1503,1506,1507,1509],{},[1352,1500,1501],{},"Scala 2 and Scala 3 cannot share the same binary artifact",": Scala 2.13 and 3.x have different TASTy\u002Fclassfile formats. For cross-published libraries, use ",[20,1504,1505],{},"%%"," (cross-built) not ",[20,1508,1505],{}," with a hardcoded version.",[1344,1511,1512,1517,1518,1521,1522,1524,1525,1528],{},[1352,1513,1514,1516],{},[20,1515,800],{}," initialization is lazy",": The ",[20,1519,1520],{},"MODULE$"," singleton initializes on first access, not at class-load time. Side effects in ",[20,1523,800],{}," bodies run at an unpredictable time — use ",[20,1526,1527],{},"@main"," or explicit initialization.",[1344,1530,1531,1537,1538,1541],{},[1352,1532,1533,1536],{},[20,1534,1535],{},"-Werror"," + Scala 2 macro libraries",": Macro expansions can produce warnings that become errors. Use ",[20,1539,1540],{},"-Wconf:cat=unused:silent"," to suppress warnings from generated code.",[1344,1543,1544,1547,1548,1551,1552,1555,1556,1558,1559,1562,1563,1566,1567,1570],{},[1352,1545,1546],{},"JVM target mismatch",": If ",[20,1549,1550],{},"scalaVersion"," targets JVM 17 but your ",[20,1553,1554],{},"JAVA_HOME"," is JDK 11, ",[20,1557,729],{}," fails with ",[20,1560,1561],{},"UnsupportedClassVersionError",". Pin ",[20,1564,1565],{},"javaOptions"," and ",[20,1568,1569],{},"fork := true"," to control the JVM version sbt uses for running.",[15,1572,1574],{"id":1573},"quick-quiz","🧠 Quick Quiz",[1344,1576,1577,1578,1581],{},"What does ",[20,1579,1580],{},"scalac -Xprint:delambdafy"," show, and why does it matter for performance?",[1337,1583,1584,1586,1596,1619],{},[1340,1585,1342],{},[1344,1587,1588,1591,1592,1595],{},[20,1589,1590],{},"-Xprint:delambdafy"," shows how the compiler desugars lambdas (",[20,1593,1594],{},"x => x * 2",") into JVM constructs:",[1597,1598,1599,1610],"ul",{},[1600,1601,1602,1605,1606,1609],"li",{},[1352,1603,1604],{},"Non-capturing lambdas"," → a singleton ",[20,1607,1608],{},"$$Lambda$"," object (zero allocation per call).",[1600,1611,1612,1615,1616,1618],{},[1352,1613,1614],{},"Capturing lambdas"," → a new ",[20,1617,1608],{}," instance per invocation, capturing free variables as fields.",[1344,1620,1621,1622,1625,1626,1628],{},"This matters for hot paths: a lambda capturing ",[20,1623,1624],{},"this"," allocates on every call. In performance-critical code, hoist the lambda to a ",[20,1627,921],{}," or use a non-capturing method reference.",[1630,1631,1632],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}",{"title":30,"searchDepth":44,"depth":44,"links":1634},[1635,1637,1638,1639,1640,1641,1642,1643,1644],{"id":17,"depth":44,"text":1636},"Compiler Pipeline: .scala → JVM Bytecode",{"id":163,"depth":44,"text":164},{"id":391,"depth":44,"text":392},{"id":778,"depth":44,"text":779},{"id":1098,"depth":44,"text":1099},{"id":1264,"depth":44,"text":1265},{"id":1375,"depth":44,"text":1376},{"id":1495,"depth":44,"text":1496},{"id":1573,"depth":44,"text":1574},"Production Scala 3 toolchain setup — scala-cli, sbt, Coursier, mill, compiler phases, JVM bytecode mapping, and project scaffolding patterns used in real codebases.","md",{},"\u002Fscala\u002F01-getting-started",{"title":5,"description":1645},"scala\u002F01-getting-started","qXQo6IcAaSrIF5fWHBiiw2TekNPmP9En6LW8s8iRggE",1789924653864]