The Haskell 98 Report
top | back | next | contents | function index


5  モジュール

モジュールは、値、データ型、型シノニム、クラスなど (4 章を見よ)を、他のモジュールから 有効範囲に持ち込むリソースの集合、インポート部と、リソースの 一部を他のモジュールで利用することを可能にするエクスポート部 によって生成された環境において定義するものである。モジュールで定義さ れたり、インポートされたり、あるいはエクスポートされたりする値、型、 クラスのことをいうのにエンティティという用語を使う。

Haskell のプログラムはモジュールの集りであり、そのなかの一 つは慣用的に Main と呼ばれるもので、値 main をエク スポートするものでなければならない。プログラムのMain モジュールの識別子 main の値である。これはある 型 t に対する IO t 型の計算(7 章 を見よ)でなければならない。プログラムが実行されたとき、計算 main が行われる。(型 t の)結果は破棄される。

モジュールはインポートされるモジュール名を与え、インポートするエン ティティを指定する明示的な import 宣言を通じて他のモジュール を参照する。モジュールは相互再帰にすることができる。

モジュールは、単に名前空間を制御するためだけに使われ、モジュール自 身は第一級の値ではない。複数モジュールからなる Haskell のプログラムは、 それぞれのエンティティにユニークな名前をふり、すべての出現において適 切なユニーク名を参照するように変更してから、すべてのモジュール本体を 連結することにより、単一モジュールのプログラムに変換することができる。 (これには、2 つのちょっとした例外がある。第一は、default 宣 言は一つのモジュール上でのスコープしかもたない (4.3.4 節)。第二に、単相性制限 の規則 2 は(4.5.5 節) モジュールの境界に影響される)。 たとえば、以下は 3 つのモジュールからなるプログラムである。

  module Main where
    import A
    import B
    main = A.f >> B.f

  module A where
    f = ...

  module B where
    f = ...

これは次の単一モジュールのプログラムと同等である。

  module Main where
    main = af >> bf

    af = ...

    bf = ...

これらのモジュールは相互再帰的であるので、プログラムを依存性を気にす ることなく自由に分割することができる。

モジュールそのものの名前空間はフラットでそれぞれのモジュールには ユニークなモジュール名(Haskell では大文字ではじまる識別子、すなわち、 modid)を付ける。特別なモジュール Prelude というのがあっ て、デフォルトですべてのプログラムにインポートされる。(5.6 節を見よ。) さらに標準ラ イブラリが必要な場合にはインポートされる。(Part II を見よ。)

5.1  モジュール構造

モジュールは値束縛、データ型、型シノニム、クラス、そのほかに対する 宣言を含む相互再帰的有効範囲を定義する。(4 章を見よ。)

module -> module modid [exports] where body
| body
body -> { impdecls ; topdecls }
| { impdecls }
| { topdecls }
modid -> conid
impdecls -> impdecl1 ; ... ; impdecln (n>=1)
topdecls -> topdecl1 ; ... ; topdecln (n>=1)

モジュールはヘッダ部分から始まる。ヘッダ部とは、module と いうキーワード、モジュール名、そして、エクスポートするエンティティの リスト(これは丸括弧でかこまれる)である。ヘッダ部のあとには、必須では ないが、インポートするモジュールを指定する import 宣言のリス ト(impdecls5.3 節)がつづく。 インポートする束縛を限定することもできる。そして、その後にモジュール の本体とつづく。単にトップレベルの 宣言のリスト(topdecls4)である。

