メタプログラミング
メタプログラミング (英語: metaprogramming) [注釈 1]とはプログラミング技法の一種で、一般に「プログラムを記述するプログラム」を書くこと、またはそのプログラムを指す[1]。対象言語に埋め込まれたマクロ言語によって行われることもある。
概要
一般に、スクリプト言語はメタプログラミングが得意だとされている。コンパイル型言語は実行前にソースコードを一括で変換するという特性上、翻訳と実行を繰り返すスクリプト言語よりも実行時の割り込みや変換の自由度が低い[2]。
代表的なメタプログラミングの例はLISPのマクロである。LISPではデータ、コードが全てS式で表現されるが、マクロによりS式が言語処理系に解釈される前に別なS式へと変換することができる。これにより例えば、
(defstruct point (x 0) (y 0))
という記述から
- 構造体定義 point型
- コンストラクタ make-point (省略時の初期値は0, 0)
- アクセサ point-x point-y
- 複製 copy-point
- 述語 point-p
が自動的に生成される。
これがメタプログラミングと呼ばれるのは、「自動生成が言語組み込みの機能ではなくLISPのマクロによって記述されており、必要なら同様の機構をプログラマが定義できる」ためである。これは事実上言語文法の拡張に等しく、非常に強力なプログラム能力を得ることになる。反面、マクロは本来の文法を逸脱した字句の置き換えであるため、コードが「記述どおりでない」動作を行うことを意味している。そのため一般に必要でないメタプログラミングは避けられるべきとされる。
たとえば、文字列を整形して出力するformat関数
(format t "hello,world")
を用いた例[3]を紹介する。LispのS式がLispフォームになるためには、「S式の最初の要素は(関数、マクロ、特殊フォーム)のいずれかではならない。」しかし、
(defmacro backwards (expr) (reverse expr))
を定義すると、以下のように書ける:
(backwards ("hello,world" t format))
このことは、Lispのマクロが、上のかぎ括弧「」で括った言語仕様を変更し、独自の文法を作り上げたとも考えられる。しかし、この文法は、通常のLispにおいて期待されるような構成をしていない「記述どおりでない動作を行うプログラム」の一例であり、コードの可読性を損なう恐れのある不必要なメタプログラミングである。
メタプログラミングの他の例としてはC++における「テンプレートメタプログラミング」などが挙げられる。
危険性
ただ、メタプログラミングが強力な手段である以上、それに伴う危険性も理解しておかねばならない。
次はJavaScriptにおけるメタプログラミングの例である。
const add = new Function(..."xy", "return x + y");
add(2, 3); // => 5
この例では、文字列から関数addを生成したうえで、その関数を利用して計算を行っている[注釈 2]。
ただ、このような野放図な使い方をすると、Functionコンストラクタに与える引数を打ち間違えただけで破壊的かつ致命的な結果を引き起こす場合がある。
そこでJavaScriptでは、「既存の機能を拡張する」ことに注力した、
- Proxy
- Reflect
というオブジェクトを提供している[4]。このように、目的ありきの手段として使用することにより、危険性を最小限にまで抑えながら強みを最大化することができる。
脚注
- ^ あんどうやすし 2020, p. 343.
- ^ あんどうやすし 2020, pp. 343–344.
- ^ Peter Siebel: Practical Common Lisp 第三章の8から引用
- ^ あんどうやすし 2020, p. 345-346.
参考文献
- あんどうやすし「メタプログラミングを学ぶ」『ハンズオンJavaScript』(初版)オライリー・ジャパン、東京、2020年11月13日。ISBN 978-4-87311-922-9。
関連項目
- Common Lisp
- Scheme
- LISP
- REBOL
- クワイン (プログラミング)
- 部分評価
- ドメイン固有言語 - メタプログラミングにより構築することもできる。
引用エラー: 「注釈」という名前のグループの <ref>
タグがありますが、対応する <references group="注釈"/>
タグが見つかりません