モジュール

モジュールは(他のモジュールからスコープの中へもたらされたリソースの) インポート の一式によって作成された環境の中の値やデータ型、型シノニム、クラスなど(章を参照)の集まりを定義する。 これらのリソースのいくつかを エクスポート し、他のモジュールでそれらを利用できるようにする。 モジュールの中で定義された、またはその中へインポートされた、あるいはエクスポートされた値または型クラスに参照するために項の エンティティ を使用する。

Haskell プログラム はモジュールの集まりであり、その中の一つから規約により、Mainが呼ばれなければならず、かつmain値をエクスポートしなければいけない。 プログラムの はモジュールMainの中の識別子mainの値であり、そしていくつかの型τのための型IO τの計算結果でなければいけない(章を参照)。 プログラムが実行されたとき、計算結果mainは行わられ、(型τの)その結果は捨てられる。

モジュールは明示的なimport宣言により他のモジュールを参照することができ、 その各々でインポートされたモジュールの名前を与え、かつインポートされるためにそのエンティティを明記する。 モジュールは互いに再帰的にインポートされることもできる。

モジュールは名前空間の制御に使われ、かつファーストクラス値ではない。 複数のモジュールからなるHaskellのプログラムは、単一のモジュールのプログラムに、各エンティティに一意な名前を与え、その参照が出現する全ての場所を適切に一意な名前に変えて全てのモジュールの本体1の名前を連結することにより変換することができる。

例えば、ここに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 = ... 

なぜならそれらは互いに再帰的であることが許可され、モジュールはプログラムを依存の状態に注意することなく自由に分割することを許す。

モジュールの名前(語彙素modid)は大文字で始まり、ドットで区切られ、空白をはさまない一つ以上の識別子の列である。 例えば、Data.BoolMainForeign.Marshal.Allocはすべて有効なモジュール名である。

modid{conid .} conid(modules)

モジュール名は新しいコンポーネントを追加すると、元々のモジュール名の子を階層の中に作成し、その階層の中に配置されているように考えることができる。 例えば、モジュールContorl.Monad.STContorl.Monad下の階層の子である。 しかしこれは単なる慣習であり、言語の定義には含まれない。このリポートではmodidは平坦な名前空間を占有する単一の識別子のように扱われる。

中でも特別なモジュール Prelude が存在し、これはデフォルトで全てのモジュールにインポートされる(セクション5.6)。 加えて、必要に応じてインポートされる標準ライブラリのモジュールの集合も特別である(Part2を参照)。

(訳注:上のリンク先であるPart2はHaskell2010のライブラリの部分でこのリポートの13章から42章までを指す。)

1 この文に2つの小さな例外がある。 一つは、デフォルト宣言のスコープは単一のモジュール内のみに及ぶ(セクション4.3.4)。 二つめは、単相性の制約のルール2がモジュールの境界によって影響を受ける。

モジュールの構造

モジュールは値束縛やデータ型、型シノニム、クラスなどへの宣言を含む相互再帰的なスコープを定義する(4章を参照)。

module module modid [exports] where body
|body
body { impdecls ; topdecls }
|{ impdecls }
|{ topdecls }
impdecls impdecl1 ; … ; impdecln(n ≥ 1)
topdecls topdecl1 ; … ; topdecln(n ≥ 1)

モジュールはmoduleキーワードとその名前、エクスポートされる(丸括弧で囲まれた)エンティティのリストをヘッダーに伴って始まる。 そのヘッダーの次にはインポートされるモジュールを明記する空かもしれないimport宣言(impdecls、セクション5.3)のリストが続き、 必要に応じてインポートされる束縛を制限する。 これには空かもしれないトップレベルの宣言のリストが次に続くであろう(topdecls4章)。

