モジュールの相互参照


## モジュールを相互参照する

GHC では、モジュールの相互参照をすることもできるが、そのためには循環参照を回避するために、以下のような手順を踏む必要がある。まず、互いに相互参照しているモジュールがあるとすると、

```haskell:A.hs
module A where

import B ( TB(..) )

newtype TA = MkTA Int

f :: TB -> TA
f (MkTB x) = MkTA x
```

```haskell:B.hs 
module B where

import {-# SOURCE #-} A ( TA(..) )

data TB = MkTB !Int

g :: TA -> TB
g (MkTA x) = MkTB x
```

``B.hs`` で ``A`` モジュールをロードするのに、  ``import {-# SOURCE -#} A ...`` と SOURCE プラグマを使っている点に注目。 SOURCE プラグマを指定することで、循環参照を回避するために ``A.hs`` でなく ``A.hs-boot`` というファイルをロードするようになる。このファイルに ``B`` をロードするために必要な、最小限の ``A`` モジュールの内容を記述しておく、というわけ。

```haskell:A.hs-boot
module A where

newtype TA = MkTA Int
```
   
これでコンパイルできるようになる。

```sh
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.8.3                          
$ ghc A.hs
[1 of 3] Compiling A[boot]          ( A.hs-boot, A.o-boot )
[2 of 3] Compiling B                ( B.hs, B.o )
[3 of 3] Compiling A                ( A.hs, A.o )
```

## .hs-boot ファイルの制限

``.hs-boot`` ファイルの制限は、主に以下のような制限がある。

**- 対応する ``.hs`` ファイルと同じディレクトリに配置する必要がある。**

**- ``.lhs`` ファイルの場合、 ``.lhs-boot`` となる。**

**- ``.hs-boot`` ファイルには、対応する ``.hs`` ファイルのサブセットしか含めることができない。**

> A hs-boot file need only contain the bare minimum of information needed to get the bootstrapping process started. For example, it doesn't need to contain declarations for everything that module ``A`` exports, only the things required by the module(s) that import ``A`` recursively.

いうことで、 ``.hs-boot`` ファイルには、 ``.hs`` ファイルと異なる内容を記述することはできない。試しに、上記の ``A.hs-boot`` に、 ``A.hs`` で定義されていない関数定義を追加してみると、

```haskell
module A where

newtype TA = MkTA Int

hoge :: Int -> Int
```

```
A.hs-boot:5:1:
    A.hoge is exported by the hs-boot file, but not exported by the module
```

と、怒られる。

**- ``.hs-boot`` ファイルには、変数や関数の型宣言のみを記述でき、定義本体を含めることはできない。**

> There must be no value declarations, but there can be type signatures for values. For example:

> ```haskell
  double :: Int -> Int
  ```

**- ``.hs-boot`` ファイルの data 宣言では、型定義本体を含めず、抽象型としておくことができる。**

> A data type declaration can either be given in full, exactly as in Haskell, or it can be given abstractly, by omitting the ``=`` sign and everything that follows. For example:

> ```haskell
  data T a b
  ```

> In a source program this would declare ``TA`` to have no constructors ( a GHC extension: see [Data types with no constructors](http://www.haskell.org/ghc/docs/latest/html/users_guide/data-type-extensions.html#nullary-types) ), but in an hi-boot file it means "I don't know or care what the constructors are". This is the most common form of data type declaration, because it's easy to get right. You can also write out the constructors but, if you do so, you must write it out precisely as in its real definition.

> If you do not write out the constructors, you may need to give a kind annotation ( [Explicitly-kinded quantification](http://www.haskell.org/ghc/docs/latest/html/users_guide/other-type-extensions.html#kinding) ), to tell GHC the kind of the type variable, if it is not ``*`` . ( In source files, this is worked out from the way the type variable is used in the constructors. ) For example:

> ```haskell
  data R (x :: * -> *) y
  ```

> You cannot use ``deriving`` on a data type declaration; write an instance declaration instead. 

## 参考にしたサイト

- [4.7.9 How to compile mutually recursive modules](http://www.haskell.org/ghc/docs/latest/html/users_guide/separate-compilation.html#mutual-recursion)