Inline and Metaprogramming
Prerequisites: Type classes
Scala 3 can run code at compile time, producing faster or safer programs.
inline
An inline method is expanded at each call site. Combined with inline if
and constant arguments, branches are resolved during compilation:
inline val debugEnabled = false
inline def debug(inline msg: String): Unit =
inline if debugEnabled then println(s"[debug] $msg")
def expensive(): String =
println("computing...")
"details"
@main def inlining(): Unit =
debug(s"state: ${expensive()}")
println("done")
done
Because debugEnabled is false, the whole debug call — including
expensive() — is removed from the compiled code.
Compile-time checks
import scala.compiletime.{error, constValue}
inline def percentage(inline value: Int): Int =
inline if value < 0 || value > 100 then error("percentage must be between 0 and 100")
else value
@main def checks(): Unit =
println(percentage(42))
println(constValue[3] + 1)
42
4
import scala.compiletime.error
inline def percentage(inline value: Int): Int =
inline if value < 0 || value > 100 then error("percentage must be between 0 and 100")
else value
@main def badCheck(): Unit =
println(percentage(150))
-- Error: Main.scala:8:20 ------------------------------------------------------
8 | println(percentage(150))
| ^^^^^^^^^^^^^^^
| percentage must be between 0 and 100
1 error found
The invalid literal is rejected when compiling, with your message.
Deriving with Mirror
Mirror gives compile-time access to a case class's structure — the
foundation of derives:
import scala.deriving.Mirror
import scala.compiletime.constValueTuple
inline def fieldNames[A](using m: Mirror.ProductOf[A]): List[String] =
constValueTuple[m.MirroredElemLabels].toList.map(_.toString)
case class User(id: Long, name: String, email: String)
@main def mirrors(): Unit =
println(fieldNames[User])
List(id, name, email)
Macros
For full control, Scala 3 macros use quotes ('{ ... }) and splices
(${ ... }) to generate code from typed syntax trees. They're powerful but
complex — reach for inline, Mirror, and derives first. Libraries use
macros for things like compile-time SQL checking and JSON codecs.