The scala.quoted package provides Scala 3's powerful macro system using quotes and splices for compile-time code generation and analysis.
trait Quotes:
// Provides access to compiler internals and reflection API
type reflect: ReflectionThe main context object providing access to the compiler's reflection API and utilities needed for macro programming.
abstract class Expr[+T]:
def show(using Quotes): String
def value(using Quotes): T // Only works for constant expressions
def asTerm(using Quotes): reflect.TermRepresents quoted expressions that can be spliced into code. Expr[T] contains code that, when executed, produces a value of type T.
abstract class Type[T <: AnyKind]:
def show(using Quotes): String
def asTerm(using Quotes): reflect.TermRepresents quoted types that can be used in type-level computations and spliced into contexts expecting types.
trait ToExpr[T]:
def apply(x: T)(using Quotes): Expr[T]
object ToExpr:
given ToExpr[Boolean]
given ToExpr[Byte]
given ToExpr[Short]
given ToExpr[Int]
given ToExpr[Long]
given ToExpr[Float]
given ToExpr[Double]
given ToExpr[Char]
given ToExpr[String]
given [T: ToExpr]: ToExpr[List[T]]
given [T: ToExpr]: ToExpr[Seq[T]]
given [T: ToExpr]: ToExpr[Option[T]]
// ... and many more standard typesType class for converting runtime values to quoted expressions.
trait FromExpr[T]:
def apply(expr: Expr[T])(using Quotes): Option[T]
object FromExpr:
given FromExpr[Boolean]
given FromExpr[Byte]
given FromExpr[Short]
given FromExpr[Int]
given FromExpr[Long]
given FromExpr[Float]
given FromExpr[Double]
given FromExpr[Char]
given FromExpr[String]
given [T: FromExpr]: FromExpr[List[T]]
given [T: FromExpr]: FromExpr[Option[T]]
// ... and many more standard typesType class for extracting constant values from quoted expressions (when possible).
abstract class Varargs[T]:
def apply(using Quotes): Seq[Expr[T]]Handles variable arguments in macro contexts, providing access to a sequence of expressions.
class ExprMap(using Quotes):
def apply[T](expr: Expr[T]): Expr[T]
def transformChildren[T](expr: Expr[T]): Expr[T]Utility class for recursively transforming expressions in macro code.
import scala.quoted.*
// Quoting expressions - creates Expr[T]
'{ 1 + 2 } // Expr[Int]
'{ "hello" + "world" } // Expr[String]
'{ List(1, 2, 3) } // Expr[List[Int]]
// Splicing expressions - injects Expr[T] into quoted context
val expr: Expr[Int] = '{ 42 }
'{ $expr + 1 } // Expr[Int] representing 42 + 1
// Quoting types - creates Type[T]
'[Int] // Type[Int]
'[List[String]] // Type[List[String]]def makeAddition(x: Expr[Int], y: Expr[Int])(using Quotes): Expr[Int] =
'{ $x + $y }
def makeList[T: Type](elements: Expr[T]*)(using Quotes): Expr[List[T]] =
'{ List(${ Varargs(elements.toSeq) }*) }import scala.quoted.*
inline def debug[T](inline x: T): T =
${ debugImpl('x) }
def debugImpl[T: Type](x: Expr[T])(using Quotes): Expr[T] =
val code = x.show
'{
println(s"Debug: ${ Expr(code) } = ${$x}")
$x
}
// Usage
val result = debug(2 + 3 * 4)
// Prints: Debug: 2.+(3.*(4)) = 14
// Returns: 14import scala.quoted.*
inline def optimizedOperation[T](inline x: T, inline y: T): T =
${ optimizedOperationImpl('x, 'y) }
def optimizedOperationImpl[T: Type](x: Expr[T], y: Expr[T])(using Quotes): Expr[T] =
(x.value, y.value) match
case (Some(0), _) => y // 0 + y = y
case (_, Some(0)) => x // x + 0 = x
case (Some(a), Some(b)) => Expr(a + b) // Constant folding
case _ => '{ $x + $y } // Runtime addition
val result1 = optimizedOperation(0, 42) // Optimized to just 42
val result2 = optimizedOperation(10, 20) // Optimized to 30
val result3 = optimizedOperation(x, y) // Runtime additionimport scala.quoted.*
inline def typeInfo[T]: String =
${ typeInfoImpl[T] }
def typeInfoImpl[T: Type](using Quotes): Expr[String] =
import quotes.reflect.*
val tpe = TypeRepr.of[T]
val typeName = tpe.show
val isGeneric = tpe.typeArgs.nonEmpty
val info = s"Type: $typeName, Generic: $isGeneric"
Expr(info)
val info1 = typeInfo[Int] // "Type: Int, Generic: false"
val info2 = typeInfo[List[String]] // "Type: List[String], Generic: true"import scala.quoted.*
inline def analyzeExpression[T](inline expr: T): String =
${ analyzeExpressionImpl('expr) }
def analyzeExpressionImpl[T: Type](expr: Expr[T])(using Quotes): Expr[String] =
import quotes.reflect.*
val term = expr.asTerm
val analysis = term match
case Literal(constant) => s"Literal: ${constant.show}"
case Ident(name) => s"Identifier: $name"
case Apply(fun, args) => s"Application: ${fun.show} with ${args.length} arguments"
case Select(qualifier, name) => s"Selection: $name from ${qualifier.show}"
case _ => s"Complex expression: ${term.show}"
Expr(analysis)
val analysis1 = analyzeExpression(42) // "Literal: 42"
val analysis2 = analyzeExpression("hello".length) // Complex analysis
val analysis3 = analyzeExpression(List(1, 2, 3)) // "Application: List with 3 arguments"import scala.quoted.*
inline def logCalls[T](inline expr: T): T =
${ logCallsImpl('expr) }
def logCallsImpl[T: Type](expr: Expr[T])(using Quotes): Expr[T] =
import quotes.reflect.*
def transform(term: Term): Term = term match
case Apply(fun, args) =>
val transformedArgs = args.map(transform)
val loggedCall = '{
println(s"Calling: ${${Expr(fun.show)}}")
${Apply(fun, transformedArgs).asExprOf[Any]}
}.asTerm
loggedCall
case _ => term.changeOwner(Symbol.spliceOwner)
transform(expr.asTerm).asExprOf[T]
// Usage logs all method calls
val result = logCalls {
val x = List(1, 2, 3)
x.map(_ + 1).filter(_ > 2)
}import scala.quoted.*
inline def validateJson(inline json: String): String =
${ validateJsonImpl('json) }
def validateJsonImpl(json: Expr[String])(using Quotes): Expr[String] =
json.value match
case Some(jsonString) =>
// Validate JSON at compile time
try
// Assuming some JSON parser
parseJson(jsonString) // Throws if invalid
json
catch
case e: Exception =>
quotes.reflect.report.errorAndAbort(s"Invalid JSON: ${e.getMessage}")
case None =>
quotes.reflect.report.errorAndAbort("JSON string must be a compile-time constant")
val validJson = validateJson("""{"name": "Alice", "age": 30}""") // Compiles
// val invalidJson = validateJson("""{"name": "Alice", "age":}""") // Compile errorimport scala.quoted.*
inline def showStructure[T](inline value: T): String =
${ showStructureImpl('value) }
def showStructureImpl[T: Type](value: Expr[T])(using Quotes): Expr[String] =
import quotes.reflect.*
def analyzeType(tpe: TypeRepr): String = tpe match
case AppliedType(base, args) =>
s"${base.show}[${args.map(analyzeType).mkString(", ")}]"
case _ => tpe.show
def analyzeTerm(term: Term): String = term match
case Literal(constant) => s"Literal(${constant.show})"
case Ident(name) => s"Ident($name)"
case Apply(fun, args) =>
s"Apply(${analyzeTerm(fun)}, [${args.map(analyzeTerm).mkString(", ")}])"
case Select(qualifier, name) =>
s"Select(${analyzeTerm(qualifier)}, $name)"
case _ => s"Term(${term.show})"
val typeAnalysis = analyzeType(TypeRepr.of[T])
val termAnalysis = analyzeTerm(value.asTerm)
Expr(s"Type: $typeAnalysis, Structure: $termAnalysis")
val structure = showStructure(List(1, 2, 3).map(_ + 1))
// Detailed compile-time analysis of expression structureimport scala.quoted.*
inline def benchmark[T](inline iterations: Int, inline code: T): (T, Long) =
${ benchmarkImpl('iterations, 'code) }
def benchmarkImpl[T: Type](iterations: Expr[Int], code: Expr[T])(using Quotes): Expr[(T, Long)] =
'{
val startTime = System.nanoTime()
var result: T = null.asInstanceOf[T]
var i = 0
while i < $iterations do
result = $code
i += 1
val endTime = System.nanoTime()
(result, endTime - startTime)
}
val (result, timeNanos) = benchmark(1000000, math.sqrt(42.0))
println(s"Result: $result, Time: ${timeNanos}ns")import scala.quoted.*
inline def createTuple[T, U](x: T, y: U): (T, U) =
${ createTupleImpl('x, 'y) }
def createTupleImpl[T: Type, U: Type](x: Expr[T], y: Expr[U])(using Quotes): Expr[(T, U)] =
import quotes.reflect.*
// Get type representations
val tType = TypeRepr.of[T]
val uType = TypeRepr.of[U]
// Create tuple type
val tupleType = AppliedType(TypeRepr.of[(_, _)].typeSymbol.typeRef, List(tType, uType))
'{ ($x, $y) }
// Advanced: Generate code based on type structure
inline def processType[T]: String =
${ processTypeImpl[T] }
def processTypeImpl[T: Type](using Quotes): Expr[String] =
import quotes.reflect.*
val tpe = TypeRepr.of[T]
val processing = tpe match
case AppliedType(base, List(arg)) if base <:< TypeRepr.of[List[_]] =>
s"List processing for element type: ${arg.show}"
case AppliedType(base, List(k, v)) if base <:< TypeRepr.of[Map[_, _]] =>
s"Map processing for key: ${k.show}, value: ${v.show}"
case tpe if tpe <:< TypeRepr.of[Product] =>
s"Product type processing: ${tpe.show}"
case _ =>
s"Generic processing: ${tpe.show}"
Expr(processing)
val process1 = processType[List[Int]] // "List processing for element type: Int"
val process2 = processType[Map[String, Boolean]] // "Map processing for key: String, value: Boolean"The macro system in Scala 3 provides powerful compile-time metaprogramming capabilities, allowing developers to generate code, perform static analysis, and create domain-specific languages while maintaining type safety and performance.