第9章 GHCを拡張する・ライブラリとして使う(GHC as a Library)

目次

9.1. ソース注釈
9.1.1. 値に注釈を付ける
9.1.2. 型に注釈を付ける
9.1.3. モジュールに注釈を付ける
9.2. GHCをライブラリとして使う
9.3. コンパイラプラグイン
9.3.1. コンパイラプラグインを使う
9.3.2. コンパイラプラグインを書く
9.3.2.1. CoreToDoについてより詳しく
9.3.2.2. 束縛を操作する
9.3.2.3. 注釈を使う

GHCは、ghcパッケージを通じてその内部APIを露出している。これを使うと、GHCのコンパイルドライバ全体を活用して、Haskellコードを分析したりコンパイルしたりするプログラムを書くことができる。またGHCは、コンパイル中にコンパイラプラグイン(GHCの内部中間表現Coreを見たり変更したりすることを許されたモジュール)をロードする能力を利用者に与える。プラグインは実験的な最適化や解析などに適しており、多くのよくある場合に関して、コンパイラの開発に参入する障壁を低くする。

加えて、GHCは軽量な注釈機構を用意しており、ソースコードにメタデータで注釈を付けて、後でコンパイラAPIまたはコンパイラプラグインを使って読み出すことができる。

9.1. ソース注釈

注釈(annotation)とは、ソースコード中の識別子にデータを添付するための小さなプラグマである。添付したデータはコンパイル中残り続け、GHCをライブラリとして使ったり、コンパイラプラグインを書いたりする際に読んで利用することができる。

9.1.1. 値に注釈を付ける

最上位の値の束縛には、ANNプラグマを使って、TypeableDataの両方のインスタンスであるような任意の式を添付することができる。したがって、特に、通常の値(例えばtake)だけでなくデータ構築子(例えばJust)にもANNを使って注釈を付けられる。例として、fooという関数にJust "Hello"を注釈として付けるには、次のようにする。

{-# ANN foo (Just "Hello") #-}
foo = ...

注釈の利用には、いくつか制限が課せられる。

  • 注釈を付ける束縛は最上位のものでなければならない(つまり、ネストしていてはならない)

  • 注釈を付ける束縛は現在のモジュールで宣言されていなければならない

  • 注釈とする式は、TypeableDataの両方のインスタンスのある型でなければならない。

  • 注釈とする式にはTemplate Haskellの段階制約が課せられるので、例えばコンパイル中のモジュールの関数を走らせることはできない。

    正確には、注釈{-# ANN x e #-}が段階として正しい(well staged)のは、$(e)が段階として正しい場合であり、その場合に限る。(ただし、接合構文にかかる通常の型の制限は無視し、接合の中に接合を書くことについての通常の制限も無視する。$([|1|])は注釈として問題ない(冗長ではあるが)。)

これらの制限のうちどれかがあまりに重荷だと強く思うなら、GHCチームに連絡を寄越して欲しい

一方で、これらの制限を別にすれば、多くのことが許されており、完全に評価されていない式でさえも使うことができる。注釈式は、Template Haskellの接合がコンパイラによって評価されるのとちょうど同じように評価される。よって、この注釈は問題ない。

{-# ANN f SillyAnnotation { foo = (id 10) + $([| 20 |]), bar = 'f } #-}
f = ...

9.1.2. 型に注釈を付ける

ANNプラグマで型に注釈を付けるには、typeキーワードを使う。例を示す。

{-# ANN type Foo (Just "A `Maybe String' annotation") #-}
data Foo = ...

9.1.3. モジュールに注釈を付ける

ANNプラグマでモジュールに注釈を付けるには、moduleキーワードを使う。例を示す。

{-# ANN module (Just "A `Maybe String' annotation") #-}