モジュールの簡略形式は、モジュールの本体のみを含むモジュールで、こ れは許されている。この簡略形がつかわれた場合、ヘッダ部は `module Main(main) where' と仮定する。簡略形のモ ジュールの最初の字句が { ではない場合、レイアウト規則がこの モジュールのトップレベルに対して適用される。

5.2  エクスポートリスト

exports -> ( export1 , ... , exportn [ , ] ) (n>=0)
export -> qvar
| qtycon [(..) | ( cname1 , ... , cnamen )] (n>=0)
| qtycls [(..) | ( var1 , ... , varn )] (n>=0)
| module modid
cname -> var | con

エクスポートリストはモジュール宣言によりエクスポートされる エンティティを同定するものである。モジュールの実装はこれが宣言するエ ンティティあるいは他にモジュールからインポートされたもののみをエクス ポートする。エクスポートリストが省略された場合、このモジュールで定義 されたすべての値、型、クラスがエクスポートされる。ただし、インポー トされたものについてはエクスポートされない。

エクスポートリストにあるエンティティは以下のように名前付けされる。

  1. 値、フィールドネーム、あるいはクラスメソッドは当該モジュール本体 で宣言されているかインポートされている場合、qvarid をその値の 名前として与えられ名前付けされる。演算子は括弧でかこって、 qvarid としな ければならない。

  2. data 宣言あるいは newtype 宣言による代数データ型 T 次の 3 つ方法のうちどれかによって名前を付けることができる。

    すべての場合において、(おそらくは修飾されている)型構成子 T はスコープ内になければならない。第二の形式の構成子とフィールド名 c は修飾されていない。これらの従属名のひとつは 次の場合にかぎって正当である。(a) T の構成子あるいはフィール ド名であり、かつ、その構成子あるいはフィールド名が、そのモジュール 本体のスコープ内にある場合。それが修飾された名前もとでスコープ 内にあるか、修飾されていない名前のもとでスコープ内であるかに関係し ない

      module A( Mb.Maybe( Nothing, Just ) ) where
        import qualified Maybe as Mb

    データ構成子は subordinate 名として以外でエクスポートリストのなかで 名前を与えることはできない。そうでなければ、型構成子と区別がつかない からである。

  3. type 宣言による型シノニム TT という形 式で名 前を与えることができる。ここで、T はスコープ内にあると する。

  4. class 宣言による演算 f1,...,fn をもつ クラス C は次の 3 つの方法のどれかで名前があたえられる。

    すべての場合において、C は有効範囲内になければならない。第 2 の形式では以下の場合に限って(修飾されていない)従属名 fi の一つが正しいものとなる。(a) C のクラスメソッドに名前を与え るものであり、かつ、(b)そのクラスのメソッドがそのモジュール本体の中 でスコープ内にある場合。それが修飾された名前もとでスコープ内にあるか、 修飾されていない名前のもとでスコープ内であるかに関係しない。

  5. "module M" という形式は、修飾されていない名前 "e" および修飾された名前 "M.e" の両方のスコープ 内にあるすべてのエンティティの集合に名前をあたえる。この集合は空で もよい。たとえば、

      module Queue( module Stack, enqueue, dequeue ) where
          import Stack
          ...

    ここでモジュール QueueStack からインポートさ れた全てのエンティティを纏めるために、エクスポートリストの中で、モ ジュール名 Stack を使っている。

    ひとつのモジュールはそれ自身の局所定義に、エクスポートリスト内で module M という構文で自身の名前をつかうことで、名前 を与えることができる。局所的な宣言はそのスコープに修飾された名前と 修飾されない名前の両方を持ち込むからである (5.5.1 節)。たとえば、

      module Mod1( module Mod1, module Mod2 ) where
      import Mod2
      import Mod3

    ここで、モジュール Mod1 はそのすべての局所定義と Mod2 からインポートしたすべての定義をエクスポートしいるが、 Mod3 か らインポートしたものはエクスポートしていない。

    エクスポートリスト中で module M を使うのは、 以下の場合のどちらかでなければ、誤りである。M が エクスポートリストから生れたものであるか、あるいは M が (限定修飾されているかどうかにかかわらず)少くともひとつのインポート 宣言によってインポートされている。

エクスポートリストは cumulative である。エクスポートリストにより エクスポートされるエンティティの集合は、そのリストの個別の項目により エクスポートされるエンティティの和集合である。

エンティティがどのようにエクスポートされたかは、モジュールの インポートに差をつけるものではない。たとえば、データ型 T のフィールド名 f は (f、上の(1)の場合)のように 個別にエクスポートすことができる。また、そのデータ型の明示的名前の メンバーとして(T(f)、(2)の場合)エクスポートすることもできる。 また、暗黙的名前のメンバー(T(...)、(2)の場合)としても エクスポートできる。さらに、モジュール全体をエクスポートすること (module M、(5)の場合)でエクスポートすることができる。

モジュールによってエクスポートされたエンティティの 限定修飾されていない名前は(それが属している 名前空間において)すべて別々のものでなければならない。

  module A ( C.f, C.g, g, module B ) where   -- an invalid module
  import B(f)
  import qualified C(f,g)
  g = f True

モジュール A そのものの中では名前の衝突はないが、 エクスポートリスト中の C.gg では衝突がある (C.gg は別のエンティティであることを 仮定している -- モジュールは相互再帰的にインポートできることを 思い出せ)。また、module BC.f との間にも 衝突がある( B.fC.f は別のエンティティであること を仮定する)。

5.3  インポート宣言

impdecl -> import [qualified] modid [as modid] [impspec]
| (empty declaration)
impspec -> ( import1 , ... , importn [ , ] ) (n>=0)
| hiding ( import1 , ... , importn [ , ] ) (n>=0)
import -> var
| tycon [ (..) | ( cname1 , ... , cnamen )] (n>=0)
| tycls [(..) | ( var1 , ... , varn )] (n>=0)
cname -> var | con

モジュールによりエクスポートされたエンティティは、別のモジュールの 最初の部分で import 宣言することで、そのモジュールの有効範囲 に持ち込むことができる。import 宣言は、インポートする モジュールに名前を付け、オプションとしてインポートするエンティティを 指定する。単一のモジュールが複数の import 宣言により インポートすることが可能である。インポートされた名前はトップレベル の宣言として機能し、そのモジュールの本体全体に渡る有効範囲をもつが、 トップレベルではない局所的な束縛により覆い隠される可能性がある。

複数の import 宣言の効果は厳密に累積的である。すなわち、 一つのエンティティは、あるモジュール内のすべての import 宣言によって名付られたものの有効範囲内にある。このとき、インポート の順は関係ない。

字句構造としては、終端記号「as」、 「qualified」、「hiding」はそれぞれ、varid であって、reservedid ではない。これらは import 宣言 の文脈中でのみ特別な意味をもち、それ以外では変数として用いることが できる。

5.3.1  インポートされるもの

どのエンティティがインポートされるかは、正確には次の3つの方法のうち どれかの方法で指定することができる。

  1. インポートするエンティティを明示的に括弧の中でリストアップする。 このリスト内の項目はエクスポートリストないのそれと同じ形式である。 ただし、限定修飾子は許されないことと、「module modid」 というエンティティが許されないことが例外である。(..) とい う形式が型やクラスに対して使用された場合、(..) は、その モジュールからエクスポートされた、そのすべての構成子、メソッド、あるいは フィールド名をあらわす。

    インポートリストはインポートするモジュールがエクスポートしている エンティティのみ名指ししなければならない。空のリスト であってもかまわない。その場合はそのインスタンス以外はなにも インポートされない。

  2. hiding(import1 , ... , importn) という形式を使用することに よりエンティティを除外することができる。これは、その名指しされたモ ジュールによりエクスポートされるすべてのエンティティはそのリスト内 の名指しされたエンティティを除いてすべてインポートすることを指示し ている。データ構成子は隠蔽リストのなかで直接名指しされその型名が接 頭辞としてつくことはない。それゆえ、

      import M hiding (C)

    では C という名前の構成子、クラス、型のすべてが除外される。 一方、インポートリスト内で C を使うとこれはクラスあるいは タイプだけが対象となる。

    インポートされるモジュールによってエクスポートされてない エンティティを隠蔽しようとすることはエラーである。

  3. impspec が省略された場合は、指定されたモジュールにより エクスポートされたすべてのエンティティがインポートされる。

5.3.2  修飾子付インポート

5.3.1節のルールで インポートされたそれぞれのエンティティごとに、トップレベルの環境が 拡張される。qualified キーワードを使ったインポート宣言に よってスコープに持ち込まれる名前は、インポートされたエンティティの 修飾された名前だけである。qualified キーワードを 省略すると、修飾された名前と修飾されない名前の両方がスコープに持ち 込まれる。修飾されあた名前については 5.5.1 節で詳述する。

インポートされた名前に付く修飾子はインポートされたモジュールの名前で あるか、import ステートメントに付いた as 節 (5.3.3 節)により与えられた局所的な 別名のどちらかである。それゆえ、修飾子はエンティティがもともと 宣言されていたモジュールの名前である必要はない。

修飾されていない名前を排除できるので、プログラマは修飾されて いない名前空間を完全に制御することができる。すなわち、局所的に 定義されたエンティティは修飾されてインポートされたものと同じ名前を 共有することができる。

  module Ring where
  import qualified Prelude    -- All Prelude names must be qualified
  import List( nub )

  l1 + l2 = l1 Prelude.++ l2  -- This + differs from the one in the Prelude
  l1 * l2 = nub (l1 + l2)     -- This * differs from the one in the Prelude

  succ = (Prelude.+ 1)

5.3.3  局所的な別名

インポートされたモジュールには、as 節を用いてインポート したモジュール内で局所的な別名を割当ることができる。たとえば、

  import qualified VeryLongModuleName as C

では、エンティティは`C.'を`Complex'の代わりに 修飾子として使用して参照しなければならない。このことにより別の モジュールをインポートされたモジュールに対して用いられた修飾子を 変更することなくComplex に対して置換えることができる。 これにより、スコープ内にある 2 つ以上のモジュールが同じ修飾子を 使うことは、すべての名前が曖昧なところなく解決可能であれば、 正当である。

  module M where
    import qualified Foo as A
    import qualified Baz as A
    x = A.f

