0Pricing
Scala for Backend Engineering & Functional Programming · Lesson

Practical Macros

Use cases.

Practical Macros is a free Scala for Backend Engineering & Functional Programming lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Scala for Backend Engineering & Functional Programming learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Macros in the Real World

Beyond toy examples, macros solve concrete problems: capturing source context, deriving type classes, validating literals, and generating boilerplate-free code.

Use Case: assert with Message

A macro can capture the source text of a failing condition to produce a helpful assertion message automatically.

import scala.quoted.*

inline def myAssert(inline cond: Boolean): Unit =
  ${ assertImpl('cond) }

Capturing Source Code

Inside the macro, cond.show (via the reflect API) yields the literal source of the expression, which you splice into the error message.

import scala.quoted.*

def assertImpl(cond: Expr[Boolean])(using q: Quotes): Expr[Unit] = {
  import q.reflect.*
  val src = Expr(cond.asTerm.show)
  '{ if (!${ cond }) throw new AssertionError("failed: " + ${ src }) }
}

Use Case: Compile-Time Validation

Validate a literal (such as a regex or port number) at compile time so invalid values never reach runtime.

import scala.quoted.*

inline def port(inline n: Int): Int = ${ portImpl('n) }

Aborting on Bad Input

Extract the literal, check it, and call report.errorAndAbort for invalid values — turning a runtime bug into a compile error.

import scala.quoted.*

def portImpl(n: Expr[Int])(using q: Quotes): Expr[Int] = {
  import q.reflect.*
  val v = n.valueOrAbort
  if (v < 1 || v > 65535) report.errorAndAbort("invalid port")
  n
}

Use Case: Typeclass Derivation

Macros (often via inline + scala.deriving.Mirror) can generate type class instances like JSON encoders for any case class with zero boilerplate.

import scala.deriving.Mirror

trait Show[T] { def show(t: T): String }

inline def derived[T](using Mirror.Of[T]): Show[T] = ???

Using Mirror

A Mirror exposes a product's field types and labels at compile time. Combined with inline, you iterate over fields to build an instance.

import scala.deriving.*
import scala.compiletime.*

inline def labels[T](using m: Mirror.ProductOf[T]): List[String] =
  constValueTuple[m.MirroredElemLabels].toList.map(_.toString)

Use Case: Logging with Position

A macro can read Position from the reflect API to attach file and line number to log messages with no runtime cost.

import scala.quoted.*

def posImpl(using q: Quotes): Expr[String] = {
  import q.reflect.*
  val p = Position.ofMacroExpansion
  Expr(s"${p.sourceFile.name}:${p.startLine + 1}")
}

When NOT to Use Macros

Macros are powerful but costly to maintain and debug. Prefer plain functions, inline methods, or given derivation first. Reach for macros only when those cannot express the need.

Testing Macros

Test the generated behavior like ordinary code, and test that invalid inputs fail to compile using tools such as typeCheckErrors from the compile-time test utilities.

Runtime Analog

A compile-time validation macro produces the same effect as this self-contained runtime check — but the failure happens during compilation instead of at run time.

object Main {
  def port(n: Int): Int = {
    require(n >= 1 && n <= 65535, "invalid port")
    n
  }
  def main(args: Array[String]): Unit = {
    println(port(8080)) // 8080
  }
}

Quick Check

What is the main advantage of validating a literal with a macro instead of at runtime?

Recap

You explored practical macro use cases:

  • source-capturing assert
  • compile-time validation with errorAndAbort
  • typeclass derivation via Mirror
  • position-aware logging
  • when to avoid macros and how to test them

You've completed the Metaprogramming and Macros course.

Frequently asked questions

Is the “Practical Macros” lesson free?

Yes — the full text of “Practical Macros” is free to read here on the web, and the Scala for Backend Engineering & Functional Programming course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Scala for Backend Engineering & Functional Programming course, upgrade to CoddyKit PRO.

What will I learn in “Practical Macros”?

Use cases. You practise Scala for Backend Engineering & Functional Programming with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Scala for Backend Engineering & Functional Programming?

No prior experience is required. Scala for Backend Engineering & Functional Programming on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Practical Macros” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Scala for Backend Engineering & Functional Programming lesson?

Yes. Every Scala for Backend Engineering & Functional Programming lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Inline Methods
  2. Macros Basics
  3. Quotes and Splices
  4. Practical Macros
← Back to Scala for Backend Engineering & Functional Programming