0Pricing
Scala for Backend Engineering & Functional Programming · 课时

实用宏

应用场景

实用宏 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

宏在实际开发中的应用

宏不只是用于玩具示例,还能解决具体问题:捕获源代码上下文、派生类型类、验证字面量,以及生成无需样板代码的代码。

用例:带消息的断言

宏可以捕获失败条件的源代码文本,自动生成有帮助的断言消息。

import scala.quoted.*

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

捕获源代码

在宏内部,cond.show(通过反射 API)会返回表达式的字面源代码,您可以将其拼接到错误消息中。

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

用例:编译时验证

在编译时验证字面量(例如正则表达式或端口号),这样无效值就不会进入运行时。

import scala.quoted.*

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

对无效输入中止处理

提取字面量并检查它;对于无效值,调用 report.errorAndAbort,将运行时错误转变为编译错误。

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
}

用例:类型类派生

宏(通常通过 inline + scala.deriving.Mirror)可以为任意样例类生成 JSON 编码器等类型类实例,完全无需样板代码。

import scala.deriving.Mirror

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

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

使用 Mirror

Mirror 会在编译时公开乘积类型的字段类型和标签。结合 inline,您可以遍历字段来构建实例。

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

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

用例:带位置信息的日志记录

宏可以从反射 API 读取 Position,将文件和行号附加到日志消息中,且不会产生运行时开销。

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

不应使用宏的情况

宏功能强大,但维护和调试成本较高。请优先使用普通函数、内联方法或 given 派生。只有在这些方式无法表达需求时,才应使用宏。

测试宏

像测试普通代码一样测试生成的行为,并使用编译时测试工具中的 typeCheckErrors 等工具,测试无效输入是否无法通过编译。

运行时对应实现

编译时验证宏产生的效果与这个自包含的运行时检查相同,但失败会发生在编译期间,而不是运行时。

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

快速检查

与在运行时验证相比,使用宏验证字面量的主要优势是什么?

回顾

您探索了宏的实际应用场景:

  • 捕获源代码的 assert
  • 使用 errorAndAbort 进行编译时验证
  • 通过 Mirror 派生类型类
  • 记录带位置信息的日志
  • 何时避免使用宏,以及如何测试宏

您已完成“元编程与宏”课程。

常见问题解答

「实用宏」课时是免费的吗?

是的 — 「实用宏」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

「实用宏」这节课中我会学到什么?

应用场景 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「实用宏」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Scala for Backend Engineering & Functional Programming 课中编写并运行代码吗?

能。每节 Scala for Backend Engineering & Functional Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 内联方法
  2. 宏基础
  3. 引号与拼接
  4. 实用宏
← 返回 Scala for Backend Engineering & Functional Programming