このモジュールは、Foo および Baz のどちらもが f をエクスポートしているということがないかぎり、正当である。

as 節は、qualified が付かない import ステートメント上で使うこともできる。

  import Foo as A(f)

この宣言はスコープに f および A.f 持ち込む。

5.3.4  

上述のインポート規則を明確にするために、モジュール Axyをエクスポートしていると仮定する。以下の表は それぞれのインポート宣言によってスコープに持ち込まれる名前を示すもの である。

Import declaration Names brought into scope
import A x, y, A.x, A.y
import A() (nothing)
import A(x) x, A.x
import qualified A A.x, A.y
import qualified A() (nothing)
import qualified A(x) A.x
import A hiding () x, y, A.x, A.y
import A hiding (x) y, A.y
import qualified A hiding () A.x, A.y
import qualified A hiding (x) A.y
import A as B x, y, B.x, B.y
import A as B(x) x, B.x
import qualified A as B B.x, B.y

どの場合にも、モジュール A のスコープにあるすべての インスタンス宣言がインポートされる (5.4 節)

5.4  インスタンス宣言のインポートとエクスポート

インスタンス宣言はインポートあるいはエクスポートリストで明示的に 名前を付与することはできない。ひとつのモジュール内の有効範囲にある インスタンスは常にすべてエクスポートされ、どのような インポートもインポートされたモジュールからのすべての インスタンスを持ち込む。したがって、インスタンス宣言は import 宣言のチェーン当該のインスタンス宣言を含むモジュールに到達する場合かつ その場合に限り有効範囲内にある。

