実践的なマクロ
ユースケースを学びます。
「実践的なマクロ」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これは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(reflect 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エンコーダーのような型クラスのインスタンスを、ボイラープレートなしで任意のcase class向けに生成できます。
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)ユースケース: 位置情報付きロギング
マクロはreflect 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}")
}マクロを使わない場合
マクロは強力ですが、保守とデバッグにコストがかかります。まずは通常の関数、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時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。
「実践的なマクロ」で何を学びますか?
ユースケースを学びます。 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応の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フィードバックを取得できます。ローカル設定は不要です。