or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

abstract-syntax-trees.mdexpressions-reification.mdindex.mdmacro-development.mdmirrors-reflective-operations.mdruntime-reflection.mdsymbol-system.mdtype-system.mdtype-tags-type-information.md

macro-development.mddocs/

0

# Macro Development

1

2

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.

3

4

## Capabilities

5

6

### Blackbox Context

7

8

Context for blackbox macros with restricted compile-time capabilities.

9

10

```scala { .api }

11

/**

12

* Blackbox macro context providing compile-time reflection capabilities

13

*/

14

trait Context extends scala.reflect.macros.Aliases

15

with scala.reflect.macros.Enclosures

16

with scala.reflect.macros.Names

17

with scala.reflect.macros.Reifiers

18

with scala.reflect.macros.Typers

19

with scala.reflect.macros.Universe {

20

21

/** The universe for this macro context */

22

val universe: Universe

23

24

/** Mirror for this macro context */

25

val mirror: universe.Mirror

26

27

/** The prefix expression (receiver) for this macro call */

28

def prefix: Expr[_]

29

30

/** Create expression wrapper */

31

def Expr[T: WeakTypeTag](tree: universe.Tree): Expr[T]

32

33

/** Issue compilation error */

34

def error(pos: Position, msg: String): Unit

35

36

/** Issue compilation warning */

37

def warning(pos: Position, msg: String): Unit

38

39

/** Issue compilation info message */

40

def info(pos: Position, msg: String, force: Boolean): Unit

41

42

/** Abort compilation with error */

43

def abort(pos: Position, msg: String): Nothing

44

45

/** Fresh name generation */

46

def freshName(): String

47

def freshName(name: String): String

48

49

/** Enclosing position */

50

def enclosingPosition: Position

51

52

/** Macro application position */

53

def macroApplication: Tree

54

}

55

```

56

57

**Basic Blackbox Macro Example:**

58

59

```scala

60

import scala.reflect.macros.blackbox.Context

61

import scala.language.experimental.macros

62

63

// Macro implementation

64

def debugImpl(c: Context)(expr: c.Expr[Any]): c.Expr[Any] = {

65

import c.universe._

66

67

val exprCode = show(expr.tree)

68

val debugTree = q"""

69

{

70

val result = $expr

71

println(s"Debug: $exprCode = " + result)

72

result

73

}

74

"""

75

76

c.Expr[Any](debugTree)

77

}

78

79

// Macro definition

80

def debug(expr: Any): Any = macro debugImpl

81

82

// Usage

83

debug(2 + 3) // Prints: Debug: 2 + 3 = 5

84

debug("hello".toUpperCase) // Prints: Debug: "hello".toUpperCase = HELLO

85

```

86

87

### Whitebox Context

88

89

Context for whitebox macros with expanded compile-time capabilities.

90

91

```scala { .api }

92

/**

93

* Whitebox macro context extending blackbox capabilities

94

*/

95

trait Context extends blackbox.Context {

96

// Whitebox macros can refine their return type beyond what's declared

97

// They have access to additional type information and can influence typing

98

}

99

```

100

101

**Whitebox Macro Example:**

102

103

```scala

104

import scala.reflect.macros.whitebox.Context

105

import scala.language.experimental.macros

106

107

// Whitebox macro that can refine return types

108

def selectDynamicImpl(c: Context)(name: c.Expr[String]): c.Expr[Any] = {

109

import c.universe._

110

111

name.tree match {

112

case Literal(Constant(fieldName: String)) if fieldName == "length" =>

113

// Return refined type - whitebox can specify exact return type

114

c.Expr[Int](q"${c.prefix}.toString.length")

115

case Literal(Constant(fieldName: String)) if fieldName == "upper" =>

116

c.Expr[String](q"${c.prefix}.toString.toUpperCase")

117

case _ =>

118

c.abort(c.enclosingPosition, s"Unknown field: ${show(name.tree)}")

119

}

120

}

121

122

class DynamicAccessor(value: Any) {

123

def selectDynamic(name: String): Any = macro selectDynamicImpl

124

}

125

126

// Usage - whitebox can provide exact return types

127

val accessor = new DynamicAccessor("hello")

128

val len: Int = accessor.length // Refined to Int

129

val upper: String = accessor.upper // Refined to String

130

```

