Help us understand the problem. What is going on with this article?

モジュールの相互参照

More than 5 years have passed since last update.

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

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.

参考にしたサイト

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away