0Pricing
Scala for Backend Engineering & Functional Programming · レッスン

実践的なマクロ

ユースケースを学びます。

「実践的なマクロ」は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フィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. インラインメソッド
  2. マクロの基礎
  3. Quotes と Splices
  4. 実践的なマクロ
← Scala for Backend Engineering & Functional Programmingに戻る