131

132

### Macro Utilities and Context Operations

133

134

Common operations and utilities available in macro contexts.

135

136

```scala { .api }

137

/**

138

* Macro context utilities and operations

139

*/

140

trait MacroUtils { this: Context =>

141

/** Type checking */

142

def typecheck(tree: Tree): Tree

143

def typecheck(tree: Tree, mode: TypecheckMode): Tree

144

def typecheck(tree: Tree, pt: Type): Tree

145

146

/** Untypecheck tree (remove type information) */

147

def untypecheck(tree: Tree): Tree

148

149

/** Parse string as Scala code */

150

def parse(code: String): Tree

151

152

/** Show tree as code */

153

def show(tree: Tree): String

154

155

/** Show raw tree structure */

156

def showRaw(tree: Tree): String

157

158

/** Show type */

159

def show(tpe: Type): String

160

161

/** Fresh term name */

162

def freshTermName(): TermName

163

def freshTermName(prefix: String): TermName

164

165

/** Fresh type name */

166

def freshTypeName(): TypeName

167

def freshTypeName(prefix: String): TypeName

168

169

/** Internal APIs */

170

val internal: Internal

171

}

172

```

173

174

**Macro Utilities Example:**

175

176

```scala

177

import scala.reflect.macros.blackbox.Context

178

import scala.language.experimental.macros

179

180

def printTypeInfoImpl(c: Context)(expr: c.Expr[Any]): c.Expr[Unit] = {

181

import c.universe._

182

183

// Get type information

184

val exprType = expr.actualType

185

val exprTree = expr.tree

186

187

// Type checking

188

val typecheckedTree = c.typecheck(exprTree)

189

190

// Fresh names

191

val tempVar = c.freshTermName("temp")

192

193

// Generate debug info

194

val debugInfo = show(exprTree)

195

val typeInfo = show(exprType)

196

val rawInfo = showRaw(exprTree)

197

198

val result = q"""

199

{

200

val $tempVar = $expr

201

println("Expression: " + $debugInfo)

202

println("Type: " + $typeInfo)

203

println("Raw: " + $rawInfo)

204

println("Value: " + $tempVar)

205

}

206

"""

207

208

c.Expr[Unit](result)

209

}

210

211

def printTypeInfo(expr: Any): Unit = macro printTypeInfoImpl

212

213

// Usage

214

printTypeInfo(List(1, 2, 3))

215

printTypeInfo("hello".length)

216

printTypeInfo(Map("a" -> 1, "b" -> 2))

217

```

218

219

### Quasiquotes

220

221

String interpolation syntax for constructing and deconstructing ASTs.

222

223

```scala { .api }

224

/**

225

* Quasiquote string interpolators for AST construction

226

*/

227

object Quasiquotes {

228

/** Expression quasiquote */

229

implicit class QuasiquoteExpr(val sc: StringContext) {

230

def q(args: Any*): Tree = macro ???

231

}

232

233

/** Type quasiquote */

234

implicit class QuasiquoteType(val sc: StringContext) {

235

def tq(args: Any*): Tree = macro ???

236

}

237

238

/** Pattern quasiquote */

239

implicit class QuasiquotePattern(val sc: StringContext) {

240

def pq(args: Any*): Tree = macro ???

241

}

242

243

/** Case definition quasiquote */

244

implicit class QuasiquoteCaseDef(val sc: StringContext) {

245

def cq(args: Any*): Tree = macro ???

246

}

247

}

248

```

249

250

**Quasiquotes Example:**

251

252

