yn2011's blog

技術メモ

そもそも reflection とは何なのか

Go 言語を勉強していて reflection って何なんだろ、となったので調べた内容を書いておく。

そもそも reflection とは何なのか

wikipediaによると

情報工学においてリフレクション (reflection) とは、プログラムの実行過程でプログラム自身の構造を読み取ったり書き換えたりする技術のことを指す。

よく分からない。以下では reflection の概念について書いていく。(Go 言語の reflection パッケージ固有の話ではない)

自己書き換えコード

プログラムの実行過程でプログラム自身の構造を読み書きするというのはどういうことか。1つの例として自己書き換えコードがある。

プログラムはプロセッサが処理可能な機械語に変換されて動作する。例えば C言語の場合だと以下のような変換になる。

C言語 -> アセンブリ言語 -> 機械語

より変換の下流に位置している、アセンブリ言語機械語のコードの内容をプログラムから書き換えることができれば、プログラム(ソースコード)に記述された処理とは見かけ上異なる処理*1を実行させることができる。そのようなプログラムを自己書き換えコードと呼ぶ(らしい)

例えば、C言語アセンブリ言語で書かれたプログラムで命令が格納されているメモリを直接操作すると、その前にプログラム内で書かれた命令は意味をなさなくなる。具体例としては、自己書き換えコード(self-modifying code) 等を参照。

自己書き換えをしたいモチベーションとしては色々あるみたいで自分は低レイヤーな部分に明るくないのであまり理解できていない。とりあえず wikipedia には色々載っていた。

Brian Cantwell Smith は、このアセンブリ言語の自己書き換えコードの発想(メタプログラミング)を高級言語にも導入しようとした。これが reflection という概念の元になっているらしい。(Procedural reflection in programming languages がその論文っぽい。読んではない。)

reflection はメタプログラミング

上述のような背景があるので、メタプログラミングをしたいから reflection がある、と考えても良さそう。

メタプログラミング

ところでメタプログラミングとは何か。定義は色々ありそうだが、メタなプログラミング、プログラムをプログラムする(ある言語がある言語を読み書きする)というイメージで良いっぽい。

例えば eval に渡す文字列を組み立てるプログラムは、まさにプログラムをプログラムが書いているのでメタプログラミングの1つになると思う。 また、SQL の組み立てもそうだし、TypeScript も JavaScriptメタプログラミングになると思う。

先程の自己書き換えコードがメタプログラミングに含まれるかは微妙かもしれない(C言語アセンブリ言語(機械語)を読み書きしていると言えるのかもしれない)

reflection を実装し、プログラム自身の構造を読み書きしたい背景として、このメタプログラミング(特に、操作する言語と読み書き対象の言語が一致している場合)に役立つからというものがあると思う。 自分は reflection の概念を Go 言語で初めて知ったが、他の多くの言語にも実装されている。例えば JavaJavaScript にもある。

なぜメタプログラミングしたいのか

じゃあなぜメタプログラミングしたいのか。特に、操作する言語と読み書き対象の言語が一致しているメタプログラミングは何が目的なのか。

TypeScript 等、別の言語を生成するならまだしも、なぜ同じ言語を取り扱うメタプログラミングが必要とされるのか。

実行時にしか分からない情報を取得したい・その情報を使った処理を行いたい

例えば、Go の場合は以下がある。

  • interface 型の変数の値の型*2
  • 構造体タグ (タグ名や値は任意の値を設定でき、構文がおかしい場合は実行時エラーになる)
  • json 等の外部データ (を Go の型システムの世界に変換したい)

また、ジェネリックに近いことも reflect パッケージを使えば実現できる(コード例はWho needs generics? Use ... instead!の 5. Use reflection を参照)

ジェネリックのためにreflect パッケージを使うとそれなりに面倒な感じにはなるっぽいが、元々の言語仕様にないことを実現できてしまうのはメタプログラミングの力だと思う。

Go 言語の reflection について

色々と reflection の概念について書いてきた。実際の Go の reflect パッケージの取り扱い方については以下の資料が役に立ちそうなのでリンク。

その他参考

*1:プログラムは記述された通りにしか動かないのであくまで見かけ上

*2:一応 reflect を使わなくてもtype assertion で特定の型かどうかの判定はできる