VirtusLab Scala Stack (VSS)

Direct-style Scala: type-safe code that is easy to comprehend and generate.

Our open-source stack that builds on solid foundations provided by Scala 3 with its advanced type system, and Java's Virtual Threads, which bring the performance of reactive programming while retaining a familiar programming model. Use the libraries together or each one separately, whatever is the best fit for your project.

Generate a seed project

For coding agents: llms.txt · scala-skill

VSS in action!

Each tab is an excerpt of a runnable scala-cli script.

Code examples
case class Forecast(city: String, tempC: Int, sunny: Boolean)
  derives ConfiguredJsonValueCodec, Schema
case class TooFarAhead(maxDays: Int) derives ConfiguredJsonValueCodec, Schema
val maxDays: Int = 14

val forecast: ServerEndpoint[Any, Identity] = endpoint.get
  .in("forecast" / path[String]("city")).in(query[Int]("days"))
  .out(jsonBody[Forecast]).errorOut(jsonBody[TooFarAhead])
  .handle: (city, days) =>
    if days > maxDays then Left(TooFarAhead(maxDays))
    else Right(Forecast(city, 21, sunny = true))

@main def run(): Unit =
  NettySyncServer().port(8080).addEndpoint(forecast).startAndWait()
case class Quote(vendor: String, priceCents: Int)

def ask(vendor: String, priceCents: Int, latency: FiniteDuration): Quote =
  sleep(latency)
  Quote(vendor, priceCents)

def cheapest(): Quote =
  val (cached, acme, globex) = par(
    ask("cache", 1300, 10.millis),
    retry(Schedule.exponentialBackoff(50.millis).maxRetries(3))(
      ask("acme", 1250, 100.millis)
    ),
    timeoutOption(1.second)(ask("globex", 1100, 200.millis))
  )
  (List(cached, acme) ++ globex.toList).minBy(_.priceCents)
case class City(name: String) derives Codec.AsObject, Schema

def ask(question: String): Either[AgentFailure, String] = resourceScope:
  val weather = AgentTool.fromFunction("get_weather", "Current weather in a city"):
    (city: City) => s"22°C and sunny in ${city.name}"

  val agent = OpenAIAgent
    .synchronous(OpenAI.fromEnv, ChatCompletionModel.GPT4oMini)
    .maxIterations(5)
    .tools(weather)
    .build

  val backend = useCloseableInScope(DefaultSyncBackend())
  agent.run(question)(backend).finalAnswer
def storage(using Context): Stack =
  val bucket = s3.BucketV2("reports")

  val versioning = s3.BucketVersioningV2(
    "reports-versioning",
    s3.BucketVersioningV2Args(
      bucket = bucket.id,
      versioningConfiguration =
        BucketVersioningV2VersioningConfigurationArgs(status = "Enabled")
    )
  )

  Stack(versioning).exports(bucketName = bucket.bucket)

@main def run = Pulumi.run(storage)
val echo: Uri = uri"wss://ws.postman-echo.com/raw"

def greet(names: List[String])(ws: SyncWebSocket): List[String] =
  names.foreach(name => ws.sendText(s"hello, $name"))
  names.map(_ => ws.receiveText())

@main def run(): Unit = resourceScope:
  val backend = useCloseableInScope(DefaultSyncBackend())
  val response = basicRequest
    .get(echo)
    .response(asWebSocketOrFail(greet(List("ada", "grace"))))
    .send(backend)
  response.body.foreach(println)
@SqlName("reading")
@Table(SqlNameMapper.CamelToSnakeCase)
case class NewReading(city: String, tempC: Int)
  extends CreatorOf[Reading] derives DbCodec

@Table(SqlNameMapper.CamelToSnakeCase)
case class Reading(@Id id: Long, city: String, tempC: Int) derives EntityMeta

val readings: Repo[NewReading, Reading, Long] = Repo()

def insertAndFindWarmest(xa: Transactor[H2.type]): Option[Reading] = xa.transact:
  readings.create(NewReading("Krakow", 24))
  readings.create(NewReading("Oslo", 9))
  QueryBuilder.from[Reading].where(_.tempC > 20)
    .orderBy(_.tempC, SortOrder.Desc).first()
