The scala.compiletime package provides comprehensive utilities for compile-time metaprogramming, type-level computation, and constexpr-style programming in Scala 3.
def erasedValue[T]: T
transparent inline def constValue[T]: T
transparent inline def constValueOpt[T]: Option[T]
inline def constValueTuple[T <: Tuple]: TerasedValue[T]: Pattern match on types without having actual values (used in inline matches)constValue[T]: Convert singleton types to their corresponding values at compile timeconstValueOpt[T]: Safe version of constValue that returns None if conversion failsconstValueTuple[T]: Extract tuple of constant values from tuple typetransparent inline def summonFrom[T](f: Nothing => T): T
transparent inline def summonInline[T]: T
inline def summonAll[T <: Tuple]: TsummonFrom[T]: Pattern match on available given instancessummonInline[T]: Summon given value with delayed resolution until full inliningsummonAll[T]: Summon all elements of a tuple type as a tuple of given instancesinline def error(inline msg: String): Nothing
transparent inline def codeOf(arg: Any): String
inline def requireConst(inline x: Boolean | Byte | Short | Int | Long | Float | Double | Char | String): Uniterror: Produce user-defined compile errors during inline expansioncodeOf: Get string representation of code (for debugging and error messages)requireConst: Assert that a value is constant after inlining and constant foldingdef uninitialized: Nothing
def deferred: Nothing
def byName[T](x: => T): Tuninitialized: Marker for uninitialized mutable fields (compile-time only)deferred: Marker for deferred given definitions in traitsbyName: Assertion for by-name parameters (used in nullability checking)extension [T](x: T) transparent inline def asMatchable: x.type & MatchableCast values to be Matchable for pattern matching with unconstrained type parameters.
// Arithmetic operations
type +[X <: Int, Y <: Int] <: Int
type -[X <: Int, Y <: Int] <: Int
type *[X <: Int, Y <: Int] <: Int
type /[X <: Int, Y <: Int] <: Int
type %[X <: Int, Y <: Int] <: Int
// Comparison operations
type <[X <: Int, Y <: Int] <: Boolean
type <=[X <: Int, Y <: Int] <: Boolean
type >[X <: Int, Y <: Int] <: Boolean
type >=[X <: Int, Y <: Int] <: Boolean
// Utility operations
type S[X <: Int] <: Int // Successor (X + 1)
type Abs[X <: Int] <: Int // Absolute value
type Min[X <: Int, Y <: Int] <: Int // Minimum
type Max[X <: Int, Y <: Int] <: Int // Maximumtype ![X <: Boolean] <: Boolean // Negation
type &&[X <: Boolean, Y <: Boolean] <: Boolean // Logical AND
type ||[X <: Boolean, Y <: Boolean] <: Boolean // Logical OR
type XOR[X <: Boolean, Y <: Boolean] <: Boolean // Exclusive ORtype +[X <: String, Y <: String] <: String // Concatenation
type Length[X <: String] <: Int // String length
type Substring[S <: String, IBeg <: Int, IEnd <: Int] <: String // Substring
type CharAt[S <: String, I <: Int] <: Char // Character at index
type Matches[S <: String, Regex <: String] <: Boolean // Regex matchingimport scala.compiletime.*
inline def typeDescription[T]: String =
inline erasedValue[T] match
case _: String => "text"
case _: Int => "integer"
case _: Boolean => "boolean"
case _: EmptyTuple => "empty tuple"
case _: (h *: t) => s"tuple starting with ${typeDescription[h]}"
case _ => "unknown type"
// Usage
val desc1 = typeDescription[String] // "text"
val desc2 = typeDescription[Int *: String *: EmptyTuple] // "tuple starting with integer"import scala.compiletime.*
// Convert singleton types to values
type Three = 3
type Hello = "hello"
val three: 3 = constValue[Three] // 3
val hello: "hello" = constValue[Hello] // "hello"
// Safe constant extraction
inline def safeConstant[T]: String =
constValueOpt[T] match
case Some(value) => s"Constant: $value"
case None => "Not a constant type"
val result1 = safeConstant[42] // "Constant: 42"
val result2 = safeConstant[String] // "Not a constant type"
// Working with tuple constants
type Numbers = (1, 2, 3)
val numbers: (1, 2, 3) = constValueTuple[Numbers]import scala.compiletime.*
inline def safeDivision(inline numerator: Int, inline denominator: Int): Double =
requireConst(denominator) // Ensure denominator is known at compile time
inline if constValue[denominator.type] == 0 then
error("Division by zero detected at compile time")
else
numerator.toDouble / denominator.toDouble
// Usage
val result1 = safeDivision(10, 2) // Compiles: 5.0
// val result2 = safeDivision(10, 0) // Compile error: "Division by zero detected at compile time"
val x = 5
// val result3 = safeDivision(10, x) // Compile error: x is not constantimport scala.compiletime.*
trait Show[T]:
def show(value: T): String
given Show[Int] with
def show(value: Int) = value.toString
given Show[String] with
def show(value: String) = s"\"$value\""
given Show[Boolean] with
def show(value: Boolean) = value.toString
// Summon specific instance
inline def showValue[T](value: T): String =
summonInline[Show[T]].show(value)
// Pattern match on available instances
inline def showAny[T](value: T): String =
summonFrom {
case given Show[T] => summon[Show[T]].show(value)
case _ => "No Show instance available"
}
// Summon multiple instances
inline def showTuple[T <: Tuple](tuple: T): List[String] =
summonAll[Tuple.Map[T, Show]].toList.zip(tuple.toList).map {
case (showInstance, value) => showInstance.asInstanceOf[Show[Any]].show(value)
}
val result1 = showValue(42) // "42"
val result2 = showAny("hello") // "\"hello\""
val result3 = showTuple((42, "world", true)) // List("42", "\"world\"", "true")import scala.compiletime.ops.int.*
type Two = 2
type Three = 3
type Five = Two + Three
type Six = Two * Three
type IsLess = Two < Three // true
type Successor = S[Two] // 3
// Compile-time computation
inline def factorial[N <: Int]: Int =
inline constValue[N] match
case 0 => 1
case 1 => 1
case n => n * factorial[N - 1]
val fact5: Int = factorial[5] // 120 (computed at compile time)
// Type-level validation
inline def validateArrayIndex[Size <: Int, Index <: Int]: Unit =
inline if constValue[Index >= Size] then
error("Array index out of bounds")
else if constValue[Index < 0] then
error("Negative array index")
validateArrayIndex[5, 2] // Compiles
// validateArrayIndex[5, 7] // Compile error: "Array index out of bounds"import scala.compiletime.ops.string.*
type Hello = "Hello"
type World = "World"
type HelloWorld = Hello + " " + World // "Hello World"
type HelloLength = Length[Hello] // 5
type FirstChar = CharAt[Hello, 0] // 'H'
inline def greet[Name <: String]: String =
"Hello, " + constValue[Name] + "!"
val greeting = greet["Alice"] // "Hello, Alice!"
// Compile-time string validation
inline def validateEmail[Email <: String]: Unit =
inline if !constValue[Matches[Email, ".*@.*\\..*"]] then
error("Invalid email format")
validateEmail["user@example.com"] // Compiles
// validateEmail["invalid-email"] // Compile error: "Invalid email format"import scala.compiletime.*
inline def logged[T](inline expr: T): T =
println(s"Evaluating: ${codeOf(expr)}")
expr
val result = logged(2 + 3 * 4)
// Prints: "Evaluating: 2.+(3.*(4))"
// Returns: 14
// Debug macro expansion
inline def debugType[T]: String =
inline erasedValue[T] match
case _: Int => s"Int type with code: ${codeOf(42)}"
case _: String => s"String type with code: ${codeOf("hello")}"
case _ => s"Other type"import scala.compiletime.*
// Compile-time list processing
type NestedTuple = ((Int, String), (Boolean, Double), (Char, Float))
inline def processTuple[T <: Tuple]: String =
inline erasedValue[T] match
case _: EmptyTuple => "empty"
case _: (h *: t) =>
s"head: ${typeDescription[h]}, tail: ${processTuple[t]}"
inline def tupleSize[T <: Tuple]: Int =
inline erasedValue[T] match
case _: EmptyTuple => 0
case _: (h *: t) => 1 + tupleSize[t]
val description = processTuple[NestedTuple]
val size = tupleSize[NestedTuple] // 3
// Compile-time validation for type classes
inline def requireShow[T]: Unit =
summonFrom {
case given Show[T] => ()
case _ => error(s"No Show instance for type ${codeOf(??? : T)}")
}
inline def showIfPossible[T](value: T): String =
requireShow[T]
summon[Show[T]].show(value)import scala.compiletime.*
inline def platformSpecific[T](value: T): String =
inline if constValue[scala.util.Properties.isWin] then
s"Windows: $value"
else if constValue[scala.util.Properties.isLinux] then
s"Linux: $value"
else
s"Other OS: $value"
// Feature flags at compile time
inline val FEATURE_ENABLED = true
inline def withFeature[T](inline enabled: T, inline disabled: T): T =
inline if FEATURE_ENABLED then enabled else disabled
val result = withFeature("Feature is on", "Feature is off")The compile-time programming capabilities in Scala 3 enable powerful metaprogramming techniques, allowing developers to move computations and validations from runtime to compile time, resulting in better performance and stronger type safety guarantees.