2012年1月2日月曜日

GHCにおいてモジュールを相互再帰させるには


ソフトウェアの設計において、モジュールを相互参照させたい状況があります。論理的にはモジュールを細かく分ければ相互参照が起きないようにできる場合でも、意味的に考えた場合にそれらを分割したくないようなときです。

このエントリでは、GHCでHaskellのコードをコンパイルする際にモジュールを相互参照させる方法を紹介します。

The Haskell 98 Reportにおける記述
Haskellの言語仕様である"The Haskell 98 Report"では、モジュールは相互再帰をしても良いとされています。
"5. Modules" in The Haskell 98 Report
... Modules may be mutually recursive. ...
ところが、GHCでHaskellのコードをコンパイルする場合、そのままではモジュールを相互参照させることができずエラーとなります。

そうご参照させたモジュールをGHCでコンパイルする場合、追加の記述が少しだけ必要になります。

GHCで相互再帰モジュールをコンパイルするには
GHCは相互再帰モジュールをサポートしていますが、それをコンパイルするには、インポートグラフの循環参照回避のために、以下が必要となります。
  • SOURCEプラグマ
  • hs-bootファイル
SOURCEプラグマは、import宣言においてのみ使われるプラグマです。インポートグラフが循環しないようにコンパイラにヒントを与えるためのものです。

hs-bootファイルは、対応する.hsファイルのモジュール宣言や型宣言のみを抽出して記述したファイルです。.hs-boot拡張子を与えてプログラマが用意します。上述のSOURCEプラグマが付与されたモジュールをインポートする場合、インポートされる側のモジュールについては、.hsファイルではなく.hs-bootファイルから生成される情報を参照することになります。

具体的にどのように実装するかについては、GHCユーザガイドの4.7.9. How to compile mutually recursive moduleにて詳しく記述されています。

--