たとえば、import M() はモジュール M から あらたな名前はなにも持ち込むことはないが、M 内のすべての インスタンスは有効範囲に持ち込まれる。インスタンス宣言を供給すること のみを目的としたモジュールは、空のエクスポートリストを持つことができる。 たとえば、

  module MyInstances() where
  instance Show (a -> b) where
    show fn = "<<function>>"
  instance Show (IO a) where
    show io = "<<IO action>>"

5.5  名前の衝突とクロージャ

5.5.1  修飾された名前

修飾された名前modid.name のように書く(2.4 節)。 修飾された名前はスコープ内に持ち込まれる。

5.5.2  名前の衝突

あるモジュールが、f あるいは A.f という名前の束 縛を含んでいれば、それは曖昧なところなく、どのエンティティを参照している か解決できるものでなければならない。すなわち、f あるいは A.f の束縛はそれぞれ唯一でなければならない。

解決できない名前があることは、そのプログラムがそれらの名前に言及しないか ぎりはエラーではない。たとえば、

  module A where
    import B
    import C
    tup = (b, c, d, x)
  
  module B( d, b, x, y ) where
    import D
    x = ...
    y = ...
    b = ...
  
  module C( d, c, x, y ) where
    import D
    x = ...
    y = ...
    c = ...

  module D( d ) where
    d = ...

tup の定義について考えてみよ。

