Quotes と Splices
コードをデータとして扱います。
「Quotes と Splices」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。
コードをデータとして扱う
Scala 3のメタプログラミングでは、コードをデータとして扱います。クォートはコードの一部をExprとして取得し、スプライスはExprをコードに戻して挿入します。
クォート: '{ ... }
'{ expr }は式をExpr[T]に変換します。コードは実行されず、マクロが操作できる値になります。
import scala.quoted.*
def greeting(using Quotes): Expr[String] = '{ "Hello, " + "world" }スプライス: ${ ... }
${ expr }はExprを外側のクォートに挿入し、生成された断片をつなぎ合わせます。クォートとスプライスは逆の操作です。
import scala.quoted.*
def doubled(x: Expr[Int])(using Quotes): Expr[Int] = '{ ${ x } * 2 }リフトした値のスプライス
クォートの内部では、Expr(...)でリフトした値をスプライスして、コンパイル時定数を生成コードに埋め込めます。
import scala.quoted.*
def addN(x: Expr[Int], n: Int)(using Quotes): Expr[Int] =
'{ ${ x } + ${ Expr(n) } }コードを再帰的に構築する
マクロは、ループや再帰の中でクォートとスプライスを組み合わせ、複雑な式を構築します。たとえば、べき乗を繰り返しの乗算へ展開できます。
import scala.quoted.*
def pow(x: Expr[Double], n: Int)(using Quotes): Expr[Double] =
if (n == 0) '{ 1.0 } else '{ ${ x } * ${ pow(x, n - 1) } }クォートのパターンマッチング
クォートのパターンにマッチさせることで、コードを分解できます。これにより式の形状を調べ、最適化や書き換えを行えます。
import scala.quoted.*
def optimize(e: Expr[Int])(using Quotes): Expr[Int] = e match {
case '{ ($a: Int) + 0 } => a
case _ => e
}Type[T]とクォートされた型
Expr[T]が式を保持するのと同様に、Type[T]は型を保持します。'[T]で型をクォートし、型が必要な場所ではgiven Type[T]を使ってスプライスします。
import scala.quoted.*
def typeName[T](using t: Type[T], q: Quotes): Expr[String] =
Expr(Type.show[T])reflect API
より低レベルな処理では、quotes.reflectが抽象構文木(Term、Symbol、TypeRepr)を公開します。これにより、クォートだけでは直接表現できないコードも調査・構築できます。
import scala.quoted.*
def show(e: Expr[Any])(using q: Quotes): Expr[String] = {
import q.reflect.*
Expr(e.asTerm.show)
}レベル間の往復変換
asTermでExprをTermに変換し、asExpr / asExprOf[T]で戻せます。これにより、高レベルのクォートAPIとリフレクションAPIを橋渡しできます。
import scala.quoted.*
def ident[T: Type](e: Expr[T])(using q: Quotes): Expr[T] = {
import q.reflect.*
e.asTerm.asExprOf[T]
}実行時の例
先ほどの再帰的なべき乗マクロは、通常の乗算へ展開されます。この自己完結型の実行時バージョンは、生成されたコードが返す結果と同じ結果を示します。
object Main {
def pow(x: Double, n: Int): Double =
if (n == 0) 1.0 else x * pow(x, n - 1)
def main(args: Array[String]): Unit = {
println(pow(3.0, 2)) // 9.0
}
}クォートとスプライスは逆の操作
この双対性を覚えておきましょう。'{ }はコードをExprへ持ち上げ、${ }はExprをコードに戻します。これらを正しく入れ子にすることが、マクロ構築の核心です。
確認問題
クォートの内部でスプライス演算子${ x }は何をしますか?
まとめ
クォートとスプライスについて学びました。
'{ }はコードをExpr[T]にクォートします${ }はExprをコードにスプライスします- クォートのパターンマッチングでコードを分解します
Type[T]とreflectAPI
次は、実践的なマクロです。
よくある質問
「Quotes と Splices」レッスンは無料ですか?
はい。「Quotes と Splices」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。
「Quotes と Splices」で何を学びますか?
コードをデータとして扱います。 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「Quotes と Splices」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?
はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。