Reflection Library for the Scala Programming Language providing runtime reflection capabilities and metaprogramming support
—
Compile-time reflection contexts for macro development including blackbox and whitebox macro support. Macros enable code generation and transformation at compile time, providing powerful metaprogramming capabilities.
Context for blackbox macros with restricted compile-time capabilities.
/**
* Blackbox macro context providing compile-time reflection capabilities
*/
trait Context extends scala.reflect.macros.Aliases
with scala.reflect.macros.Enclosures
with scala.reflect.macros.Names
with scala.reflect.macros.Reifiers
with scala.reflect.macros.Typers
with scala.reflect.macros.Universe {
/** The universe for this macro context */
val universe: Universe
/** Mirror for this macro context */
val mirror: universe.Mirror
/** The prefix expression (receiver) for this macro call */
def prefix: Expr[_]
/** Create expression wrapper */
def Expr[T: WeakTypeTag](tree: universe.Tree): Expr[T]
/** Issue compilation error */
def error(pos: Position, msg: String): Unit
/** Issue compilation warning */
def warning(pos: Position, msg: String): Unit
/** Issue compilation info message */
def info(pos: Position, msg: String, force: Boolean): Unit
/** Abort compilation with error */
def abort(pos: Position, msg: String): Nothing
/** Fresh name generation */
def freshName(): String
def freshName(name: String): String
/** Enclosing position */
def enclosingPosition: Position
/** Macro application position */
def macroApplication: Tree
}Basic Blackbox Macro Example:
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
// Macro implementation
def debugImpl(c: Context)(expr: c.Expr[Any]): c.Expr[Any] = {
import c.universe._
val exprCode = show(expr.tree)
val debugTree = q"""
{
val result = $expr
println(s"Debug: $exprCode = " + result)
result
}
"""
c.Expr[Any](debugTree)
}
// Macro definition
def debug(expr: Any): Any = macro debugImpl
// Usage
debug(2 + 3) // Prints: Debug: 2 + 3 = 5
debug("hello".toUpperCase) // Prints: Debug: "hello".toUpperCase = HELLOContext for whitebox macros with expanded compile-time capabilities.
/**
* Whitebox macro context extending blackbox capabilities
*/
trait Context extends blackbox.Context {
// Whitebox macros can refine their return type beyond what's declared
// They have access to additional type information and can influence typing
}Whitebox Macro Example:
import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros
// Whitebox macro that can refine return types
def selectDynamicImpl(c: Context)(name: c.Expr[String]): c.Expr[Any] = {
import c.universe._
name.tree match {
case Literal(Constant(fieldName: String)) if fieldName == "length" =>
// Return refined type - whitebox can specify exact return type
c.Expr[Int](q"${c.prefix}.toString.length")
case Literal(Constant(fieldName: String)) if fieldName == "upper" =>
c.Expr[String](q"${c.prefix}.toString.toUpperCase")
case _ =>
c.abort(c.enclosingPosition, s"Unknown field: ${show(name.tree)}")
}
}
class DynamicAccessor(value: Any) {
def selectDynamic(name: String): Any = macro selectDynamicImpl
}
// Usage - whitebox can provide exact return types
val accessor = new DynamicAccessor("hello")
val len: Int = accessor.length // Refined to Int
val upper: String = accessor.upper // Refined to StringCommon operations and utilities available in macro contexts.
/**
* Macro context utilities and operations
*/
trait MacroUtils { this: Context =>
/** Type checking */
def typecheck(tree: Tree): Tree
def typecheck(tree: Tree, mode: TypecheckMode): Tree
def typecheck(tree: Tree, pt: Type): Tree
/** Untypecheck tree (remove type information) */
def untypecheck(tree: Tree): Tree
/** Parse string as Scala code */
def parse(code: String): Tree
/** Show tree as code */
def show(tree: Tree): String
/** Show raw tree structure */
def showRaw(tree: Tree): String
/** Show type */
def show(tpe: Type): String
/** Fresh term name */
def freshTermName(): TermName
def freshTermName(prefix: String): TermName
/** Fresh type name */
def freshTypeName(): TypeName
def freshTypeName(prefix: String): TypeName
/** Internal APIs */
val internal: Internal
}Macro Utilities Example:
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
def printTypeInfoImpl(c: Context)(expr: c.Expr[Any]): c.Expr[Unit] = {
import c.universe._
// Get type information
val exprType = expr.actualType
val exprTree = expr.tree
// Type checking
val typecheckedTree = c.typecheck(exprTree)
// Fresh names
val tempVar = c.freshTermName("temp")
// Generate debug info
val debugInfo = show(exprTree)
val typeInfo = show(exprType)
val rawInfo = showRaw(exprTree)
val result = q"""
{
val $tempVar = $expr
println("Expression: " + $debugInfo)
println("Type: " + $typeInfo)
println("Raw: " + $rawInfo)
println("Value: " + $tempVar)
}
"""
c.Expr[Unit](result)
}
def printTypeInfo(expr: Any): Unit = macro printTypeInfoImpl
// Usage
printTypeInfo(List(1, 2, 3))
printTypeInfo("hello".length)
printTypeInfo(Map("a" -> 1, "b" -> 2))String interpolation syntax for constructing and deconstructing ASTs.
/**
* Quasiquote string interpolators for AST construction
*/
object Quasiquotes {
/** Expression quasiquote */
implicit class QuasiquoteExpr(val sc: StringContext) {
def q(args: Any*): Tree = macro ???
}
/** Type quasiquote */
implicit class QuasiquoteType(val sc: StringContext) {
def tq(args: Any*): Tree = macro ???
}
/** Pattern quasiquote */
implicit class QuasiquotePattern(val sc: StringContext) {
def pq(args: Any*): Tree = macro ???
}
/** Case definition quasiquote */
implicit class QuasiquoteCaseDef(val sc: StringContext) {
def cq(args: Any*): Tree = macro ???
}
}Quasiquotes Example:
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
def loggerImpl(c: Context)(message: c.Expr[String]): c.Expr[Unit] = {
import c.universe._
// Using quasiquotes for AST construction
val loggerTree = q"""
{
val timestamp = java.time.LocalDateTime.now()
val logMessage = s"[$timestamp] ${$message}"
println(logMessage)
}
"""
c.Expr[Unit](loggerTree)
}
def measureTimeImpl(c: Context)(expr: c.Expr[Any]): c.Expr[Any] = {
import c.universe._
val resultName = TermName(c.freshName("result"))
val startName = TermName(c.freshName("start"))
val endName = TermName(c.freshName("end"))
// Quasiquote with variable substitution
val timerTree = q"""
{
val $startName = System.nanoTime()
val $resultName = $expr
val $endName = System.nanoTime()
val duration = ($endName - $startName) / 1000000.0
println(s"Execution time: $duration ms")
$resultName
}
"""
c.Expr[Any](timerTree)
}
def logger(message: String): Unit = macro loggerImpl
def measureTime[T](expr: T): T = macro measureTimeImpl
// Usage
logger("Application started")
val result = measureTime {
Thread.sleep(100)
"Hello World"
}Macros triggered by annotations for code transformation.
/**
* Annotation macro support
*/
class MacroAnnotation extends scala.annotation.StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro MacroAnnotation.impl
}
object MacroAnnotation {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
// Transform annotated code
val result = annottees.map(_.tree).toList match {
case (classDecl @ q"$mods class $className(..$params) extends ..$parents { ..$body }") :: Nil =>
// Transform class
q"""
$mods class $className(..$params) extends ..$parents {
..$body
def generatedMethod: String = "Generated by macro annotation"
}
"""
case _ => c.abort(c.enclosingPosition, "Annotation can only be applied to classes")
}
c.Expr[Any](result)
}
}Annotation Macro Example:
import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros
import scala.annotation.{StaticAnnotation, compileTimeOnly}
@compileTimeOnly("enable macro paradise to expand macro annotations")
class toString extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro toString.impl
}
object toString {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
def generateToString(className: TypeName, params: List[ValDef]): DefDef = {
val fieldAccess = params.map { param =>
val name = param.name.toString
q"$name + '=' + ${param.name}"
}
val toStringBody = if (fieldAccess.nonEmpty) {
fieldAccess.reduce((acc, field) => q"$acc + ', ' + $field")
} else {
q"''"
}
q"override def toString: String = ${className.toString} + '(' + $toStringBody + ')'"
}
val result = annottees.map(_.tree).toList match {
case (classDecl @ q"$mods class $className(..$params) extends ..$parents { ..$body }") :: Nil =>
val toStringMethod = generateToString(className, params)
q"""
$mods class $className(..$params) extends ..$parents {
..$body
$toStringMethod
}
"""
case _ => c.abort(c.enclosingPosition, "Can only annotate classes")
}
c.Expr[Any](result)
}
}
// Usage
@toString
class Person(name: String, age: Int)
val person = new Person("Alice", 25)
println(person) // Person(name=Alice, age=25)Complex macro patterns for advanced metaprogramming.
/**
* Advanced macro development patterns
*/
object AdvancedMacroPatterns {
/** Type-level computation macro */
def typeComputation[T](implicit c: Context, tag: c.WeakTypeTag[T]): c.Expr[String]
/** Compile-time reflection macro */
def reflectiveAccess(obj: Any, methodName: String): Any
/** Code generation macro */
def generateClass(className: String, fields: (String, String)*): Any
/** Validation macro */
def validate[T](expr: T): T
/** Serialization macro */
def serialize[T: Context#WeakTypeTag](obj: T): String
}Complete Macro Development Example:
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
// Comprehensive macro example with multiple features
object ComprehensiveMacros {
// 1. Debug macro with type information
def debugWithTypeImpl(c: Context)(expr: c.Expr[Any]): c.Expr[Any] = {
import c.universe._
val exprType = expr.actualType
val exprString = show(expr.tree)
val typeString = show(exprType)
val pos = expr.tree.pos
q"""
{
val result = $expr
println(s"[${${pos.toString}}] $exprString: $typeString = " + result)
result
}
"""
}
// 2. Assertion macro with custom messages
def assertImpl(c: Context)(condition: c.Expr[Boolean], message: c.Expr[String]): c.Expr[Unit] = {
import c.universe._
val conditionString = show(condition.tree)
q"""
if (!$condition) {
throw new AssertionError(s"Assertion failed: $conditionString - " + $message)
}
"""
}
// 3. Performance measurement macro
def benchmarkImpl(c: Context)(name: c.Expr[String])(expr: c.Expr[Any]): c.Expr[Any] = {
import c.universe._
val resultVar = TermName(c.freshName("result"))
val startVar = TermName(c.freshName("start"))
val endVar = TermName(c.freshName("end"))
q"""
{
println(s"Starting benchmark: " + $name)
val $startVar = System.nanoTime()
val $resultVar = $expr
val $endVar = System.nanoTime()
val duration = ($endVar - $startVar) / 1000000.0
println(s"Completed benchmark: " + $name + s" in $duration ms")
$resultVar
}
"""
}
// 4. Compile-time string processing
def processStringImpl(c: Context)(str: c.Expr[String]): c.Expr[String] = {
import c.universe._
str.tree match {
case Literal(Constant(s: String)) =>
// Process string at compile time
val processed = s.toUpperCase.reverse
c.Expr[String](Literal(Constant(processed)))
case _ =>
c.abort(c.enclosingPosition, "String must be a literal")
}
}
// Macro definitions
def debugWithType(expr: Any): Any = macro debugWithTypeImpl
def assert(condition: Boolean, message: String): Unit = macro assertImpl
def benchmark[T](name: String)(expr: T): T = macro benchmarkImpl
def processString(str: String): String = macro processStringImpl
}
// Usage examples
import ComprehensiveMacros._
// Debug with type information
val list = debugWithType(List(1, 2, 3).map(_ * 2))
// Custom assertions
assert(list.length == 3, "List should have 3 elements")
// Performance measurement
val result = benchmark("List processing") {
(1 to 1000000).toList.filter(_ % 2 == 0).sum
}
// Compile-time string processing
val processed = processString("hello") // Becomes "OLLEH" at compile time
println(processed)
// Error handling in macros
def safeDebugImpl(c: Context)(expr: c.Expr[Any]): c.Expr[Any] = {
import c.universe._
try {
val result = q"""
{
val value = $expr
println(s"Value: " + value)
value
}
"""
c.Expr[Any](result)
} catch {
case ex: Exception =>
c.error(c.enclosingPosition, s"Macro expansion failed: ${ex.getMessage}")
expr
}
}
def safeDebug(expr: Any): Any = macro safeDebugImplInstall with Tessl CLI
npx tessl i tessl/maven-org-scala-lang--scala-reflect