型シグネチャ内あるいは結合性宣言内での名前は常に修飾されていないが、 同じ宣言リスト(トップレベルにあらわれるクラスメソッドに対する結合性 宣言は例外 -- 4.4.2 節)内の他の宣言を 曖昧なところなく参照する。たとえば、以下のモジュールは正当なものである。

  module F where

    sin :: Float -> Float
    sin x = (x::Float)

    f x = Prelude.sin (F.sin x)

たとえ、プレリュード関数 sin が暗黙のうちにスコープ内に あっても、局所的な sin の宣言が正当なものである。 Prelude.sinF.sin はともに、 どちらの sin かが曖昧にならないように、修飾されている必要が ある。しかしながら、F の最初の行の型シグネチャ中の 修飾されていない sin は曖昧なとろなく、sin の 局所宣言を参照している。

5.5.3  クロージャ

ひとつの Haskell プログラム内のすべてのモジュールは閉じたもの でなければならない。すなわち、ソースコードによって明示的に言及さ れたすべての名前は局所的に定義されているか、あるいは、別のモジュール からインポートされたものであるかどちらかでなくてはならない。型チェッ クあるいは別のコンパイル時解析のためにコンパイラが必要とするエンティ ティは、名前により言及されなければインポートされている必要はない。 Haskell のコンパイルシステムはコンパイルに必要な全ての情報をプログラ マの助けなしに見つける責任がある。すなわち、ある変数 x のイ ンポートすることで、これらのエンティティがユーザのプログラムのなかで 名前で参照されないうちに、x のシグネチャ中のデータ型およびク ラスが当該のモジュールに x と同時に持ち込まれることを要求し てはいけない。Haskell システムは型チェックやその他の目的のためにエン ティティに付随するはずのすべての情報を黙ってインポートしなければなら ない。このようなエンティティは明示的にエクスポートされている必要もな い。以下のプログラムは、TM1 をエスケープしてい るわけではないのにもかかわらず正当なプログラムである。

  module M1(x) where
    data T = T
    x = T
  
  module M2 where
    import M1(x)
    y = x

この例では、y に対する明示的な型シグネチャを与える方法はない。 それは T が有効範囲にないからである。T が明示的にエ クスポートされてようとなかろうと、モジュール M2 はプログラム の型チェックを正しく行うために T について十分知っている。

エクスポートされたあるエンティティの型はエクスポートされていない型 シノニムには影響されない。たとえば、

  module M(x) where
    type T = Int
    x :: T
    x = 1

のなかで、x の型は T でかつ Int である。こ れらは T が有効範囲になくとも交換可能である。すなわち、 T の定義はその名前 T が有効範囲にあるなしにかかわら ず、それにあたったすべてのモジュールに対して利用可能である。 T をエクスポートする唯一の理由は他のモジュールがそれを名前で 参照することを可能にするためである。型チェッカーはそれがエクスポート さていようがいまいが、必要なら T の定義を見つけだす。

5.6  標準プレリュード

Haskell の特徴の多くは、「標準プレリュード」と呼ばれる、標準データ 型、標準クラスおよび標準関数のライブラリとして、Haskell 自身で定義さ れている。Haskell ではこのプレリュードは、モジュール Prelude に含まれている。おおくの定義済みのライブラリモジュールもあり、これら には、利用頻度のより少ない関数や型を供給している。たとえば、配列、表、 入出力のほとんどはどれも標準ライブラリにある。これらは別のドキュメン ト Haskell Library Report [8] の中で定義されている。ライブラリをプレリュードとは別することはでサイ ズを削りプレリュードが複雑になるのを軽減するという利点がある。そうす ることで簡単に同化することができ、プログラマが利用できる有用な名前空 間を増すことができる。

プレリュードとライブラリモジュールが他のモジュールと異るのは、セマ ンティクス(実装ではない)として Haskell の言語定義の固定された部分であ るという点である。その意味は、例えば、コンパイラがこのプレリュードの ソースコードを参考にすることなくプレリュード中の関数の呼出しを最適化 してもよいということである。

5.6.1   Prelude モジュール