```scala

253

import scala.reflect.macros.blackbox.Context

254

import scala.language.experimental.macros

255

256

def loggerImpl(c: Context)(message: c.Expr[String]): c.Expr[Unit] = {

257

import c.universe._

258

259

// Using quasiquotes for AST construction

260

val loggerTree = q"""

261

{

262

val timestamp = java.time.LocalDateTime.now()

263

val logMessage = s"[$timestamp] ${$message}"

264

println(logMessage)

265

}

266

"""

267

268

c.Expr[Unit](loggerTree)

269

}

270

271

def measureTimeImpl(c: Context)(expr: c.Expr[Any]): c.Expr[Any] = {

272

import c.universe._

273

274

val resultName = TermName(c.freshName("result"))

275

val startName = TermName(c.freshName("start"))

276

val endName = TermName(c.freshName("end"))

277

278

// Quasiquote with variable substitution

279

val timerTree = q"""

280

{

281

val $startName = System.nanoTime()

282

val $resultName = $expr

283

val $endName = System.nanoTime()

284

val duration = ($endName - $startName) / 1000000.0

285

println(s"Execution time: $duration ms")

286

$resultName

287

}

288

"""

289

290

c.Expr[Any](timerTree)

291

}

292

293

def logger(message: String): Unit = macro loggerImpl

294

def measureTime[T](expr: T): T = macro measureTimeImpl

295

296

// Usage

297

logger("Application started")

298

val result = measureTime {

299

Thread.sleep(100)

300

"Hello World"

301

}

302

```

303

304

### Annotation Macros

305

306

Macros triggered by annotations for code transformation.

307

308

```scala { .api }

309

/**

310

* Annotation macro support

311

*/

312

class MacroAnnotation extends scala.annotation.StaticAnnotation {

313

def macroTransform(annottees: Any*): Any = macro MacroAnnotation.impl

314

}

315

316

object MacroAnnotation {

317

def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {

318

import c.universe._

319

320

// Transform annotated code

321

val result = annottees.map(_.tree).toList match {

322

case (classDecl @ q"$mods class $className(..$params) extends ..$parents { ..$body }") :: Nil =>

323

// Transform class

324

q"""

325

$mods class $className(..$params) extends ..$parents {

326

..$body

327

def generatedMethod: String = "Generated by macro annotation"

328

}

329

"""

330

case _ => c.abort(c.enclosingPosition, "Annotation can only be applied to classes")

331

}

332

333

c.Expr[Any](result)

334

}

335

}

336

```

337

338

**Annotation Macro Example:**

339

340

```scala

341

import scala.reflect.macros.whitebox.Context

342

import scala.language.experimental.macros

343

import scala.annotation.{StaticAnnotation, compileTimeOnly}

344

345

@compileTimeOnly("enable macro paradise to expand macro annotations")

346

class toString extends StaticAnnotation {

347

def macroTransform(annottees: Any*): Any = macro toString.impl

348

}

349

350

object toString {

351

def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {

352

import c.universe._

353

354

def generateToString(className: TypeName, params: List[ValDef]): DefDef = {

355

val fieldAccess = params.map { param =>

356

val name = param.name.toString

357

q"$name + '=' + ${param.name}"

358

}

359

360

val toStringBody = if (fieldAccess.nonEmpty) {

361

fieldAccess.reduce((acc, field) => q"$acc + ', ' + $field")

362

} else {

363

q"''"

364

}

365

366

q"override def toString: String = ${className.toString} + '(' + $toStringBody + ')'"

367

}

368

369

val result = annottees.map(_.tree).toList match {

370

case (classDecl @ q"$mods class $className(..$params) extends ..$parents { ..$body }") :: Nil =>

371

val toStringMethod = generateToString(className, params)

372

q"""

373

$mods class $className(..$params) extends ..$parents {

374

..$body

375

$toStringMethod

376

}

377

"""

378

case _ => c.abort(c.enclosingPosition, "Can only annotate classes")

379

}

380

381

c.Expr[Any](result)

382

}

383

}

384

385

// Usage

386

@toString

387

class Person(name: String, age: Int)

388

389

val person = new Person("Alice", 25)

390

println(person) // Person(name=Alice, age=25)

391

```

392

393

### Advanced Macro Patterns

394

395

Complex macro patterns for advanced metaprogramming.

396

397

```scala { .api }

398

/**

399

* Advanced macro development patterns

400

*/

401

object AdvancedMacroPatterns {

402

/** Type-level computation macro */

403

def typeComputation[T](implicit c: Context, tag: c.WeakTypeTag[T]): c.Expr[String]

404

405

/** Compile-time reflection macro */

406

def reflectiveAccess(obj: Any, methodName: String): Any

407

408

/** Code generation macro */

409

def generateClass(className: String, fields: (String, String)*): Any

410

411

/** Validation macro */

412

def validate[T](expr: T): T

413

414

/** Serialization macro */

415

def serialize[T: Context#WeakTypeTag](obj: T): String

416

}

417

```

