Scala coding style, tooling, and functional programming guidance, with dedicated sections on direct-style Scala, Ox structured concurrency, and synchronous Tapir. Auto-load for any task involving Scala code, especially when using direct-style or "plain" Scala.
Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Der Befehl bleibt in einer Zeile. Scrollen Sie horizontal, um ihn vor dem Kopieren vollständig zu prüfen.
Sie bevorzugen eine lokale Kopie? Laden Sie die Dateien herunter, die SkillsMP derzeit vorliegen.
Datei-Explorer
26 Dateien
SKILL.md wird angezeigt
SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
direct-style-scala
description
Scala coding style, tooling, and functional programming guidance, with dedicated sections on direct-style Scala, Ox structured concurrency, and synchronous Tapir. Auto-load for any task involving Scala code, especially when using direct-style or "plain" Scala.
You are an expert backend software engineer and architect.
Scala tooling
ALWAYS use tools to compile and run tests instead of relying on bash commands
after adding a dependency to build.sbt, ALWAYS run the import-build tool
to lookup a dependency or the latest version, use the find-dep tool. If
find-dep is unavailable, resolve versions yourself — but NEVER from
search.maven.org/solrsearch, whose index can be stale by many months (it has
been observed pinned to year-old versions). Use these instead:
latest version of a KNOWN artifact — read the canonical resolver source,
https://repo1.maven.org/maven2/<group-with-slashes>/<artifact>/maven-metadata.xml,
and take <release> (or the last <version>). <release> may itself be a
prerelease (e.g. scalatest's was 3.3.0-SNAP4, Scala 3's 3.9.0-RC1); unless
you specifically want one, skip versions containing RC, M<n>, SNAP,
alpha, beta, or NIGHTLY and take the latest stable. Remember the Scala
suffix, e.g. com/softwaremill/ox/core_3. This is never stale.
DISCOVERY by name (unknown coordinates) — query Scaladex, which is
Scala-aware (handles _3 / cross-versions):
https://index.scala-lang.org/api/autocomplete?q=<name> to find the
org/repo, then look up the exact artifact on repo1 as above.
to lookup the API of a class, use the inspect tool. To lookup the docs or
usages, use the get-docs and get-usages tools
to compile the project, use compile-full, compile-module tools
to search for symbols, use glob-search and typed-glob-search tools
if you do need to use sbt, use sbt --client instead of sbt to connect to
a running sbt server for faster execution
to verify that the app starts use sbt run, WITHOUT --client, as it
prevents interrupting the process
before committing, ALWAYS format all changed Scala files using the sbt
scalafmt plugin: sbt --client scalafmtAll
the project MUST compile with zero warnings. Ensure build.sbt includes
-Wunused:all, -Wvalue-discard, -Wnonunit-statement in scalacOptions.
Fix warnings in code; only use @nowarn for generated code or unfixable
third-party issues (with a comment explaining why).
Coding style
ALWAYS use braceless syntax — do not use {}
responsibilities in code MUST be segregated between appropriately named
entities
before creating or moving a .scala file, decide its package, filename, and
top-level visibility. Read Code Organization and
Visibility when adding packages/modules or
widening visibility.
when dealing with resources, properly track who owns which resources, and
ensure proper ordering on cleanup
every top-level class, trait, enum, and object MUST have intentional
visibility at declaration time: default-public (no modifier),
private[<subpkg>], or private[<rootpkg>]. Choose by scanning actual call
sites.
comment on any aspects that aren't obvious from the implementation, but are
important to know when reading the code
each function MUST handle exactly one concern — either a single logical
operation, or a short orchestration of named steps. When a function does
multiple things (validate, transform, persist, notify), extract each step into
its own well-named function so the orchestrator reads as a sequence of
intentions. Naming a coherent step is always a valid reason to extract, even
if the logic is used only once.
use Ox's .pipe and .tap (import ox.*) to drop single-use vals that
only feed the next line. .pipe(f) returns f(value); .tap(f) runs a side
effect and returns the value unchanged. Keep a named val when the name
documents intent or the value is reused
tests MUST be targeted — each test covers exactly one scenario. No
overlapping or redundant tests.
every public function, val, and given MUST have an explicit return type — this
prevents accidental signature drift during refactors:
// Wrong — inferred return type can silently change:
def findUser(id: Id[User])(using DbTx) =
userModel.findById(id).toRight(Fail.NotFound("user"))
// Right — return type is explicit and stable:
def findUser(id: Id[User])(using DbTx): Either[Fail, User] =
userModel.findById(id).toRight(Fail.NotFound("user"))
Performance
NEVER materialize unbounded data into memory. Use streaming with Flow or
paging to process large datasets and paginated API results incrementally.
virtual threads are never preempted — long CPU-bound computations (a few
suffice, e.g. a mapPar over such work) can starve every other virtual
thread in the process. Run long or non-instrumentable compute via
computeIntensive (platform-thread pool; the blocking caller keeps it
structured); in CPU-bound loops you control, call cede() about once per
millisecond.
Direct-style Scala
in Tapir, use .handle / .handleSecurity / .handleSuccess to wire
endpoint logic — NEVER use .serverLogic / .serverSecurityLogic. The
.handle family is the direct-style API. The .serverLogic family requires
a monadic wrapper (Future, IO) and MUST NOT be used.
ALWAYS use Ox for threading, channels, and async coordination. Avoid raw
Thread.ofVirtual, LinkedBlockingQueue, synchronized/Lock, and
lifecycle flags. Use java.util.concurrent coordination primitives only for
pure atomic state or when bridging a foreign API that Ox does not cover.
create local, focused supervised scopes for request-, message-, or
job-level concurrency. Accept a parent (using Ox) only when a fork or
resource must be tied to that parent scope's lifetime.
keep constructors plain; use factories that take (using Ox) and return
values that do not carry the capability. If the factory only registers
resources and starts no forks, take the narrower (using ResourceScope);
for scoped cleanup with no enclosing scope and no concurrency, use
resourceScope instead of supervised.
decide the owning scope BEFORE writing concurrent code. Model a stream reader
or worker as a fork in a supervised scope whose lifetime matches the work;
its result is the fork's return value (fork{…}.join()), not a value
published through a shared AtomicReference. NEVER return an object that owns
running forks/threads to be driven later — its lifetime escapes every scope,
making cancellation and cleanup manual again. Pass a consumer into the scope
instead of handing a live handle out.
a fork blocked reading a subprocess pipe, stdin, a file, or any other
classic java.io stream is NOT ended by scope cancellation — without the
right teardown shape, shutdown deadlocks. BEFORE writing code that drives a
subprocess or reads a blocking external stream (socket, SSE), read
Subprocesses and External
Streams.
Functional programming
use pure functions, immutable data, higher-order functions, ADTs. NEVER use
shared mutable state.
var declarations MUST be inside methods (e.g. processing loops), never
as class fields. Class-level vars break reasoning and testability. The sole
exception is mutable state encapsulated by an Ox Actor, which serialises every
invocation onto a single thread — the actor is what makes the field safe to hold
(see Concurrency and Inter-Thread Communication).
Use only immutable collections (Map, Set, List) — never mutable.Map,
mutable.Set, mutable.Buffer.
model state as an immutable case class. State transitions are pure functions
that take the current state and return a new one via .copy(). Confine the
var that threads state to the smallest possible scope:
case class ProcessingState(
processed: Map[String, Long] = Map.empty,
pending: Set[String] = Set.empty
)
def handleItem(state: ProcessingState, item: Item): ProcessingState =
state.copy(processed = state.processed.updated(item.key, item.offset))
def run(items: Iterator[Item]): ProcessingState =
var state = ProcessingState()
for item <- items do
state = handleItem(state, item)
state
push side effects behind traits so that state transitions are testable without
real infrastructure. Tests substitute in-memory implementations — mutable
collections are acceptable in test helpers that simulate external systems.
APIs MUST be lawful: given identical arguments and explicit dependencies,
they yield the same observable result. Do not hide dependencies like Clock,
Random, or UUID inside methods — pass them explicitly or capture them in
the class constructor:
// Wrong — hidden non-determinism:
class OrderService:
def place(order: Order): Confirmation =
val id = UUID.randomUUID()
val now = Instant.now()
Confirmation(id, now)
// Right — dependencies are explicit and injectable:
class OrderService(clock: Clock, idGenerator: () => UUID):
def place(order: Order): Confirmation =
val id = idGenerator()
val now = clock.instant()
Confirmation(id, now)
wrap String, Int, Long, and Boolean domain values in opaque types or
enums — NEVER use raw primitives for domain concepts. This applies to
identifiers (OrderId, ProductCode), quantities (Quantity, Amount), and
configuration values (Port, TopicName). When a generated library (e.g.
scalaxb) produces raw String fields, introduce opaque types at the boundary
where generated types are converted to domain types.
eliminate boolean blindness — replace Boolean parameters and return values
with two-case enums so intent is explicit and exhaustiveness is checked:
// Wrong — caller must remember what `true` means:
def recordFlush(success: Boolean, durationMs: Double): Unit
// Right — intent is unambiguous:
enum FlushOutcome:
case Success, Failure
def recordFlush(outcome: FlushOutcome, duration: Duration): Unit
NEVER throw exceptions for recoverable failures. Instead, return an Either[E, T].
Use exceptions only for unrecoverable errors, which should terminate the current
processing unit (request, message handling, etc.)
if a value can be absent, use Option[T] — NEVER use null or sentinel
values. Option is for presence/absence only, not for errors.
model different states of an entity as separate types — NEVER use Option
fields to represent state transitions:
// Wrong — callers must remember to check confirmedAt:
case class Order(id: Id[Order], items: List[Item], confirmedAt: Option[Instant])
// Right — the type tells you what state the order is in:
case class PendingOrder(id: Id[Order], items: List[Item])
case class ConfirmedOrder(id: Id[Order], items: List[Item], confirmedAt: Instant)
design domain models so that invalid data CANNOT be constructed. Use enums,
opaque types, or smart constructors to encode invariants:
// Wrong — any string is accepted:
def setPort(port: Int): Unit
// Right — invalid values are rejected at construction:
opaque type Port = Int
object Port:
def apply(value: Int): Either[String, Port] =
if value >= 1 && value <= 65535 then Right(value)
else Left(s"Port out of range: $value")
define sealed-trait or enum error hierarchies — NEVER use stringly-typed
errors.
NEVER use bare try/catch for recoverable failures. Reserve try/catch for
defect or unrecoverable error boundaries only.
Use-Case Guide
BEFORE writing any code that uses Tapir, Ox, sttp, or direct-style Scala, you MUST
fetch the chapter(s) relevant to your current task from this guide and follow the
patterns shown there. This is not optional — code that ignores guide patterns will
be rejected in review.
Retrieve the chapter as raw, unmodified text — read every code block and
paragraph in full. Do NOT use a tool that summarises the page: summaries silently
drop the > Required / > Important callouts and the exact API calls that make
the chapter correct. Prefer reading the chapter file directly from the installed
skill directory; if fetching over the network, use a method that returns the
verbatim file (a raw HTTP GET), not a fetch-and-summarise tool.
Every API, pattern, and constraint described in the fetched chapter MUST be
followed. If the chapter says to use a specific API (e.g. useInScope for
resource management), do NOT substitute a different approach. If the chapter
marks something as required, it is required.
New Project Setup — minimal direct-style
Scala project skeleton with sbt and Ox: directory layout, build.sbt,
required scalacOptions, OxApp.Simple entry point. adopt-tapir as a
starting point for HTTP projects.
Code Organization and Visibility — top-level
visibility, file naming exceptions, Scala 3 package shadowing, and
sbt/Scalafix boundary enforcement.
Resource Management — useInScope,
useCloseableInScope, reverse-order release, scope-based cleanup;
resourceScope for cleanup without concurrency, using ResourceScope as
the narrower capability.
Background Processes — OxApp entry point,
forkDiscard/forkUserDiscard for daemon vs. user threads,
forever/sleep for periodic loops, orderly shutdown.
Concurrency and Inter-Thread Communication
— Flows for declarative concurrent pipelines (mapPar, merge,
mapStateful), Ox primitive selection, channels for worker mailboxes and
shutdown, actors for serialized mutable state, computeIntensive/cede for
CPU-bound work on virtual threads.
Subprocesses and External Streams
— driving a subprocess / socket / SSE reader as a fork whose return value is
the result; why a non-interruptible pipe read needs the resource destroyed
in the scope body's finally (before the join) rather than via
releaseAfterScope; process-tree teardown; abandonOnInterruptReads for
reads that can't be unblocked by closing; pipe back-pressure.
Error Handling
Error Handling — Fail ADT, Ox either blocks with
.ok() short-circuiting, transactEither, .catching, nesting rules.
Error Output Customisation — JSON error
responses for all error types. Bidirectional Fail → HTTP status code
mapping, failOutput, defaultHandlers for decode failures and 404s.
SOAP with scalaxb — XSD-to-Scala code generation,
SOAP envelope wrapping/unwrapping, Tapir XML codecs for scalaxb types,
SOAPAction-based endpoint routing, SOAP fault error handlers.
JSON Request and Response Bodies — jsoniter codec
derivation for DTOs, why list bodies need their own codec, encoding
parameterless enums as plain strings via withDiscriminatorFieldName(None),
and using opaque-type identifiers directly in DTOs.
Endpoint Inputs — PlainCodecs for
path/query/header inputs: mapping a built-in codec onto an opaque-type id,
Codec.derivedEnumeration for enum-valued inputs, and how multiple inputs reach
the handler.
Data & Integration
SQL Persistence — Magnum with PostgreSQL: @Table
case classes, DbCodec for opaque types, Repo/TableInfo, sql
interpolation, Flyway migrations, HikariCP.