The Haskell 98 Report
top | back | next | contents | function index
モジュールは、値、データ型、型シノニム、クラスなど (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 を見よ。)
モジュールは値束縛、データ型、型シノニム、クラス、そのほかに対する 宣言を含む相互再帰的有効範囲を定義する。(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 宣言のリス ト(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 をその値の 名前として与えられ名前付けされる。演算子は括弧でかこって、 qvarid としな ければならない。
data 宣言あるいは newtype 宣言による代数データ型 T 次の 3 つ方法のうちどれかによって名前を付けることができる。
type 宣言による型シノニム T は T という形 式で名 前を与えることができる。ここで、T はスコープ内にあると する。
class 宣言による演算 f1,...,fn をもつ クラス C は次の 3 つの方法のどれかで名前があたえられる。
"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 か らインポートしたものはエクスポートしていない。
エクスポートリスト中で 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.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」はそれぞれ、varid であって、reservedid ではない。これらは import 宣言 の文脈中でのみ特別な意味をもち、それ以外では変数として用いることが できる。
どのエンティティがインポートされるかは、正確には次の3つの方法のうち どれかの方法で指定することができる。
インポートするエンティティを明示的に括弧の中でリストアップする。 このリスト内の項目はエクスポートリストないのそれと同じ形式である。 ただし、限定修飾子は許されないことと、「module modid」 というエンティティが許されないことが例外である。(..) とい う形式が型やクラスに対して使用された場合、(..) は、その モジュールからエクスポートされた、そのすべての構成子、メソッド、あるいは フィールド名をあらわす。
インポートリストはインポートするモジュールがエクスポートしている エンティティのみ名指ししなければならない。空のリスト であってもかまわない。その場合はそのインスタンス以外はなにも インポートされない。
hiding(import1
, ... ,
importn) という形式を使用することに
よりエンティティを除外することができる。これは、その名指しされたモ
ジュールによりエクスポートされるすべてのエンティティはそのリスト内
の名指しされたエンティティを除いてすべてインポートすることを指示し
ている。データ構成子は隠蔽リストのなかで直接名指しされその型名が接
頭辞としてつくことはない。それゆえ、
import M hiding (C)
では C という名前の構成子、クラス、型のすべてが除外される。
一方、インポートリスト内で C を使うとこれはクラスあるいは
タイプだけが対象となる。
インポートされるモジュールによってエクスポートされてない エンティティを隠蔽しようとすることはエラーである。
impspec が省略された場合は、指定されたモジュールにより エクスポートされたすべてのエンティティがインポートされる。
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)
インポートされたモジュールには、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 持ち込む。
上述のインポート規則を明確にするために、モジュール A が x と yをエクスポートしていると仮定する。以下の表は それぞれのインポート宣言によってスコープに持ち込まれる名前を示すもの である。
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 節)
インスタンス宣言はインポートあるいはエクスポートリストで明示的に 名前を付与することはできない。ひとつのモジュール内の有効範囲にある インスタンスは常にすべてエクスポートされ、どのような インポートもインポートされたモジュールからのすべての インスタンスを持ち込む。したがって、インスタンス宣言は 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>>"
修飾された名前 は modid.name のように書く(2.4 節)。 修飾された名前はスコープ内に持ち込まれる。
あるモジュールが、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.sin と F.sin はともに、
どちらの sin かが曖昧にならないように、修飾されている必要が
ある。しかしながら、F の最初の行の型シグネチャ中の
修飾されていない sin は曖昧なとろなく、sin の
局所宣言を参照している。
ひとつの Haskell プログラム内のすべてのモジュールは閉じたもの
でなければならない。すなわち、ソースコードによって明示的に言及さ
れたすべての名前は局所的に定義されているか、あるいは、別のモジュール
からインポートされたものであるかどちらかでなくてはならない。型チェッ
クあるいは別のコンパイル時解析のためにコンパイラが必要とするエンティ
ティは、名前により言及されなければインポートされている必要はない。
Haskell のコンパイルシステムはコンパイルに必要な全ての情報をプログラ
マの助けなしに見つける責任がある。すなわち、ある変数 x のイ
ンポートすることで、これらのエンティティがユーザのプログラムのなかで
名前で参照されないうちに、x のシグネチャ中のデータ型およびク
ラスが当該のモジュールに x と同時に持ち込まれることを要求し
てはいけない。Haskell システムは型チェックやその他の目的のためにエン
ティティに付随するはずのすべての情報を黙ってインポートしなければなら
ない。このようなエンティティは明示的にエクスポートされている必要もな
い。以下のプログラムは、T が M1 をエスケープしてい
るわけではないのにもかかわらず正当なプログラムである。
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 の定義を見つけだす。
Haskell の特徴の多くは、「標準プレリュード」と呼ばれる、標準データ 型、標準クラスおよび標準関数のライブラリとして、Haskell 自身で定義さ れている。Haskell ではこのプレリュードは、モジュール Prelude に含まれている。おおくの定義済みのライブラリモジュールもあり、これら には、利用頻度のより少ない関数や型を供給している。たとえば、配列、表、 入出力のほとんどはどれも標準ライブラリにある。これらは別のドキュメン ト Haskell Library Report [8] の中で定義されている。ライブラリをプレリュードとは別することはでサイ ズを削りプレリュードが複雑になるのを軽減するという利点がある。そうす ることで簡単に同化することができ、プログラマが利用できる有用な名前空 間を増すことができる。
プレリュードとライブラリモジュールが他のモジュールと異るのは、セマ ンティクス(実装ではない)として Haskell の言語定義の固定された部分であ るという点である。その意味は、例えば、コンパイラがこのプレリュードの ソースコードを参考にすることなくプレリュード中の関数の呼出しを最適化 してもよいということである。
Prelude モジュールは`import Prelude'があるか のようにすべてのモジュールに自動的にインポートされるが、これは、明示 的に import宣言によってインポートされない場合に限る。明示的 なインポートに対するこの規約はプレリュード中で定義されたエンティティ を、他のモジュールからインポートする場合と同様に、選択的にインポート することを可能にします。
Prelude 内のエンティティのセマンティクスは、 8 章で与えられている Haskell で書かれた Prelude の実装によって指定される。いくつ かの型( Int など)および、いくつかの関数(たとえば、 Int の加算など)については直接 Haskell で指定することはできな い。このようなエンティティの扱いは実装に依存するので、 この 8 章では形式的に 定義しない。Prelude はタプルの扱いについても完全なもの ではない。タプルの族およびそのインスタンス宣言は無限にあるはずだが、 実装はひとつの図式をしめすだけである。
8 章は Prelude モジュールを PreludeList、 PreludeIO などのいくつかの他のモジュールを用いて定義している。 これらのモジュールは、Haskell 98 の一部ではなく、一部だけ別々 にインポートすることはできない。Prelude モジュールの構造を説 明するのに役立つように存在しているにすぎない。言語定義の一部ではなく、 実装の一部であると考えられるべきものである。
プレリュードをその場で提供するために別のモジュールを使うあるいは
構築することが可能である。プレリュードは、それが暗黙のうちにインポー
トされるということを除けば、普通の他の 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 に対して新しいインスタ ンスを定義することはできない。
どの Haskell の実装を用いるかによっては、相互再帰的なモジュールの 分割コンパイルは、インポートされたモジュールは、それがコンパイルされ る前に参照されてもいいように付加的な情報が必要となる。すべてのエクス ポートされた値の明示的な型シグネチャが相互再帰を処理するために必要と なる。分割コンパイルの正確な詳細はこのレポートでは定義しない。
構成子なしでデータ型をエクスポートできるということは、抽象データ型
(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