418

419

**Complete Macro Development Example:**

420

421

```scala

422

import scala.reflect.macros.blackbox.Context

423

import scala.language.experimental.macros

424

425

// Comprehensive macro example with multiple features

426

object ComprehensiveMacros {

427

428

// 1. Debug macro with type information

429

def debugWithTypeImpl(c: Context)(expr: c.Expr[Any]): c.Expr[Any] = {

430

import c.universe._

431

432

val exprType = expr.actualType

433

val exprString = show(expr.tree)

434

val typeString = show(exprType)

435

val pos = expr.tree.pos

436

437

q"""

438

{

439

val result = $expr

440

println(s"[${${pos.toString}}] $exprString: $typeString = " + result)

441

result

442

}

443

"""

444

}

445

446

// 2. Assertion macro with custom messages

447

def assertImpl(c: Context)(condition: c.Expr[Boolean], message: c.Expr[String]): c.Expr[Unit] = {

448

import c.universe._

449

450

val conditionString = show(condition.tree)

451

452

q"""

453

if (!$condition) {

454

throw new AssertionError(s"Assertion failed: $conditionString - " + $message)

455

}

456

"""

457

}

458

459

// 3. Performance measurement macro

460

def benchmarkImpl(c: Context)(name: c.Expr[String])(expr: c.Expr[Any]): c.Expr[Any] = {

461

import c.universe._

462

463

val resultVar = TermName(c.freshName("result"))

464

val startVar = TermName(c.freshName("start"))

465

val endVar = TermName(c.freshName("end"))

466

467

q"""

468

{

469

println(s"Starting benchmark: " + $name)

470

val $startVar = System.nanoTime()

471

val $resultVar = $expr

472

val $endVar = System.nanoTime()

473

val duration = ($endVar - $startVar) / 1000000.0

474

println(s"Completed benchmark: " + $name + s" in $duration ms")

475

$resultVar

476

}

477

"""

478

}

479

480

// 4. Compile-time string processing

481

def processStringImpl(c: Context)(str: c.Expr[String]): c.Expr[String] = {

482

import c.universe._

483

484

str.tree match {

485

case Literal(Constant(s: String)) =>

486

// Process string at compile time

487

val processed = s.toUpperCase.reverse

488

c.Expr[String](Literal(Constant(processed)))

489

case _ =>

490

c.abort(c.enclosingPosition, "String must be a literal")

491

}

492

}

493

494

// Macro definitions

495

def debugWithType(expr: Any): Any = macro debugWithTypeImpl

496

def assert(condition: Boolean, message: String): Unit = macro assertImpl

497

def benchmark[T](name: String)(expr: T): T = macro benchmarkImpl

498

def processString(str: String): String = macro processStringImpl

499

}

500

501

// Usage examples

502

import ComprehensiveMacros._

503

504

// Debug with type information

505

val list = debugWithType(List(1, 2, 3).map(_ * 2))

506

507

// Custom assertions

508

assert(list.length == 3, "List should have 3 elements")

509

510

// Performance measurement

511

val result = benchmark("List processing") {

512

(1 to 1000000).toList.filter(_ % 2 == 0).sum

513

}

514

515

// Compile-time string processing

516

val processed = processString("hello") // Becomes "OLLEH" at compile time

517

println(processed)

518

519

// Error handling in macros

520

def safeDebugImpl(c: Context)(expr: c.Expr[Any]): c.Expr[Any] = {

521

import c.universe._

522

523

try {

524

val result = q"""

525

{

526

val value = $expr

527

println(s"Value: " + value)

528

value

529

}

530

"""

531

c.Expr[Any](result)

532

} catch {

533

case ex: Exception =>

534

c.error(c.enclosingPosition, s"Macro expansion failed: ${ex.getMessage}")

535

expr

536

}

537

}

538

539

def safeDebug(expr: Any): Any = macro safeDebugImpl

540

```