Практические макросы
Варианты применения
«Практические макросы» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Scala for Backend Engineering & Functional Programming, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
Макросы в реальных проектах
Помимо учебных примеров, макросы решают конкретные задачи: сохраняют контекст исходного кода, автоматически выводят экземпляры классов типов, проверяют литералы и генерируют код без шаблонного наполнения.
Пример применения: assert с сообщением
Макрос может захватить текст исходного кода неудачного условия и автоматически сформировать информативное сообщение об ошибке утверждения.
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)Пример применения: ведение журнала с позицией
Макрос может получить Position через API отражения и добавить к сообщениям журнала имя файла и номер строки без затрат во время выполнения.
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}")
}Когда НЕ следует использовать макросы
Макросы мощны, но требуют больших усилий для сопровождения и отладки. Сначала предпочитайте обычные функции, методы inline или выведение через 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 - ведение журнала с указанием позиции
- случаи, когда макросов следует избегать, и способы их тестирования
Вы завершили курс «Метапрограммирование и макросы».
Часто задаваемые вопросы
Урок «Практические макросы» бесплатный?
Да — полный текст урока «Практические макросы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
Чему я научусь в уроке «Практические макросы»?
Варианты применения Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?
Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Практические макросы»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?
Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Встраиваемые методы
- Основы макросов
- Цитаты и вставки
- Практические макросы