Macro - マクロ とは

Ola Biniコメント記念に、Ioke GuideのMacro部分を(とりあえず)和訳。ぎこちないのは俺クオリティ。
http://www19.atwiki.jp/katzchang/pages/15.htmlで、全訳中です。お客様のなかで、協力して頂ける方はいらっしゃいませんかぁー?

Macros - マクロ

Ioke でのマクロとメソッドの最大の違いは、マクロに与えられた引数はマクロに送られるまでは評価されないという点にある。つまり、実行時にメッセージチェーンそのものを送ることができる。多くの言語では、この種の機能は一般的に「名前呼び出し」と呼ばれる。マクロが呼び出されたとき、 Call 種の「call」セルにアクセスが発生する。この時、呼び出し情報へのアクセスが与えられ、引数として与えられたコードを評価することが可能となり、引数が幾つ与えられたか等を評価でき…などなど。

マクロは DefaultBehavior の「macro」セルで生成する。これで、 DefaultMacro のミミックが返る。マクロは引数を定義できないため、メソッドと比べて説明は少し簡単になるが、マクロはメソッドよりも少し面白いことが出来る。覚えておくべき重要なことは、ほとんどのマクロは splatted? な引数を受け取れないということだ。ほとんどの場合、キーワード引数も受け取れない、が、必要であれば真似ることはできる。通常、マクロは制御構造の実装やコードの操作に使う。

メソッドと同様、マクロは特別なレシーバ上で評価される。メソッド実行コンテキストと同じ種のオブジェクトを受け取るが、中身は少し異なる。特に、マクロ用のコンテキストは「self」「@」「currentMassage」「surroundingContext」および「call」セルを持つ。「call」セルは特に重要だ。Callのミミックであり、Callには実行環境を操作するためのいくつか重要なメソッドが定義されている。これらだ:

arguments
このメソッドはメッセージとして与えられた引数そのもののリストを返す。引数への評価などの操作はされない。
ground
callが起動された時のグラウンドオブジェクトを返す。引数を実行環境上で評価する場合に必要となる。
message
現在実行しているメッセージ。マクロ実行コンテキストの「currentMessage」セルと同様。
evaluatedArguments
リスト上の全ての引数を、通常のルール(ただし、splattingやキーワード操作を除く)に従って評価し、返す。
resendToMethod
特定のメッセージを他のメソッドに再送信する。情報を書き写さないでも済む。

これらのメソッドは少し理解しづらいので、Iokeでの実装例を幾つか見ながら、マクロがどのように使われるか確認しよう。

Mixins Enumerable map = macro(
  "takes one or two arguments. if one argument is given,
it will be evaluated as a message chain on each element
in the enumerable, and then the result will be collected
in a new List. if two arguments are given, the first one
should be an unevaluated argument name, which will be
bound inside the scope of executing the second piece of
code. it's important to notice that the one argument
form will establish no context, while the two argument form
establishes a new lexical closure.",

  len = call arguments length
  result = list()
  if(len == 1,
    code = call arguments first
    self each(n, result << code evaluateOn(call ground, cell(:n))),

    code = LexicalBlock createFrom(call arguments, call ground)
    self each(n, result << code call(cell(:n))))
  result)

上記のコードは、Enumerableのmapメソッドの実装だ。mapメソッドは、あるコレクションを所定のルールに従って他に割り当てる。1つまたは 2つの引数をとる。1つだけ引数が与えられた場合、メッセージチェーンを受け取り、結果を蓄える。2つの引数が与えられた場合、1つ目は使用する引数の名前で、2つ目はそれぞれの入力に対して実行されるコードとなる。

まず、1ステップ目で引数の数を調べている。これは「call arguments」セルの length をチェックすればよい。length が1であれば、1つ目の引数が実行するコードということになり、引数を「code」セルに割り当てればよい。で、「code」は Message のミミックであり、かつ Message には「evaluateOn」メソッドがあり、メッセージチェーンを全て実行できる。そして、用意したコレクションの各要素に対して実行される。「evaluateOn」の結果がリストに追加される。「call ground」を用いて、コードが評価される ground を取得している。

引数が2つ与えられた場合、ショートカットされ(?)て引数から構文ブロックが生成され、使われる。ので、「LexicalBlock createFrom」を呼び、引数と ground を与え、コレクションの各要素に対してコードが実行される。

マクロの動きを見せるのは少しトリッキーだ。理解のためには、 Ioke の標準メソッドやマクロの実装を読むことをお勧めする。機能性が高い。