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
- Inline Methods
- Macros Basics
- Quotes and Splices
- Practical Macros