or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

annotations.mdcompile-time.mdgeneric-programming.mdimmutable-arrays.mdindex.mdmacros.mdstructural-types.mdtuples.mdtype-safe-equality.md
tile.json

macros.mddocs/

Macro and Metaprogramming

The scala.quoted package provides Scala 3's powerful macro system using quotes and splices for compile-time code generation and analysis.

Core Types

Quotes Context

trait Quotes:
  // Provides access to compiler internals and reflection API
  type reflect: Reflection

The main context object providing access to the compiler's reflection API and utilities needed for macro programming.

Quoted Expressions

abstract class Expr[+T]:
  def show(using Quotes): String
  def value(using Quotes): T  // Only works for constant expressions
  def asTerm(using Quotes): reflect.Term

Represents quoted expressions that can be spliced into code. Expr[T] contains code that, when executed, produces a value of type T.

Quoted Types

abstract class Type[T <: AnyKind]:
  def show(using Quotes): String
  def asTerm(using Quotes): reflect.Term

Represents quoted types that can be used in type-level computations and spliced into contexts expecting types.

Conversion Type Classes

ToExpr - Value to Expression

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 types

Type class for converting runtime values to quoted expressions.

FromExpr - Expression to Value

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 types

Type class for extracting constant values from quoted expressions (when possible).

Utility Types

Variable Arguments

abstract class Varargs[T]:
  def apply(using Quotes): Seq[Expr[T]]

Handles variable arguments in macro contexts, providing access to a sequence of expressions.

Expression Transformation

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.

Quote and Splice Syntax

Basic Quote/Splice

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]]

Nested Quotes and Splices

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) }*) }

Usage Examples

Simple Macro Definition

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: 14

Conditional Code Generation

import 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 addition

Working with Types

import 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"

Expression Analysis

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"

Code Transformation

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)
}

Compile-Time Validation

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 error

Generic Macro Programming

import 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 structure

Macro with Multiple Expression Arguments

import 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")

Advanced Type Manipulation

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.