モジュールの本体のみで成るモジュールの短縮形式は許される。 もしこれが使われるなら、そのヘッダーは‘module Main(main) where’であると推測される。 もし短縮されたモジュールの初めの語彙素が{でなければ、そのときそのレイアウトルールはそのモジュールのトップレベルへ適用される。

エクスポートリスト

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

エクスポートリストはモジュール宣言によってエクスポートされるエンティティを識別する。 モジュールの実装はそのモジュールで宣言しているまたは他のモジュールからインポートしているエンティティのみをエクスポートできる。 もしエクスポートリストが省略されるなら、モジュールの中で定義されたすべての値や型、クラスはインポートされるものを除いてエクスポートされる。

エクスポートリストのエンティティは次に従う名前である。

  1. 値、フィールド名またはクラスメソッドはそれらがモジュールの本体またはインポートによって宣言されたものかどうかにかかわらず、 qvaridのように値の名前を与えることによって名前をつけることができ、そしてそれはスコープ内でなければならない。 演算子はそれらをqvaridsに変えるために丸括弧で閉じられなければならない。

  2. dataまたはnewtype宣言で宣言された代数データ型Tは次の3つの方法の一つで名前をつけることができる。

    • 形式Tコンストラクタまたはフィールド名によってではなく、 型によって名前がつけられる。 コンストラクタなしに型をエクスポート出来るという仕様は、抽象データ型のコンストラクタについても同様である(セクション5.8)。
    • 形式T(c1,...,cn)は型とそのコンストラクタとフィールド名の複数または全ての名前である。
    • 略された形式T(..)は(修飾されたかされていないかのどちらにしろ)現在のスコープにある型と全てのそのコンストラクタとフィールド名の名前である。

    全てのケースにおいて(修飾されているかもしれない)型コンストラクタTはスコープになければならない。 2番目の形式の中のciの名前であるそのコンストラクタとフィールドは修飾されない。 これらの付随する名前の一つは次の場合にのみ正当である。 (a) Tのコンストラクタまたはフィールドの名前である。かつ、 (b) コンストラクタまたはフィールドが 修飾されるかされない名前の下のスコープ内にあるかどうかにかかわらず モジュール本体のスコープ内にある。 例えば、次のコードは正当である。

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

    データコンストラクタは付随する名前のようなものを除いてエクスポートリストの中で名前をつけることはできない。 なぜならそうでなければ型コンストラクタから見分けられないからだ。

  3. data宣言によって宣言される型シノニムTは形式Tによって名前をつけられることができ、Tはスコープ内にある。

  4. class宣言で宣言される演算f1,...,fnを伴うクラスCは次の3つの方法のひとつから名前をつけることができる。

    • 形式Cクラスメソッドを除いて クラスの名前である。
    • 形式C(f1,...,fn)はクラスとそのメソッドのいくつかまたは全ての名前である。
    • 略された形式C(..)は(修飾されるされないか関係なく)スコープにあるクラスとその全てのメソッドの名前である。

    全てのケースにおいて、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 
    

    ここのモジュールMod1Mod2からインポートされたそれらと同様に全てのローカル定義をエクスポートするが、Mod3からインポートされたものは異なる。

    Mがエクスポートリストをもつモジュールでない限り、または少なくとも1つのインポート宣言によって(修飾されるかどうかにかかわらず)インポートされたモジュールでない限り、module Mをエクスポートリストで使うことはエラーとなる。

エクスポートリストは累積される。 すなわち、エクスポートリストによってエクスポートされるエンティティのセットはリストの個々のアイテムによってエクスポートされたエンティティの和集合である。

エンティティがどのようにエクスポートされているものであっても、インポートしているモジュールには違いがない。 例えば、データ型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は異なる実体であると仮定する)にエクスポートリスト内で衝突する名前がある。

インポート宣言

impdeclimport [qualified] modid [as modid] [impspec]
| (empty declaration)
impspec( import1 , … , importn [ , ] )(n ≥ 0)
|hiding ( import1 , … , importn [ , ] )(n ≥ 0)
importvar
| tycon [ (..) | ( cname1 , … , cnamen )](n ≥ 0)
| tycls [(..) | ( var1 , … , varn )](n ≥ 0)
cnamevar | con

モジュールによってエクスポートされたエンティティはモジュールのはじめのimport宣言を伴って他のモジュールのスコープの中へもたらされる。 import宣言はインポートされるモジュールの名前をつけ、任意でインポートされるエンティティを明示する。 単一モジュールは一つ以上のimport宣言によってインポートされるかもしれない。 インポートされた名前はトップレベルの宣言のように扱い、 そのスコープはモジュールの実体全体に及ぶが、ローカルのトップレベルではない束縛によってシャドーイングされることもある。