Prelude モジュールは`import Prelude'があるか のようにすべてのモジュールに自動的にインポートされるが、これは、明示 的に import宣言によってインポートされない場合に限る。明示的 なインポートに対するこの規約はプレリュード中で定義されたエンティティ を、他のモジュールからインポートする場合と同様に、選択的にインポート することを可能にします。

Prelude 内のエンティティのセマンティクスは、 8 章で与えられている Haskell で書かれた Prelude の実装によって指定される。いくつ かの型( Int など)および、いくつかの関数(たとえば、 Int の加算など)については直接 Haskell で指定することはできな い。このようなエンティティの扱いは実装に依存するので、 この 8 章では形式的に 定義しない。Prelude はタプルの扱いについても完全なもの ではない。タプルの族およびそのインスタンス宣言は無限にあるはずだが、 実装はひとつの図式をしめすだけである。

8 章は Prelude モジュールを PreludeListPreludeIO などのいくつかの他のモジュールを用いて定義している。 これらのモジュールは、Haskell 98 の一部ではなく、一部だけ別々 にインポートすることはできない。Prelude モジュールの構造を説 明するのに役立つように存在しているにすぎない。言語定義の一部ではなく、 実装の一部であると考えられるべきものである。

5.6.2  プレリュード名の隠蔽

モジュール Anull を再定義しているが、このことを null を除外して Prelude をインポートすることにより 表示しなければならない。さらに、Anull をエクス ポートするが、A から修飾子なしに null をインポート するモジュールはどれも、Prelude からの null を隠蔽 し、Aからのだけをインポートしなければならない。したがって、 プレリュードの名前が意図せず隠蔽されてしまうことはほとんどない。

プレリュードをその場で提供するために別のモジュールを使うあるいは 構築することが可能である。プレリュードは、それが暗黙のうちにインポー トされるということを除けば、普通の他の Haskell のモジュールと同じであ る。いくつかのプレリュード中のオブジェクトで特殊な構文構成で参照さ れということのみが特別あつかいである。プレリュードによる名前の再定義 はこれらの特別な構文構成の意味には影響しない。たとえば、

  module B where
    import Prelude()
    import MyPrelude
    f x = (x,x)
    g x = (,) x x
    h x = [x] ++ []

明示的な import qualified Prelude 宣言は、 Prelude からの自動的なインポートを妨げる。一方、 import MyPrelude は非標準のプレリュードをスコープに持ち 込む。タプルの特別な構文( (x,x) (,) など)や リストの特別な構文( [x] [] など)は引き続き 標準Preludeによって定義されたタプルやリストを参照する。 たとえば、リストの別のインプリメンテーションという意味では、 [x] を再定義する方法はない。しかし、一方では、++ は 特別な構文ではなく、MyPrelude からインポートされた ++ を参照することになる

しかし、Prelude 中の instance 宣言を隠蔽すること はできない。たとえば、 Show Char に対して新しいインスタ ンスを定義することはできない。

5.7  分割コンパイル

どの Haskell の実装を用いるかによっては、相互再帰的なモジュールの 分割コンパイルは、インポートされたモジュールは、それがコンパイルされ る前に参照されてもいいように付加的な情報が必要となる。すべてのエクス ポートされた値の明示的な型シグネチャが相互再帰を処理するために必要と なる。分割コンパイルの正確な詳細はこのレポートでは定義しない。

5.8  抽象データ型

構成子なしでデータ型をエクスポートできるということは、抽象データ型 (ADT)の構成を可能にする。たとえば、スタックに対応する ADT は以下のよ うに定義することができる。

  module Stack( StkType, push, pop, empty ) where
    data StkType a = EmptyStk | Stk a (StkType a)
    push x s = Stk x s
    pop (Stk _ s) = s
    empty = EmptyStk

Stack をインポートするモジュールは StkType 型の値を 構成することはできない、それはこの型の構成子にアクセスすることができ ないからである。

newtype 宣言を用いて、既存の型のうえに ADT を構築すること も可能である。たとえば、スタックはリストを使って定義可能である。

  module Stack( StkType, push, pop, empty ) where
    newtype StkType a = Stk [a]
    push x (Stk s) = Stk (x:s)
    pop (Stk (_:s)) = Stk s
    empty = Stk []


The Haskell 98 Report
top | back | next | contents | function index
December 2002