LoginSignup
9
8

More than 5 years have passed since last update.

モジュールの相互参照

Last updated at Posted at 2014-10-16

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

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

A.hs
module A where

import B ( TB(..) )

newtype TA = MkTA Int

f :: TB -> TA
f (MkTB x) = MkTA x
B.hs
module B where

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

data TB = MkTB !Int

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

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

A.hs-boot
module A where

newtype TA = MkTA Int

これでコンパイルできるようになる。

$ 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 で定義されていない関数定義を追加してみると、

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:

  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:

  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 ), 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 ), 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:

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

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

参考にしたサイト

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8