case class Reading(sensor: String, tempC: Int)

val cycle: Vector[Int] = Vector(17, 19, 21, 23, 20)

def sensor(name: String, every: FiniteDuration): Flow[Reading] =
  Flow.tick(every).zipWithIndex
    .map((_, i) => Reading(name, cycle((i % cycle.size).toInt)))

def warmReadings(): List[Reading] =
  sensor("north", 10.millis)
    .merge(sensor("south", 25.millis))
    .filter(_.tempC >= 20)
    .take(6)
    .runToList()
@main def run(args: String*): Unit = flow(OrcaArgs(args.toArray)):
  val plan = stage("Plan"):
    Plan.autonomous.from(userPrompt, planningAgent).value

  val session = codingAgent.session("implementer", seed = plan.brief)

  for task <- plan.tasks do
    stage(s"Task: ${task.title}"):
      session.run(task.description)
      reviewThenFix(
        coderSession = session,
        reviewers = allReviewers(reviewAgent),
        task = task
      )

VirtusLab: the company behind Scala

VirtusLab maintains the Scala 3 compiler and the Scala runner (Scala CLI). We provide enterprise support, consulting and development services, as well as a broad migration offer, between Scala version and to/from Scala.

Why VSS?

Why direct-style Scala 3 is the best choice for long-term system development.

  1. Direct style

    Familiar style

    Direct-style is programming as we all know it - it is immediately familiar for anyone looking at the code, be it a human developer or an AI coding agent.

    Results of functions are immediately available, there’s no need to wrestle with Futures, IOs or Promises. There’s no async/await which pollutes the code, and no suspendable or “regular” function distinction.

    At the same time, thanks to Java’s virtual threads, direct-style Scala retains the performance known from asynchronous, reactive programming styles.

  2. Type safety

    Make illegal states unrepresentable, use latest Scala 3 features.

    The simplest features are sometimes the most powerful: algebraic data types with pattern matching - pioneered in mainstream languages by Scala, are now becoming the de facto standard in any language.

    Scala 3 enhances these features with lightweight enums & opaque types. Combined with implicits, path-dependent types, lambdas, type members and parameters and many more, you get unprecedented type-safety - which provides a fast and precise feedback loop for coding agents.

  3. Mature platform

    Scala leverages the most mature runtime available.

    Scala runs on the JVM, a platform with decades of innovation, research and hardening. Java’s Garbage Collector has been fine-tuned so to meet the requirements of web-scale and enterprise services.

    Scala seamlessly interoperates with Java, opening the door to a vast ecosystem of libraries, so that most problems can be solved by reusing code, instead of writing it anew.

  4. Structured concurrency

    Making concurrency more comprehensible and harder to get wrong.

    With structured concurrency, the syntactical structure of the code defines the lifetime of threads. This allows for more local reasoning, ensuring proper resource cleanup, no “action-at-a-distance”, or thread leaks.

    A solution that was born in Python, form the basis of safe coroutine usage in Kotlin, is now available for Scala & Java.

  5. No lock-in

    Any part of our stack can be used stand-alone, replaced or combined with third-party libraries.

    We propose to build applications on top of a set of libraries, not a framework: each component can be replaced, or used in isolation.

    While many of our libraries support direct-style, they often don’t mandate it, and allow working with effect systems such as cats-effect or ZIO, using the same APIs.

    You’re free to choose a JSON library, a programming style, or error model that best suites your team and your application.

Meet us & learn more about Scala+AI

Scala Days 2026 takes place in Berlin on October 12–13. After the conference we are touring San Francisco, Chicago and New York on October 19–26, bringing some of ScalaDays to the US.

Templates

Language

Tooling

Backend

AI tooling

DevOps

Visdom

AI-native SDLC platform: the missing layer between AI coding and production. Built by VirtusLab to bring the context, governance, CI, and traceability needed to make AI-generated code production ready.