複数のimport宣言の効果は厳密に累積し、 もしモジュール内のimport宣言のいずれかでインポートされるなら、エンティティはスコープ内にある。 インポート宣言の順序は無関係である。

語彙的に、終端期号"as""qualified""hiding"はそれぞれreservedidではなくvaridである。 それらはimport宣言の文脈内でのみ特別な意味を持ち、変数のようにも使われることが出来る。

何がインポートされるか

正確にどのエンティティがインポートされるかは次に3つの方法の一つで明示されることが出来る。

  1. インポートされるエンティティは丸括弧の中のリスト化しているものによってはっきりと明示されることが出来る。 そのリストの中のアイテムは修飾子が許可されないこととmodule <em>modid</em>エンティティが許可されないことを除いてエクスポートリスト内のものと同様に同じ形式を持つ。 インポートの形式(..)が型またはクラスで使われるとき、(..)はモジュールからエクスポートされるコンストラクタまたはメソッド、フィールド名の全てを参照する。

    そのリストはインポートされるモジュールによってエクスポートされるエンティティのみに名前をつけないといけない。 リストは空でもよく、その場合はインスタンス以外はインポートされない。

  2. エンティティは形式hiding(inport1, ..., importn)を使うことによって除外されることができる。 そして名前がつけられたモジュールによってエクスポートされる全エンティティはリスト内で名前を付けられたものを除いてインポートされるべきことを明示する。 データコンストラクタは関連する型で接頭辞をつけられることなく隠れているリスト内で直接名前をつけることが出来る。 例えば、

    import M hiding (C)
    

    Cと名前を付けられるあらゆるコンストラクタまたはクラス、型は除外される。 対象的にインポートリスト内でCを使うことはクラスまたは型のみに名前をつける。

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

  3. 最後にもしimpspecは省かれるなら、そのとき明示されたモジュールによってエクスポートされるエンティティはインポートされる。

修飾されるインポート

セクション5.3.1のルールの下で各エンティティに対して、トップレベルの環境は拡張される。 もしインポート宣言がqualifiedキーワードを使っていたなら、エンティティの修飾された名前のみがスコープ内へともたらされる。 もしqualifiedキーワードが省略されているなら、そのときエンティティの修飾される名前 修飾されない名前の 両者はスコープの中へともたらされる。 セクション5.5.1では修飾される名前のより詳細を述べていく。

インポートされる名前の修飾子はインポートされるモジュールの名前かimport文のas句に与えられるローカルな別名のどちらかである。 このゆえに、修飾子は必ずしもインポートするエンティティが元々宣言されていたモジュールの名前でなくとも良い。

修飾されない名前を除外するための機能は修飾されない名前空間のプログラマによる完全な制御を許し、 ローカルで定義されたエンティティは修飾子つきでインポートされたものと同じ名前を共有することが出来る。

module Ring where  
import qualified Prelude    -- All Prelude names must be qualified  
import Data.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)

ローカルな別名

インポートされるモジュールはas句を使いインポートするモジュールの中でローカルな別名を割り当てられることができる。 例えば、次の

import qualified VeryLongModuleName as C

モジュールの中ではエンティティはVeryLongModuleName.ではなくC.という修飾子を用いて参照されなければならない。 これはインポートされるモジュールで使われる修飾子を変更せずに異なるモジュールをVeryLongModuleNameの代わりにされることも許す。 全ての名前が曖昧さなく解決される限りにおいては、2つ以上のモジュールがスコープ内で同じ修飾子を使用することは正当である。 例えば、

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

このモジュールはFooBazが両方fをエクスポートしないときに限り正当である。

as句はqualifiedimportではない式でも使われることができる。

import Foo as A(f)

この宣言はfA.fをスコープの中にもたらす。

上記のインポートルールを明らかにするため、xyをエクスポートするモジュールAを想定してほしい。 そのときこの表は名前が明記されたインポート式によってスコープの中へもたらされることを示す。

インポート宣言スコープの中にもたらされる名前
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)。