マクロは強力なsyntax拡張機能

他の多くのプログラミング言語と同様、curlでもコードをモジュール化して再利用するためにクラスやプロシージャを使うことができます。curlにはもうひとつ、マクロと呼ばれるコードの再利用手段があります。

例えば、イベントハンドラを記述するときに使う on はマクロのひとつです。

{CommandButton
    label = "commit",
    {on Action at cb:CommandButton do
        {cb.dialog.commit}
    }
}

こんな感じで普段何気なく使っていることと思います。
では、この on マクロが具体的に何をしているのかご存知でしょうか?イベントハンドラを記述するものなので、おそらくEventHandlerクラスのインスタンスを生成してくれているだろうことは想像がつくかもしれません。(実際にその通りです)
それでは、他の多くのクラスのようにコンストラクタ呼出しでインスタンスを生成することと何が異なるのでしょうか?EventHandlerもクラスでありコンストラクタが存在します。実際、以下のようなEventHandlerの記述も可能です。

{CommandButton
    label = "commit",
    {EventHandler
        Action,
        {proc {ev:Event, target:EventTarget}:void
            {(target asa CommandButton).dialog.commit}
        }
    }
}

しかしこのコードでは on マクロでは必要のなかった匿名プロシージャの宣言や、EventTargetのキャストが必要になっています。つまり、on マクロはイベントハンドラとゆう頻繁に記述されるコードに対して、必要最小限の簡単な専用syntaxを提供してくれていると言えます。


マクロはコンパイル時のソースコード変換のための仕組みです。on マクロも、実際はコンパイル時にEventHandlerのコンストラクタ呼出しコードに変換されています。基本的に、イディオムと呼ばれるような(冗長であるけれど繰り返される)コーディングが必要となるような場面に対してマクロは威力を発揮します。

典型的な例は、決まりきった前処理(or後処理)が必要となるような場合です。例えばストリームの処理には必ずcloseとゆう後処理が必要になります。しかも、例外が発生した場合でも必ずcloseが行われるよう、try/finallyを利用しなければなりません。curlはこのようなストリームの処理に対して with-open-streams とゆうマクロを提供しています。

let buf:StringBuf = {StringBuf}
{if-non-null from = {choose-file} then
    {with-open-streams in = {read-open from} do
        {in.read-one-string buf = buf}
        {if not buf.empty? then
            {error "File is not empty!"}
        }
    }
}

with-open-streams マクロのおかげで try/finally や close を明示的に書かなくて済みます。このように、マクロによってクラスやプロシージャでは達成できないようなロジックの再利用が可能になります。マクロのおかげで冗長なコーディングが削減され、プログラマはより本質的なロジックの記述に専念できます。マクロを自分で定義する際は、ヘルプドキュメントの以下の節が役に立ちます。

  • [Surge Lab 開発者ガイド > 基本概念 - 構文 > マクロ]


最近では、特定の問題領域に対して抽象度の高い必要最小限のAPIセットや開発環境を用意することによって生産性を高めるDSLという技術が注目されています。curlのマクロには、言語レベルでより強力にDSLをサポートできる可能性があるのではないかと思います。みなさんも実際のプログラミングでマクロを活用してみてはいかがでしょうか?