Skip to content

Scala 3 Capabilities and Capture Checking

Scala 3 is exploring capability-based effect reasoning at the language level, centered on context parameters/functions and experimental capture checking.

Last reviewed: February 16, 2026.

FieldValue
LanguageScala 3
Feature statusExperimental
Official docsScala 3 experimental features index
Capture checking referenceCapture Checking (Scala 3 reference)

What This Is (and Is Not)

Scala capabilities are a language-level route to effect/capability tracking.

  • Is: static tracking of captured capabilities through types
  • Is not (yet): a finalized built-in algebraic effect-handler system in mainstream Scala

This work is best understood as capability discipline and effect reasoning infrastructure, still in active evolution.


Current Status (as of February 16, 2026)

  • Scala 3.8 is released (January 2026 line), and capture-checking work is highlighted in release communication.
  • Capture checking remains under the experimental feature framework.

Sources:


Core Mechanisms

1. Context Parameters and Context Functions

Scala's using / context-function machinery supports implicit capability passing with direct-style call syntax. Context functions (?=>) enable threading capabilities through call chains without explicit plumbing:

scala
// A capability for performing I/O
trait IO:
  def println(s: String): Unit
  def readLine(): String

// Using a capability via context parameter
def greet(using io: IO): Unit =
  io.println("Hello, " + io.readLine())

// Context function type: IO is passed implicitly
type Effectful[A] = IO ?=> A

def program: Effectful[Unit] =
  // IO capability is implicitly available
  println("hello")

Source: Context Functions (Scala 3 reference)

2. Capture Checking

Capture checking annotates types with the capabilities they capture, preventing capability escape and improving local reasoning about effects:

scala
import language.experimental.captureChecking

// This closure captures the 'io' capability
val f: (Int => Unit)^{io} = (x: Int) => io.println(x.toString)

The ^{io} annotation indicates that f captures the io capability. The type system ensures that:

  1. Capabilities do not escape their intended scope
  2. Functions that perform effects are properly annotated
  3. Pure functions are guaranteed to capture no capabilities

Source: Capture Checking (Scala 3 reference)

3. Boundary / Break for Structured Non-Local Exits

Available since Scala 3.3.0, boundary and break provide non-local returns -- a stepping stone toward full algebraic effects:

scala
import scala.util.boundary
import boundary.break

def findIndex(xs: List[Int], target: Int): Int =
  boundary:
    for (x, i) <- xs.zipWithIndex do
      if x == target then break(i)
    -1

boundary defines a scope and break exits it, analogous to an effect handler and an effect operation.

Source: Dropped: Nonlocal Returns (use scala.util.boundary)


Why It Matters for Effect Systems

Scala's capability direction is important because it shifts part of effect reasoning from library encodings into the language type system.

Potential upside:

  • stronger static guarantees for authority/effect usage
  • direct-style ergonomics without mandatory monadic wrappers

Current limitation:

  • full algebraic handlers with resumptions are still primarily a research/library domain, not a stabilized core Scala language feature.

Strengths

  • Language-level integration (not purely library-level discipline)
  • Strong alignment with capability-based security/reasoning ideas
  • Promising path for direct-style effect-safe APIs

Limits

  • Experimental and evolving
  • Tooling and ecosystem practices still catching up
  • Semantics and ergonomics are not yet "finished" in the way mature effect libraries are

Sources