モジュール
モジュールは(他のモジュールからスコープの中へもたらされたリソースの) インポート の一式によって作成された環境の中の値やデータ型、型シノニム、クラスなど(4章を参照)の集まりを定義する。 これらのリソースのいくつかを エクスポート し、他のモジュールでそれらを利用できるようにする。 モジュールの中で定義された、またはその中へインポートされた、あるいはエクスポートされた値または型クラスに参照するために項の エンティティ を使用する。
Haskell プログラム はモジュールの集まりであり、その中の一つから規約により、Main
が呼ばれなければならず、かつmain
値をエクスポートしなければいけない。
プログラムの 値 はモジュールMain
の中の識別子main
の値であり、そしていくつかの型τ
のための型IO τ
の計算結果でなければいけない(7章を参照)。
プログラムが実行されたとき、計算結果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.Bool
とMain
、Foreign.Marshal.Alloc
はすべて有効なモジュール名である。
modid | → | {conid .} conid | (modules) |
モジュール名は新しいコンポーネントを追加すると、元々のモジュール名の子を階層の中に作成し、その階層の中に配置されているように考えることができる。
例えば、モジュールContorl.Monad.ST
はContorl.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)のリストが続き、
必要に応じてインポートされる束縛を制限する。
これには空かもしれないトップレベルの宣言のリストが次に続くであろう(topdecls
、4章)。
モジュールの本体のみで成るモジュールの短縮形式は許される。
もしこれが使われるなら、そのヘッダーは‘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 |
エクスポートリストはモジュール宣言によってエクスポートされるエンティティを識別する。 モジュールの実装はそのモジュールで宣言しているまたは他のモジュールからインポートしているエンティティのみをエクスポートできる。 もしエクスポートリストが省略されるなら、モジュールの中で定義されたすべての値や型、クラスはインポートされるものを除いてエクスポートされる。
エクスポートリストのエンティティは次に従う名前である。
-
値、フィールド名またはクラスメソッドはそれらがモジュールの本体またはインポートによって宣言されたものかどうかにかかわらず、
qvarid
のように値の名前を与えることによって名前をつけることができ、そしてそれはスコープ内でなければならない。 演算子はそれらをqvarids
に変えるために丸括弧で閉じられなければならない。 -
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
データコンストラクタは付随する名前のようなものを除いてエクスポートリストの中で名前をつけることはできない。 なぜならそうでなければ型コンストラクタから見分けられないからだ。
-
data
宣言によって宣言される型シノニムTは形式Tによって名前をつけられることができ、Tはスコープ内にある。 -
class
宣言で宣言される演算f1,...,fnを伴うクラスCは次の3つの方法のひとつから名前をつけることができる。- 形式Cは クラスメソッドを除いて クラスの名前である。
- 形式C(f1,...,fn)はクラスとそのメソッドのいくつかまたは全ての名前である。
- 略された形式C(..)は(修飾されるされないか関係なく)スコープにあるクラスとその全てのメソッドの名前である。
全てのケースにおいて、Cはスコープになければならない。 2番目の形式の中で、(修飾されない)付随する名前fiのひとつは次の場合にのみ正当である。 (a) Cのクラスメソッドの名前である。かつ (b) そのクラスメソッドが修飾されるされない名前の下のスコープ内にあるかどうかにかかわらずモジュール本体のスコープ内にある。
-
形式
module M
は修飾されない名前"e"
と修飾される名前"M.e"
の両方を伴うスコープ内にある全エンティティのセットの名前である。 このセットは空でもよい。 例えば、module Queue( module Stack, enqueue, dequeue ) where import Stack ...
ここのモジュール
Queue
はStack
からインポートされた全エンティティを略して書くためそのエクスポートリスト内のモジュール名Stack
を使う。 モジュールは構文"module M"
内のそれが保有する名前を使うエクスポートリストの中で保有するローカルな定義の名前をつけることができる、 なぜなら、ローカル宣言は修飾されるされない名前の両方をスコープの中へもたらす(セクション5.5.1)。 例えば、module Mod1( module Mod1, module Mod2 ) where import Mod2 import Mod3
ここのモジュール
Mod1
はMod2
からインポートされたそれらと同様に全てのローカル定義をエクスポートするが、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.g
とg
の間(C.g
とg
は異なる実体であると仮定する。モジュールは相互再帰的にインポートできることを思い出してほしい。)と
module B
とC.f
の間(B.f
とC.f
は異なる実体であると仮定する)にエクスポートリスト内で衝突する名前がある。
インポート宣言
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"
はそれぞれreservedidではなくvaridである。
それらはimport
宣言の文脈内でのみ特別な意味を持ち、変数のようにも使われることが出来る。
何がインポートされるか
正確にどのエンティティがインポートされるかは次に3つの方法の一つで明示されることが出来る。
-
インポートされるエンティティは丸括弧の中のリスト化しているものによってはっきりと明示されることが出来る。 そのリストの中のアイテムは修飾子が許可されないことと
module <em>modid</em>
エンティティが許可されないことを除いてエクスポートリスト内のものと同様に同じ形式を持つ。 インポートの形式(..)
が型またはクラスで使われるとき、(..)
はモジュールからエクスポートされるコンストラクタまたはメソッド、フィールド名の全てを参照する。そのリストはインポートされるモジュールによってエクスポートされるエンティティのみに名前をつけないといけない。 リストは空でもよく、その場合はインスタンス以外はインポートされない。
-
エンティティは形式
hiding
(inport1, ..., importn)を使うことによって除外されることができる。 そして名前がつけられたモジュールによってエクスポートされる全エンティティはリスト内で名前を付けられたものを除いてインポートされるべきことを明示する。 データコンストラクタは関連する型で接頭辞をつけられることなく隠れているリスト内で直接名前をつけることが出来る。 例えば、import M hiding (C)
C
と名前を付けられるあらゆるコンストラクタまたはクラス、型は除外される。 対象的にインポートリスト内でC
を使うことはクラスまたは型のみに名前をつける。インポートされたモジュールによって実際にエクスポートされないエンティティを隠すことはエラーである。
-
最後にもし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
このモジュールはFoo
とBaz
が両方f
をエクスポートしないときに限り正当である。
as
句はqualifiedimport
ではない式でも使われることができる。
import Foo as A(f)
この宣言はf
とA.f
をスコープの中にもたらす。
例
上記のインポートルールを明らかにするため、x
とy
をエクスポートするモジュール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)。