0Pricing
Scala for Backend Engineering & Functional Programming · Урок

Практические макросы

Варианты применения

«Практические макросы» — бесплатный урок 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 — локальная установка не требуется.

Все уроки этого курса

  1. Встраиваемые методы
  2. Основы макросов
  3. Цитаты и вставки
  4. Практические макросы
← Назад к Scala for Backend Engineering & Functional Programming