The Haskell 98 Report
top | back | next | contents | function index
この章では、Haskell の宣言の構文と非形式的セマンティクスに ついて解説する。
module | -> | module modid [exports] where body | |
| | body | ||
body | -> | { impdecls ; topdecls } | |
| | { impdecls } | ||
| | { topdecls } | ||
topdecls | -> | topdecl1 ; ... ; topdecln | (n>=1) |
topdecl | -> | type simpletype = type | |
| | data [context =>] simpletype = constrs [deriving] | ||
| | newtype [context =>] simpletype = newconstr [deriving] | ||
| | class [scontext =>] tycls tyvar [where cdecls] | ||
| | instance [scontext =>] qtycls inst [where idecls] | ||
| | default (type1 , ... , typen) | (n>=0) | |
| | decl | ||
decls | -> | { decl1 ; ... ; decln } | (n>=0) |
decl | -> | gendecl | |
| | (funlhs | pat0) rhs | ||
cdecls | -> | { cdecl1 ; ... ; cdecln } | (n>=0) |
cdecl | -> | gendecl | |
| | (funlhs | var) rhs | ||
idecls | -> | { idecl1 ; ... ; idecln } | (n>=0) |
idecl | -> | (funlhs | var) rhs | |
| | (空) | ||
gendecl | -> | vars :: [context =>] type | (型シグネチャー) |
| | fixity [integer] ops | (結合性宣言) | |
| | (空宣言) | ||
ops | -> | op1 , ... , opn | (n>=1) |
vars | -> | var1 , ... , varn | (n>=1) |
fixity | -> | infixl | infixr | infix |
構文範疇 topdecls の宣言は Haskell のモジュール (5 章)のトップレベルでのみ許されている。 一方、decls はトップレベルでも、入れ子になったスコープ(たとえば、 let や where の構成の中)でも使用が許されている。
説明のために、宣言を次の 3 つのグループにわける:
Haskell には (整数や浮動小数のようにな)「処理系に直接組み込まれた」 プリミティブ型があるが、ほとんどの「組込み」データ型はtype宣 言やdata宣言を用いた標準のHaskellコードで定義されている.こ れらの「組み込み」データ型については 6.1節で詳しく説明する.
Haskell は、型の静的セマンティクスを与えるのに、伝統的な Hindley-Milner の多相型システム[3、 5]を用いるが、これを多重定義 関数を導入するための構造的方法をもたらす、型クラス(ある いは単にクラス)で拡張し、それを用いる。
class 宣言(4.3.1 節) は新たな型クラスを導入するものであり、その多重定義演算は、そ の型クラスのインスタンスであるすべての型でサポートされなければならな い。instance 宣言 (4.3.2 節)はある型が、あるク ラスのインスタンスであること、その名前の型でインスタンス化さ れた——クラスメソッドと呼ばれる——多重定義演算の定義を含む。
たとえば、演算 (+) および nagate を型
Int および Float 上で多重定義したいとしよう。
Num とよばれる新しいクラスを導入する:
class Num a where -- simplified class declaration for Num
(+) :: a -> a -> a -- (Num is defined in the Prelude)
negate :: a -> a
この宣言は、このように読むことができる。「ある型 a がクラス
Num のインスタンスであるのは、その型の上で、適切な型の(多重
定義)クラスメソッドが存在するときである。」
そして、Int と Float がこのクラスのインスタンスで
あることを宣言することができる:
instance Num Int where -- simplified instance of Num Int
x + y = addInt x y
negate x = negateInt x
instance Num Float where -- simplified instance of Num Float
x + y = addFloat x y
negate x = negateFloat x
ここで addInt、negateInt、addFloat および
negateFloat はここでは組み込み関数であるとしておく。しかし、
一般には、どのようなユーザ定義の関数であってもよい。上の最初の宣言は、
「Int はクラス Num のインスタンスのひとつであること
は、(クラスメソッド) (+) および negate の定義により
確かたど証明される」と読むことができる。
型と構成子クラスのさらなる例は、Jones の論文 [7]、あるいは、Wadler と Blott の論文[12]に見ること ができる。「型クラス」という用語は、元々は Haskell 1.0 の型システムを 記述するのに用いられていたものであり、「構成子クラス」の用語は元々の 型クラスを拡張を記述するのに用いられていたものである。このレポートで はすでに、これらの用語を分けて用いる理由はなく、「型クラス」は元々の Haskell 型クラスとJones によって導入された構成子クラスの両方を含んで いる。
型の式の正当性を確かめるために、これらを別の種に分類する。 種は次の 2 つの可能性のどちらかの形式である。
記号 * はすべての引数なしの型構成子を表す。
もし、k1 および k2 が種ならば、 k1-> k2 は種 k1 のある型を引数としてとり、 結果として、種 k2 のある型を返す ような関数の型の種である
値の式の正当性が型推論により検査されるのと同様の方法で、型の式の正 当性は、種の推論により検査される。 しかしながら型推論の場合とちがい、 種の推論は全面的に暗黙のうちに行われ、この言語の目に見える部分ではな い。種の推論は 4.6 節で議論す る。
type | -> | btype [-> type] | (関数の型) | |
btype | -> | [btype] atype | (型の適用) | |
atype | -> | gtycon | ||
| | tyvar | |||
| | ( type1 , ... , typek ) | (タプルの型、k>=2) | ||
| | [ type ] | (リストの型) | ||
| | ( type ) | (括弧でくくられた構成子) | -->||
gtycon | -> | qtycon | ||
| | () | (ユニットの型) | ||
| | [] | (リストの構成子) | ||
| | (->) | (関数の構成子) | ||
| | (,{,}) | (tupling constructor) | (タプルの構成子) |
Haskell の型の式の構文は上記で与えられる。データの値がデータ構成子 により構築されるのとちょうど同じように、型の値は、型構成子により構築 される。データ構成子と同様に、型構成子の名前は大文字ではじまる。デー タ構成子とはちがい、中置型構成子は認められていない(ただし、 (->) は例外)。
型の式の基本形式は以下のとおりである。
型変数は小文字ではじまる識別子として書く。型変数の種はそれが出 現した文脈により暗黙裡に決定される。
型構成子。殆どの型構成子は大文字ではじまる認識子として書く。 たとえば、
型適用。もし、t1 が種 k1 -> k2 の型で t2 が種 k1 の型ならば、 t1 t2 は 種 k2 の型式である。
(t) という形式の括弧でくくられた 型は、型 t と同一である。
例えば、型式 IO a は定数 IO の変数 a への適用と理解することができる。IO 型構成子は種 * -> * であるから、変数 a および、式全体 IO a の種は * になるはずである。一般に種の推論(4.6 節を見よ)の過程はユーザ定義デー タ型、型シノニムおよびクラスの種を正しく決定するのに必要である。
いくつかの型式を旧来の方法で表記するための特別な構文が用意されてい る。
関数の型 は t1 -> t2 という形式で、これは、型 (->) t1 t2 と同等である。関数の矢印は右結合である。
タプル型 は (t1,..., tk) (このとき k>=2) という形式で、これは、型 (,...,) t1 ... tk (ここでは括弧のなかには k-1 個のコンマがあ る)と同等である。これは、k-タプルの型を表しており、最初の要素の 型は t1、二番目は t2、 などとなっている(3.8 節および 6.1.4 節を見よ)。
リスト型は [t] という形式 でこれは、[] t の型と同等である。これは型 t の要素のリストの型を表す(3.7 節および 6.1.3 節を見よ)。
これらの特別な構文形式は、どのスコープにあるかにかかわらず、つねに 関数、タプル、リストに対する組み込みの型構成子を表している。同様に 前置の型構成子 (->)、[]、()、 (,) なども常に組み込みの型構成子を表わす。これらに修飾子を付 けこと、および、インポートおよびエクスポートのリストで言及することは できない(5 章)。(それゆえ、上のよ うな特別な生成規則 "gtycon" がある。)
タプル、リスト、関数の型は特別な構文を持つが、同等の機能をもつユー ザ定義の代数型と同じセマンティクスをもつ。
式と型は一貫した構文を持つことに注意せよ。もし、 ti が式あるいはパターン ei の型であるなら、式 (\e1 -> e2), [e1] および (e1,e2) の型はそれぞれ、(t1 -> t2), [t1] および (t1,t2) である。
ひとつの例外(クラス宣言(4.3.1 節)中の区別された型変数)をのぞき、Haskell の型式中の変数は全て全称修 飾されていると看倣される。全称修飾に対応する明示的な構文はない [3]。たとえば、型の式 a -> a は型 forall a. a ->a を表して いる。Haskell のプログラムの型の議論をする際、全称修飾されていること をはっきりと示すために明示的に修飾子を書くことは多々ある。明示的に修 飾された型を書く場合には forall a のスコープは右へ可能なかぎり 拡張される。たとえば、forall a . a -> a は forall a . (a -> a) を意味する。
context | -> | class | |
| | ( class1 , ... , classn ) | (n>=0) | |
class | -> | qtycls tyvar | |
| | qtycls ( tyvar atype1 ... atypen ) | (n>=1) | |
qtycls | -> | [ modid . ] tycls | |
tycls | -> | conid | |
tyvar | -> | varid |
クラス表明は qtycls tyvar の形式で、パラメタの型 tyvar がクラス qtycls のメンバであることを示す。クラス 識別子は大文字で始まる。文脈は 0 個あるいはそれ以上のクラス 表明からなり、次のような一般形をとる。
( C1 u1, ..., Cn un )
ここで、C1, ..., Cn はクラス識別子であり、各 u1, ..., un は型変 数もしくは型変数を一つ以上の型に適用したものである。n=1 の場合 は外側の括弧を省略してもよい。一般に、文脈を表すのに cx を用い、 型 t が文脈 cx で制限されることを表すのに cx => t と書く。文脈 cx は t で参照 される型変数を 1 つだけ含むものでなければならない。利便のために、文脈 cx が空の場合にもcx => t と書くが、 この場合には具象構文では=> は含まれない。
この節では、型システムの非形式的な詳細を示す。(Wadler and Blott [12] と Jones [7] とで、型と構成子クラスとに ついてそれぞれより詳しく議論されている。)
Haskell の型システムはプログラム中のそれぞれの式に型を付与
するものである。一般には型は forall u. cx =>t
のような形式をとる。ここでは u は型変数
u1, ..., un の集合
である。このような型では、ui で文脈
cx の中で自由な全称修飾された型変数はどれも、型 t のな
かで自由でなければならない。さらに、文脈 cx は上の 4.1.3 節で与えた形式でなければ
ならない。たとえば、以下は正当な型である。
Eq a => a -> a
(Eq a, Show a, Eq b) => [a] -> [b] -> String
(Eq (f a), Functor f) => (a -> b) -> f a -> f b -> Bool
第三の型では、制約 Eq (f a) はこれ以上簡単にするこ
とはできない。それは、f が全称修飾されているからである。
式 e の型は e のなかの自由変数の型を与える型環境 および、どの型がどのクラスのインスタンスであるかを宣言するク ラス環境に依存する。(ある型があるクラスのインスタンスになるため には instance 宣言あるいは deriving 節を通じてのみ である。)
型は(以下に指定する)一般化順序により関係づけられる。(与えられた環境 で)特定の式に割り当てうる最も一般的な型をその式の主型 (principal type)と呼ぶ。Haskell の拡張 Hindley-Milner 型システム はすべての式の主型を推論できる。これには正しく使われる多重定義された クラスメソッドも含まれる(4.3.4 節で述べるように、クラスメソッドでは曖昧なものが起こりうるが)。それ故 に明示的な型付け(型シグネチャ)は普通は必須ではない(3.16 節および 4.4.1 節を見よ)。
型 forall u. cx1 =>t1 は次のような場合かつその場合にかぎり、 型 forall w. cx2 =>t2 に比してより一般的という。 すなわち、ある置換 S が存在し、そのドメインが
上の文脈の主な要点は、型 forall u. cx =>t
の値が型 s においてインスタンス化されるのは、文脈
cx[s/u] が保存される場合でその場合
にかぎるということである。たとえば、double という関数を考え
てみよう。
double x = x + x
double の最も一般的な型は
forall a. Num a =>a ->a である。
double は Int 型の値に(a を Int) に
インスタンス化することにより)適用することができる。それは、
Int はクラス Num のインスタンス故に
Num Int が保存されるからである。
しかしながら、double は型 Char の値に通常は適用する
ことはできない。それは Char は通常ではクラス Num の
インスタンスではないからである。ユーザはインスタンス宣言して、
double が Char に適用できるようにするということもで
きる。
この節では、代数的データ型(data 宣言)、名前付替データ型 (newtype 宣言および型シノニム(type 宣言)について解 説する。これらの宣言はモジュールのトップレベルにしか現れない。
topdecl | -> | data [context =>] simpletype = constrs [deriving] | |
simpletype | -> | tycon tyvar1 ... tyvark | (k>=0) |
constrs | -> | constr1 | ... | constrn | (n>=1) |
constr | -> | con [!] atype1 ... [!] atypek | (arity con = k, k>=0) |
| | (btype | ! atype) conop (btype | ! atype) | (infix conop) | |
| | con { fielddecl1 , ... , fielddecln } | (n>=0) | |
fielddecl | -> | vars :: (type | ! atype) | |
deriving | -> | deriving (dclass | (dclass1, ... , dclassn)) | (n>=0) |
dclass | -> | qtycls |
constr の優先順位は式のそれと同じである。普通の構成子の適用 は、中置構成子の適用より優先順位が高い(ので、 a : Foo a は a : (Foo a))と構文解析される。
代数的データ宣言は、以下の形式をもつ。
data cx => T u1 ... uk = K1 t11 ... t1k1 | ...| Kn tn1 ... tnkn
ここで、cx は文脈である。この宣言は、以下のように与えられた 一連のデータ構成子 K1, ..., Kn で構成される新しい型構成子 T を導入す る。このレポートでは特に修飾せずに使う「構成子」は、常に「データ構築 子」を意味する。
データ構成子に型は以下のように与えられる。
Ki :: forall u1 ... uk. cxi =>ti1 ->...->tiki ->(T u1 ... uk)
ここで cxi はこれらの型変数のみが、型 ti1, ...,tiki で自由になるような cx の最大部分集合である。型変数 u1 か ら uk は別のものであり、cx および tij で出現する。その他の型変数が、cx 中あるいは右辺にあらわれた場合は、静的エラーとなる。新たな型定数 T は k1->...->kk->* という形式の種である。ここで、 引数変数 ui の種 ki は種の推論により4.6 節で説明するように決定される。 すなわち、T は 0 と k との間の引数のどこの型式の なかで使ってもよい。
たとえば、宣言
data Eq a => Set a = NilSet | ConsSet a (Set a)
は種が *->* である型構成子 Set を導入し、また、以下のよう
な型の構成子 NilSet および ConsSet を導入する。
NilSet | :: forall a. Set a |
ConsSet | :: forall a. Eq a =>a ->Set a ->Set a |
例で、ConsSet に対する多重定義型は ConsSet
がクラス Eq のインスタンスである型の値のみに適用できることを
保証する。ConsSet に対する照合は、制約
Eq a も齎す。たとえば、
f (ConsSet a s) = a
で、関数 は
Eq a => Set a -> a と推
論された型をもつ。この data 宣言の文脈はこれ以外どこにも影響
をあたえない。
データ型の構成子の可視性(すなわち、データ型の「抽象性」)は、そのデー タ型が定義されたモジュールの外に対しては、5.8 節で解説する、エクスポート リスト中のデータ型の名前により制御される。
data 宣言の deriving 部は必須ではないが、これは 導出されたインスタンスの宣言であり、これについては 4.3.3 節て解説する。
k 引数のデータ構成子は、k 個の構成要素を持つオブジェ クトを創る。これらの構成要素は通常、式やパターンのなかで構成子の引数 としての位置によりアクセスされる。大規模なデータ型についてはそのデー タオブジェクトの各構成要素にフィールドラベルを付与するのが便 利である。こうすれば、特定のフィールドをそのフィールドの構成子中の位 置とは無関係に参照することができる。
data 宣言における { } 構文を用いた構成子
定義はその構成子の構成要素にラベルを割り当てる。フィールドラベルを用
いた構成子はそれを用いない構成子と自由に混在して使用することができる。
対応するフィールドラベルをもつ構成子は、普通の構成子としても使用する
ことができる。ラベルを使うというのは、場所指定形構成子への操作に対す
る単なる簡便法であるといえる。場所指定形構成子への引数はラベルフィー
ルドの出現順と同じ順であらわれる。たとえば、
data C = F { f1,f2 :: Int, f3 :: Bool }
が定義する型と構成子は以下の定義によるものと同一である。
data C = F Int Int Bool
フィールドラベルを用いた操作については、3.15 節で解説した。data 宣言
では、複数の構成子に対して、フィールドの型が型シノニムによりすべてお
なじなら、同じフィールドラベルを使用することができる。ラベルは有効範
囲内で2つ以上の型により共有することはできない。フィールド名は通常の変
数およびクラスメソッドとトップレベルの名前空間を共有しており、有効範
囲内の他のトップレベルの名前と衝突してはならない。
パターン F {} は F レコード構文で宣言さ れたかどうかにかかわらず、構成子 F で構築されたすべての 値に照合する。
データ構成子の適用時に常に、構成子の各引数が評価されるのは代数的デー タ型宣言中の対応する型が正格フラグを持つ場合でありその場合に限る。こ のフラグは 「 ! 」で表現される。字句としては 「 ! 」は通常の変数記号であって予約済み演算子ではない。データ宣言 の引数型の文脈内でのみ特別な意味をもつ。
変換:以下の形式data cx => T u1 ... uk = ... | K s1 ... sn | ...
ここで、各 si は !
ti または ti
の形式である。式の中の K のすべての出現を (\ x1 ... xn -> ( ((K op1 x1) op2 x2) ... ) opn xn) で置き換える。ここで、opi は、もし、 si が ti の形式 であれば、遅延適用関数 $ であり、もし、! ti の形式であれば、正格適用関数 $! (6.2 節を見よ)である。 K へのパターン照合は、正格フラグには影響しない。 |
topdecl | -> | type simpletype = type | |
simpletype | -> | tycon tyvar1 ... tyvark | (k>=0) |
型シノニム宣言は従来の型と同一と新しい型を導入する。形式は以下の通 り。
type T u1 ... uk = t
これにより、新しい型構成子 T が導入される。型 (T
t1 ... tk) は型
t[t1/u1, ...,
tk/uk] と同等であ
る。型変数 u1 から uk
はそれぞれ別のものでなければならず、その有効範囲は t でなけれ
ばならない。t においてこれ以外の型変数があらわれたときには、静
的エラーとなる。新しい型構成子 T の種は k1->...->kk->k
という形式である。ここで、引数 ui の種 ki および右辺の t の種
k は 4.6 節で解説する種の推論により決
定される。たとえば、以下の定義はリストの型構成子の別の表記を提供する
ために用いることができる。
type List = []
型シノニム宣言により導入された型構成子記号 T は部分適用するこ
とはできない。T をすべての引数を与えることなく使用すると静的エ
ラーとなる。
再帰的および相互再帰データ型は許されてはいるが、型シノニムでは許さ
れず、代数的データ型定義においてのみ許される。たとえば、
type Rec a = [Circ a]
data Circ a = Tag [Rec a]
は許されるが、一方で、
type Rec a = [Circ a] -- invalid
type Circ a = [Rec a] -- invalid
は許されない。同様に
type Rec a = [Rec a] は許されない。
型シノニムは型シグネチャの可読性を向上させるための構文上の機構にす ぎない。型シノニムとその定義は完全に置き換え可能である。
topdecl | -> | newtype [context =>] simpletype = newconstr [deriving] | |
newconstr | -> | con atype | |
| | con { var :: type } | ||
simpletype | -> | tycon tyvar1 ... tyvark | (k>=0) |
以下の形式の宣言
newtype cx => T u1 ... uk = N t
は既存の型を表現する新しい型を導入する。型 (T u1 ... uk) は データ型 t の名前を付け替える。型シノニムと違うのは、これが別 の型を創り出すということである。すなわち、元の型へあるいは元の型から は明示的に型変換を行わなければならない。さらに型シノニムと違って、 newtype は再帰的な型を定義するのに使うことができる。構成子 N は式のなかでは、ある値を型 t から型(T u1... uk) へ 変換する。パターンの中で使われる N はある値を型 (T u1 ... uk) か ら型 t 変換する。これらの型変換は実行時のオーバヘッドなしで実 装することができる。newtype はオブジェクトの本質的な表現を変 更しない。
新しいインスタンス(4.3.2 節 を見よ)は newtype により定義された型に対して定義することがで きる。しかし、型シノニムに対しては定義できない。newtype によ り創り出された型は代数的データ型とは別ものである。代数的データ型の表 現は余分な間接参照をもち、その表現へのアクセスはより非効率である。こ の違いはパターン照合に対する異る規則(3.17 節を見よ)を反映したもので ある。代数的データ型とはちがい、newtype 構成子 N は リフト されない。すなわち、N _|_ は _|_ である。
以下の例は、data (代数的データ型)、type (型シノニ
ム) および、newtype (型名の付け替え)の違いを明確にするもので
ある。以下の宣言が与えられているものとしよう。
data D1 = D1 Int
data D2 = D2 !Int
type S = Int
newtype N = N Int
d1 (D1 i) = 42
d2 (D2 i) = 42
s i = 42
n (N i) = 42
式 ( d1 _|_)、(
d2 _|_) および (d2 (D2
_|_) ) はすべて _|_ と同等である。一方、
( n _|_)、(
n ( N
_|_) )、( d1
( D1 _|_) ) および
( s _|_) はすべて
42 である。特に、( N
_|_) は _|_ である一方で、(
D1 _|_) は _|_ と同等ではない。
newtype 宣言の必須ではない導出部は、data 宣言にお ける導出構成要素と同じに扱う。4.3.3 節を見よ。
newtype 宣言ではフィールド名の構文を使うことができる。もち
ろん一フィールドだけのものである。すなわち、
newtype Age = Age { unAge :: Int }
は、構成子と脱構成子の有効範囲に
Age :: Int -> Age
unAge :: Age -> Int
を導入する。
topdecl | -> | class [scontext =>] tycls tyvar [where cdecls] | |
scontext | -> | simpleclass | |
| | ( simpleclass1 , ... , simpleclassn ) | (n>=0) | |
simpleclass | -> | qtycls tyvar | |
cdecls | -> | { cdecl1 ; ... ; cdecln } | (n>=0) |
cdecl | -> | gendecl | |
| | (funlhs | var) rhs |
クラス宣言は新しいクラスとその上の演算(クラスメソッド )を導入する。クラス宣言は次のような一般的な形式をもつ。
class cx => C u where cdecls |
これは新しいクラス名 C を導入する。型変数 u の有効範 囲は、クラス本体のクラスメソッドのシグネチャのみである。文脈 cx は C のスーパークラスを以下に解説するように指定する。 cx 中で参照できる型変数は u のみである。
スーパークラスの関連は循環してはならない。すなわち、非循環有向グラ フを形成しなければならない。
class 宣言の cdecls 部分は、3 種類の宣言を含む。
クラス宣言は新しいクラスメソッドvi を 導入する。クラスメソッドの有効範囲は class 宣言の外側へ拡張 される。クラス宣言のクラスメソッドは cdecls 中で、明示的に型シ グネチャ与えられた
vi :: cxi => ti
にちょうど対応する。クラスメソッドは変数束縛およびフィールド名と トップレベルの名前空間を共有する。有効範囲内で他のトップレベルの束 縛と衝突してはならない。このことは、クラスメソッドはトップレベルの 定義、フィールド名、あるいは他のクラスメソッドと同じ名前をもつこと ができないということである。
トップレベルのクラスメソッド vi の型は
vi :: forall u,w. (C u, cxi)=>ti
である。ti は u に言及していなけ
ればならない。これは、u 以外の w について言及してもよい。
この場合には、vi の型は u および
w の両方において多相的である。cxi は
w のみ含むことができ、u は含むことができない。たとえば、
class Foo a where
op :: Num b => a -> b -> a
ここで、op の型は forall a, b. (Foo a, Num b) =>a ->b ->a である。
cdecls はどのクラスメソッドについても結合性宣言を 含むことができる(それ以外の値については不可)。しかしながら、クラス メソッドはトップレベルの値として宣言されるのであるから、クラスメソッ ドに対する結合性宣言は、そのクラス宣言の外のトップレベルに出現する ことも可能である。
最後に、cdecls はどの vi に対して
もデフォルトクラスメソッドを含むことができる。
vi に対するデフォルトクラスメソッドは、も
し、それに対応する束縛が特定の instance 宣言 (4.3.2 節を見よ)で与えられない
場合に使用される。左辺は値あるいは関数定義のみであるということを除
けば、デフォルトメソッド宣言は通常の値定義である。たとえば、
class Foo a where
op1, op2 :: a -> a
(op1, op2) = ...
は許されない。それは、デフォルト宣言の左辺がパターンだからである。
これら以外の宣言は、cdecls では許されない。
where 部分をもたない class 宣言はコレクションクラ
スを元のクラスのクラスメソッドをすべて含むさらに大きなコレクションク
ラスに結合するのに役立つ。たとえば、
class (Read a, Show a) => Textual a
このような場合、もしある型がすべてのスーパークラスのインスタンスであ
る場合、たとえ、サブクラスが直接クラスメソッドをもたないにせよ。自動
的にそのサブクラスのインスタンスであるというわけではない。
instance 宣言は where 部分なしで、明示的に与えねば
ならない。
topdecl | -> | instance [scontext =>] qtycls inst [where idecls] | |
inst | -> | gtycon | |
| | ( gtycon tyvar1 ... tyvark ) | (k>=0, tyvars distinct) | |
| | ( tyvar1 , ... , tyvark ) | (k>=2, tyvars distinct) | |
| | [ tyvar ] | ||
| | ( tyvar1 -> tyvar2 ) | (tyvar1 and tyvar2 distinct) | |
idecls | -> | { idecl1 ; ... ; idecln } | (n>=0) |
idecl | -> | (funlhs | var) rhs | |
| | (empty) |
インスタンス宣言はクラスのインスタンスを導入する。
class cx => C u where { cbody }
を class 宣言としよう。これに対応するインスタンス宣言の一般形 は次のようになる。instance cx' => C (T u1 ... uk) where { d }
ここで k>=0 および T は型シノニムではない。インス タンス化された構成子、(T u1 ... uk) は型構成子を単純な型変数 u1, ... uk に適用し たものである。これらの型変数は別々のものでなければならない。
このことは以下のようなインスタンス宣言を禁じるものである。
もし、いくつかのクラスメソッドに対して束縛が与えられなければ、 class 宣言中の対応するデフォルトクラスメソッドが使用される。 もし、このとき、そのようなデフォルトクラスメソッドが存在しない場合に はこのインスタンスのこのクラスメソッドは undefined に束縛さ れ、コンパイル時エラーとはならない。
型 T をクラス C のインスタンスとする instance 宣言は C-T インスタンス宣言と呼ばれ、以下 の制限を受ける。
ひとつの型はそのプログラム中では特定のクラスのインスタンスとして 2 回以上宣言することはできない。
そのクラスと型はおなじ種を持たなければならない。この種は、4.6 節で解説する種の推論を用いて 決定することができる。
インスタンス型 (T u1 ... uk) 中の型変数はそのインスタンスの 文脈 cx' の制約を満すものとしよう。 この仮定のもとでは、次の2 つの条件も満されなければならない。
実際病的な場合を除けば、インスタンス宣言から上のふたつの制約を満 す最も一般的インスタンスの文脈 cx' を推論することは可能で ある。しかしながら、明示てきにインスタンス文脈を書くことは必須で ある。
次はスーパークラスのインスタンスによる制限を説明するものである。
class Foo a => Bar a where ...
instance (Eq a, Show a) => Foo [a] where ...
instance Num a => Bar [a] where ...
この例は正しい Haskell である。Foo は Bar のスーパー
クラスであるから、ふたつめのインスタンス宣言は Num a と
いう仮定のもとで、[a] が Foo のインスタンスである場
合にのみ正しい。最初のインスタンス宣言はまさに、[a] はこの仮
定のもとで Foo のインスタンスであるということを言っているの
である。それは、Eq および Show は Num のスー
パークラスだからである。
もし、二つのインスタンスをつぎのように
instance Num a => Foo [a] where ...
instance (Eq a, Show a) => Bar [a] where ...
と読みかえるとプログラムは不正なものになる。ふたつめのインスタンス宣
言は (Eq a, Show a) という仮定のもとで、
[a] が Foo のインスタンスである場合にのみ正しい。し
かしながら、[a] はより強い Num a という仮定の
もとでのみ、Foo のインスタンスであるからこの制約がたもたれな
い。
instance 宣言のさらなる例については、8 章に見ることができる。
4.2.1 節で言及したように、 data 宣言および newtype 宣言は、必須ではない deriving 形式をもつ。もしこの形式が含まれている場合には、 導出インスタンス宣言は自動的にその名前付のクラスのそれぞれの データ型に対して生成される。これらのインスタンスはユーザていぎ のイン スタンスと同じ制限に支配される。もし型 T について、クラス Cを導出したら、明示的な instance 宣言を通じてか、ある いは、deriving 節中のスーパークラスをインクルードすることで、 C のすべてのスーパークラスに対するインスタンスが、T に 対して存在しなければならない。
導出インスタンスはユーザ定義のデータ型に対して便利に共通に使える演 算を提供するものである。たとえば、Eq クラス中のデータ型に対 する導出インスタンスは演算 == および /= を定義し、 プログラマがそれを定義する手間を省く。
プレリュード中のクラスでそれに対する導出インスタンスが許されているのは Eq、 Ord、 Enum、 Bounded、 Show、および Read だけである。これらすべては、 図 5 で定義されている。これ らのクラスそれぞれについてどのように導出インスタンスが生成されるかの 詳細は 10 章にある。そこ では、どのような場合にそのような導出インスタンスが可能かとういう仕様 も含まれている。標準ライブラリにより定義されているクラスも導出可能で ある。
もし、deriving 形式中で、名前付きのクラス上の instance 宣言を導出することができなければ、静的エラーとなる。 たとえば、すべてのデータ型が Enum のクラスメソッドをただしく サポートできるわけではない。導出されたされたクラスにたいして明示的 に instance 宣言を与えようとすると、この場合も静的エラーとな る。
deriving 形式が data 宣言や newtype 宣言 で省略された場合、そのデータ型に対しては、いかなるインスタンス宣言も 導出されない。すなわち、deriving 形式を省略するとい うことは、空の導出形式、deriving () と同等である。
topdecl | -> | default (type1 , ... , typen) | (n>=0) |
Haskell-スタイルの多重定義に付随する問題は曖昧な型が出来る可能
性があることである。たとえば、10 章で定義されている関数
read と show を利用して、Int および
Bool は Read および Show のメンバーだとし
た場合、式
let x = read "..." in show x
-- invalid
は曖昧である。show および read に対応する型
show | :: forall a. Show a =>a ->String |
read | :: forall a. Read a =>String ->a |
は a を Int にインスタンス化しても、Bool にインスタンス化しても満されるからである。このような式は正しく型付け されないものと考え、静的エラーとなる。
式 e の型 forall u. cx =>t におい て cx にはあり、t にはないような u の型変数 u が存在する場合、式 e は曖昧な型をもつとい う。このような型は不正である。
たとえば、先にあげた show および read を含む式は 曖昧な型をもつ、それはその型が、forall a. Show a, Read a => String であるからだ。
曖昧な型はユーザの入力によってのみ回避される。ひとつの方法は 3.16 節で説明した式の型
シグネチャを使うことである。たとえば、先の曖昧な型をもつ式は、次
のように書くことができる。
let x = read "..." in show (x::Bool)
これで型が曖昧でなくなる。
場合によっては、式の型シグネチャで型を固定するのではなく、型変数と
して同じ型を作るためにべつの意味で曖昧な式が必要となる。この目的のた
めにあるのが、関数 asTypeOf (8 章) である。x
`asTypeOf` y の値は x の値であるが、x
および y は強制的に同じ型になる。たとえば、
approxSqrt x = encodeFloat 1 (exponent x `div` 2) `asTypeOf` x
である( encodeFloat および exponent の説明について
は 6.4.6 節を見よ)。
クラス Num における曖昧性はよくあることである。それで、 Haskell ではこれを解決するもうひとつの方法を提供する。default 宣 言を使う方法である。
default (t1 , ... , tn)
ここで n>=0 であり、各 ti は Num ti が保存されなければなら ない。曖昧な型が発見されるような状況では、曖昧な型変数 v は 以下の 3 つを満す場合にデフォルト宣言可能である。
default 宣言可能なそれぞれの変数は、すべての曖昧な変数のクラスの一 つのインスタンスであるデフォルトリスト中の最初の型で置き換えられる。 このような型が見つからない場合には静的エラーとなる。
ひとつのモジュールではデフォルト宣言はひとつだけ許されており、その効
果はそのモジュール内に限られる。もし、あるモジュールでデフォルト宣言が
ないばあいには以下が仮定される。
default (Integer, Double)
空のデフォルト宣言、default () はそのモジュールのすべて
のデフォルトをオフにする。
以下の宣言はモジュールのトップレベルを含むどの宣言リスト中でも使用 できる。
gendecl | -> | vars :: [context =>] type | |
vars | -> | var1 , ..., varn | (n>=1) |
型シグネチャは変数の型を指定し、文脈をともなうこともある。型シグネ チャに形式は以下のとおり。
v1, ..., vn :: cx => t
これは、1 から n までの各 i について vi :: cx => t であることを表明したものと同等である。各 vi はその型シグネチャが含まれている同じ宣言 リスト中に値束縛をもたなければならない。すなわち、有効範囲外の変数束 縛に対して型シグネチャを与えるのは不正である。さらに、一つの変数に対 しては、たとえ、同じシグネチャであろうとも、2 度以上型シグネチャを与 えることは不正である。
4.1.2 節で言及したように、一つ
の型シグネチャに出現する型変数はどれもそのシグネチャ上で全称修飾を受
けるので、型変数の有効範囲はそれを制約する型シグネチャ内である。たと
えば、以下の宣言
f :: a -> a
f x = x :: a -- invalid
では、ふたつの型シグネチャの a は別ものである。もちろん、こ
れらの宣言は静的エラーとなる。それは x は型 forall
a. a をもたないからである。(x の型は型 f に依存
している。依存型の変数に対するシグネチャを指定する方法はいまのところ
Haskell にはない。このことについては 4.5.4 節で説明する。)
もし、与えられたプログラムが変数 f に対するシグネチャを含む なら、f は使用のたびに宣言された型をもつものとして扱われる。 f の出現ごとに同じ型が推論できない場合には静的エラーとなる。
もし、変数 f が対応する型シグネチャをもたずに定義されている なら、f はその宣言グループ(4.5 節を見よ)外で使用される たびに主型あるいは対応して推論された型をもつものとして扱われ る。しかしながら、型推論がなお可能であるためには、定義の出現および f のその宣言グループないでの使用すべては同じ単相型(4.5.2 節で説明するように、その型 から一般化によって主型が得られる)を持たねばならない。
たとえば、以下のように定義したとしよう。
sqr x = x*x
主型は sqr :: forall a. Num a =>a
->aであり、これにより、sqr 5 あるいは
sqr 0.1 のような適用が可能になる。また、次のように、もっ
と限定された型を宣言することもできる。
sqr :: Int -> Int
しかし、このときには sqr 0.1 のような適用は不正なものに
なる。以下のような型シグネチャ
sqr :: (Num a, Num b) => a -> b -- invalid
sqr :: a -> a -- invalid
は不正である。それはこれらは sqrt の主型よりも一般的な型であ
るからである。
型シグネチャは多相再帰をサポートするのに使うことができる。
以下の例は病的な例であるが、型シグネチャが推論された型よりも一般的な
型を指定するのにどのように使えるかを示している。
data T a = K (T Int) (T a)
f :: T a -> a
f (K x y) = if f x == 1 then f y else undefined
もし、このシグネチャ宣言をとりのぞくと、最初の再帰呼びだしで、
f の引数は T Int なので、f の型は
T Int -> Int と推論される。多相再帰はユー
ザがより一般的な型シグネチャ T a -> a を与
えることを可能にしている。
gendecl | -> | fixity [integer] ops | |
fixity | -> | infixl | infixr | infix | |
ops | -> | op1 , ... , opn | (n>=1) |
op | -> | varop | conop |
結合性宣言は1つ以上の演算子の結合性と束縛の優先順位を与えるものであ る。結合性宣言は型シグネチャが現われるところならどこに現われてもよく、 型シグネチャと同様、特定の演算子の性質を宣言するものである。また、こ れも型シグネチャと同様に、結合性宣言は、その演算子の宣言と同じ宣言グ ループのなかで 1 度だけ出現することができ、どの演算子に対しても高々 1 度の結合性宣言を与えることができる。(クラスメソッドの結合性宣言はめだ たぬ例外で、これはクラス宣言中あるいはトップレベルのどちらかで出現す ることができる。)
結合性には、無結合、左結合、右結合(それぞれ、infix、 infixl、infixr) の 3 種類あり、レベル 0 から 9 まで の 10 段階の優先順位(レベル 0 がもっとも弱く、レベル 9 が最も強い結び 付きをあらわす)がある。もし、数字が省略されれば、それはレベ ル 9 が想定される。結合性宣言をもたない演算子はすべて、 infixl 9 (結合性の利用について詳しくは 3 節を見よ)。表 4.1 はプレリュードで定義されてい る演算子の結合性と優先順位の表である。
Precedence | Left associative operators |
Non-associative operators |
Right associative operators |
9 | !! | . | |
8 | ^, ^^, ** | ||
7 |
*, /, `div`, `mod`, `rem`, `quot` |
||
6 | +, - | ||
5 | :, ++ | ||
4 |
==, /=, <, <=,
>, >=, `elem`, `notElem` |
||
3 | && | ||
2 | || | ||
1 | >>, >>= | ||
0 | $, $!, `seq` |
結合性は特定のエンティティ(構成子あるいは変数)の属性であり、型とお
なじようなものである。結合性はエンティティの名前の属性ではな
い。例えば、
module Bar( op ) where
infixr 7 `op`
op = ...
module Foo where
import qualified Bar
infix 3 `op`
a `op` b = (a `Bar.op` b) + 1
f x = let
p `op` q = (p `Foo.op` q) * 2
in ...
ここで、`Bar.op` は infixr 7、
`Foo.op` は infix 3 であり、f の右辺
の op の入れ子になった定義は、デフォルトの
infixl 9 という結合性をもつ。(入れ子ななった
`op` の定義に入れ子の結合性定義を与えることもできる。)
decl | -> | (funlhs | pat0) rhs |
funlhs | -> | var apat {apat } |
| | pati+1 varop(a,i) pati+1 | |
| | lpati varop(l,i) pati+1 | |
| | pati+1 varop(r,i) rpati | |
| | ( funlhs ) apat {apat } | |
rhs | -> | = exp [where decls] |
| | gdrhs [where decls] | |
gdrhs | -> | gd = exp [gdrhs] |
gd | -> | | exp0 |
この構文では 2 つの場合を区別する。左辺が pat0 であるときパターン束縛が出現す る。それ以外の場合には、この束縛は関数束縛と呼ぶ。どちらの束 縛もモジュールのトップレベルあるいは let あるいは where 構成内であらわれる。
関数束縛は変数を関数値に束縛する。変数 x に対する関数束縛の 一般的形式は、
x | p11 ... p1k | match1 |
... | ||
x | pn1 ... pnk | matchn |
ここで、各 pij はパターンであり、各 matchi は以下の形式、
= ei where { declsi } |
あるいは
| gi1 | = ei1 |
... | |
| gimi | = eimi |
where { declsi } |
であり、ここで、n>=1、1<=i<=n、 mi>=1 である。前者は、後者の特別な 場合の省略形として扱われる。すなわち、
| True = ei where { declsi } |
である。
関数を定義するすべての節は連続していなければならず、各節のパターン の数は同じでなければならない。各照合に対応するパターンの集合は、 線形でなければならない。すなわち、その集合全体でどの変数も 2 度 以上あらわれてはいけない。
関数束縛には関数の値を中置演算子に束縛する別の構文も用意されている。
たとえば、以下の 2 つの関数定義は同等である。
plus x y z = x+y+z
x `plus` y = \ z -> x+y+z
(x `plus` y) z = x+y+z
変換:関数に対する一般的な束縛の形式はセマンティクスとしてはこの等式と同 等である。(たとえば、単純なパターン束縛)x = \ x1 ... xk -> case (x1, ..., xk) of
ここで、xi は新しい識別子 |
パターン束縛は変数を値に束縛する。単純パターン束縛は p = e という形式である。パターン p は前に ~ が暗黙 上ついているかのように反駁不可パターンと同様に遅延照合される。3.12 節の変換を見よ。
パターン束縛の一般形は p match であり、ここでは match は上の関数束縛に対するのと同じ構造をしている。いいかえれ ばパターン束縛は
p | | g1 | = e1 |
| g2 | = e2 | |
... | ||
| gm | = em | |
where { decls } |
変換:上のパターン束縛は以下の単純パターン束縛とセマンティクスとしては同 等である。
|
束縛がパターン束縛であるか関数束縛であるかを見分けるのは簡単である
が、n+k パターンが存在するとこの問題はややこしくなる。4 つ例
をあげると
x + 1 = ...
-- Function binding, defines (+)
-- Equivalent to (+) x 1 = ...
(x + 1) = ...
-- Pattern binding, defines x
(x + 1) * y = ...
-- Function binding, defines (*)
-- Equivalent to (*) (x+1) y = ...
(x + 1) y = ...
-- Function binding, defines (+)
-- Equivalent to (+) x 1 y = ...
最初の二つは、区別することができる。なぜなら、パターン束縛は左辺にお
いて pat0 を持ち、これは p ではなく、
前者は括弧でかこまれない n+k パターンになりえないからである。
let あるいは where 節の関数束縛およびパターン束縛 の静的セマンティクスについてこの節で議論する。
一般的にセマンティクスは通常の Hindley-Milner 推論規則によって与え られる。依存性解析変換はまず多相性を強化するために行なわれる。 値の宣言によって束縛されるふたつの変数は次のどれかの場合、同じ宣 言グループに属する。
以下の規則の適用により、各 let あるいは where 構 成(モジュールのトップレベルの束縛を定義する where 節を含む) は、ひとつの宣言グループの変数のみを束縛し、必要とされる依存性解析を 捕捉する。(同様の変換は、Payton Jones の本 [9] で解説されている。)
Hindley-Milner の型システムは型を let-式に、ふたつの段階で 割り当てる。先ず、宣言の右辺を、全称修飾されていない型を与えて、型付 けする。次にこの型内にあらわれるすべての型変数を、この型環境内の束縛 変数に対応付けられていなければ、全称修飾する。これを一般化 (generalization)という。最後に、let-式の本体を型付けす る。
たとえば、以下の宣言を考えよ。
f x = let g y = (y,y)
in ...
g の定義の型は a ->(a,a) である。一般化の過程で、
g には多相型 forall a. a ->(a,a) が付与され、その
後、"..." の部分の型付けがおこなわれる。
多重定義の型付けを行うとき、ひとつの宣言グループからのすべての多重
定義制約がひとつにまとめられ、そのグループ内で宣言される各変数の型に
対する文脈を形成する。たとえば、
f x = let g1 x y = if x>y then show x else g2 y x
g2 p q = g1 q p
in ...
という定義内で、g1 の定義と g2 の定義はともに a
->a ->String で、蓄積された制約は Ord
a (> の使用により発生)、および Show a
(show の使用により発生)である。制約のこの集積の中にあらわれ
る型変数は被制約型変数とよぶ。
この一般化段階では g1 および g2 の両方に型
forall a. (Ord a, Show a) =>a ->a ->String
が付与される。g2 は > および show の 出現が g1 内であっても、g1 と同じ方法で多重定義され ていることに注意せよ。
ひとつの宣言グループ内で 2 つ以上の変数に対して、プログラマが明示的 に型シグネチャを与えた場合、それらの型シグネチャの文脈はその型変数の 名前の付け替えと同一でなければならない。
4.1.4 節で言及したように、
ある型の文脈は型変数あるいは1つ以上の型への型変数の適用しか制約しない。
このことにより、一般化によって生成された型は、すべての文脈制約がその
「頭部正規形」に簡約されるような形式で表現されなければならない。たと
えば、つぎのような定義を考えよう。
f xs y = xs == [y]
この型は
f :: Eq a => [a] -> a -> Bool
で与えられるのであって、
f :: Eq [a] => [a] -> a -> Bool
で与えられのではない。このリスト型で同等性がとれる場合でさえも、 リスト上の Eq に対応するインスタンス宣言を用い、一般化する前 に文脈は単純化される。もし、このようなインスタンスが有効範囲内になけ れば、静的エラーとなる。
C (m t) という形式の制約が必要であることを示す例がある。ここ
で m は一般化される型変数のひとつである。すなわち、クラス C が
型変数あるいは型構成子ではないような型式に対して適用する。
f :: (Monad m, Eq (m a)) => a -> m a -> Bool
f x y = return x == y
を考えよう。return の型は
Monad m => a -> m a
であり、(==) の型は
Eq a => a -> a -> Bool
である。それゆえ、f の型は
(Monad m, Eq (m a)) => a -> m a -> Bool
でなければならず、文脈はこれ以上簡約することはできない。
データ型の deriving 節 (4.3.3 節をみよ)より導出されたイン
スタンス宣言は、どのインスタンスとも同じように、単純なな文脈
を持たねばならない。すなわち、すべての制約は C a という形式で、
a は型変数である。たとえば、
data Apply a b = App (a b) deriving Show
という型において、導出された Show のインスタンスは文脈
Show (a b) を生成し、これは既約であり、単純ではな
い。すなわち、静的エラーとなる。
場合によっては、定義の型のなかで使われている全ての型変数に対して一
般化が可能であるとはかぎらない。たとえば、次の宣言を考えよう。
f x = let g y z = ([x,y], z)
in ...
x の型が a であるような環境では、g の定義の
型は a ->b
->([a],b) である。一
般化の段階で g には型 forall b. a ->b
->([a],b) が付与され、
b のみが全称修飾可能である。なぜなら、a はその型環境で
出現するからである。これを g の型はこの型変数
a において単相であるという。
このような単相性の影響は g のすべて適用の第一引数が単一の
型でなければならないということである。たとえば、上の... の部分が
(g True, g False)
であるのは正しい。(これは偶々、x が 型 Bool を持つ
ことを強要する。) しかし、これが、
(g True, g 'c')
であることは正しくない。一般に、型 forall u. cx
=>t は、もし a が forall u. cx
=>t において束縛されていなければ、型変数 a において
単相的であるという。
Haskell の明示的型シグネチャが単相的型変数を含む型を表現するのに十
分なパワーをもたないということは注目に値する。たとえば、次のように書くこ
とはできない。
f x = let
g :: a -> b -> ([a],b)
g y z = ([x,y], z)
in ...
なぜなら、これは、g が a および b において、
多相的であると主張しているからである (4.4.1 節を見よ)。このプログラム
では、g は、もし、その第一引数が型変数を含まない型に制限され
ている場合にのみ型シグネチャを与えることができる。たとえば、
g :: Int -> b -> ([Int],b)
である。このシグネチャは x が Int 型を持つことも示し
ている。
Haskell では一般化に関して、上で述べた標準的 Hindley-Milner 制限の他に、 特別な場合に、多相性を減ずる、さらにいくつかの制限がある。
単相性制限は変数束縛の構文に依存する。変数は関数束縛によっ ても、パターン束縛によっても束縛されること、また、単純 パターン束縛 (4.4.3 節) は単一の変数からなるパターンのパターン束縛であることを思いだすこと。
以下の二つの規則は単相制限を定義している。
単相性制限
与えられた宣言グループが制限を受けないというのは以下の場合であり、その場 合にかぎる。
そのグループ内のすべての変数それぞれが関数束縛あるいは 単純パターン束縛により束縛されている。且つ
そのグループ内の単純パターン束縛により束縛されているすべての変数 それぞれには明示的な型シグネチャが与えられている。 通常の多相性に関する Hindley-Milner 制限は、その環境で束縛されて いない型変数のみが一般化できるというものである。これに、そのグルー プに対する一般化の段階では被制限宣言グループの被制約型変数は一 般化できない というのが加わる。(型変数が,いくつかの型クラス に属さなければならないというのが、制約を受けるということの意味で あることを思いだすこと。 4.5.2 節を見よ)。
あるモジュール全体に対する型推論が完了した時点で、まだ、単相型変 数であるものはすべて、曖昧であると考え、これは default の規則(4.3.4 節)を使って特 定の型に解決する。 |
規則 1 が必要なのは2つ理由があって,どちらも非常に微妙なものであ る。
規則 1 は予想外の再計算を防ぐ。たとえば、
genericLength は以下のような型が定義されている
(List ライブラリの)標準関数である。
genericLength :: Num a => [b] -> a
ここで、次の式を考える。
let { len = genericLength xs } in (len, len)
len は一度だけ計算されるべきものと見られるが、規則 1 が
なければ、これは 2 度計算される。2 つの多重定義それぞれについて 1
度ずつである。もし、プログラマがこの計算が反復されるのを望むなら、
明示的に型シグネチャを追加すればよい。
let { len :: Num a => a; len = genericLength xs } in (len, len)
規則 1 は曖昧になることを防ぐ。たとえば、次のような宣言グルー
プを考えよう。
[(n,s)] = reads t
reads は標準関数でその型は以下のシグネチャであたえられて
いるものであることを思い出すこと。
reads :: (Read a) => String -> [(a,String)]
規則 1 がなければ、n の型は forall
a. Read a =>a となり、s は forall
a. Read a =>String となる。後
者は不正な型である。なぜなら本質的に曖昧であるからだ。
sを使用ために、どれを多重定義にするかを決定することもでき
なければ、 s に対する型シグネチャを追加してこれを解決する
もできない。このことにより、非単純パターン束縛が使用されるとき
(4.4.3 節)、推論される型はつねに被
制約型変数において単相性をもち、このことは型シグネチャが与えられてい
るかどうかにかかわらない。この場合、n および s は
a において単相性をもつ。
同じ制約がパターン束縛を受ける関数にも適用される。たとえば、
(f,g) = ((+),(-))
において、f および g はそれぞれに型シグネチャが与
えられているかどうかにかかわらず単相性をもつ。
規則 2 が必要な理由は、エクスポートされた束縛を単相的に使
用するよう強制する方法が、現モジュールの外側のモジュール上での型推論
を実行する以外になにもないということである。規則 2 はひとつのモジュー
ル内で束縛されるすべての変数の正確な型は当該モジュールによってのみ決
定されなければならず、これをインポートした側のモジュールでは決定さ
れないと主張するものである。
module M1(len1) where
default( Int, Double )
len1 = genericLength "Hello"
module M2 where
import M1(len1)
len2 = (2*len1) :: Rational
モジュール M1 上の型推論が完了してとき、len1 は単相
型 Num a => a を持つ(規則 1)。規則 2 はこ
こでは単相の型変数 a は曖昧であり、これは 4.3.4 節の default 規則を使うこと
で解決されなければならないと主張するものである。これにより、
len1 は型 Int を獲得し、len2 における
len1 の使用は型不正となる。(もし、上のコードが実際やりたいこ
とであるなら、len1 に型シグネチャを与えれば問題は解決する)。
この問題は入れ子になった束縛では起こらない。コンパイラからは有効範 囲全体が見えているからである。
単相性の規則はプログラマに対してはいくつかの結論をもたらす。関数構
文で定義されるものはどれも通常は関数をのぞみどおり一般化する。したがっ
て、
f x y = x+y
において、関数 f はクラス Num におけるいかなる多重
定義においても使用することができる。ここでは、再計算の危険はない。し
かしながら、パターン構文で定義された同じ関数
f = \x -> \y -> x+y
は完全な多重定義をしようとするなら型シグネチャを必要とする。多くの関
数は殆どの場合、単純パターン束縛をつかって定義される。ユーザは注意
深く型シグネチャを付与し、完全な多重定義を維持しなければならない。
標準プレリュードはこのことの例を多く含んでいる。
sum :: (Num a) => [a] -> a
sum = foldl (+) 0
規則 1 はトップレベルの定義と入れ子になった定義との両方に適用する。
以下のような場合を考えよう。
module M where
len1 = genericLength "Hello"
len2 = (2*len1) :: Rational
ここで、型推論は len1 が単相の型
(Num a => a)をもつことを見いだし、この型変
数 a は len2 上での型推論をおこなったときに、
Rational として解決される。
この節では種の推論を実行するため、すなわち、与えられたプロ グラム中に出現するクラスや型のそれぞれに対して適切な種を計算するため に用いられる規則を説明する。
この種の推論処理の最初の段階はデータ型、シノニム、およびクラスの定
義の集合を依存関係のグループにならべ直すことである。この段階は、4.5 節で説明した変数に対する
依存性解析とほとんど同じ方法で行われる。たとえば、次のプログラムの断
片はデータ型構成子 D、シノニム S およびクラス
C を含み、これらはすべて同じ依存性グループに含まれる。
data C a => D a = Foo (S a)
type S a = [D a]
class C a where
bar :: a -> D a -> Bool
それぞれのグループ内の変数、構成子、および、クラスの種は型推論の標準的
なテクニックおよび種を保存する単一化 [6] を用いて決定される。たとえ
ば、上の定義では、パラメータ a は bar の型の関数構
築子 (->) の引数として出現するによって種は * でなければな
らない。そうであれば、D と S は両方ともに種 * でな
ければならず、クラス C のインスタンスはどれも種 * でなければ
ならない。
推論された種のいくつかの部分は、対応する定義によって完全には決定で
きないということもありうる。このような場合には、デフォルトで * をもつ
ものと仮定する。たとえば、以下の例のそれぞれの a パラメータ
に対して任意の種 k を仮定することができる。
data App f a = A (f a)
data Tree a = Leaf | Fork (Tree a) (Tree a)
これは App および Tree に対して種
(k->*)->
k->* および
k->* を与え、それぞれに任意の種
k がある。また、これは多相の種を許す拡張を必
要とする。そのかわり、デフォルトの束縛 k=* を
使うと、これらの二つの構成子に対する実際の種はそれぞれ、
(*->*)->*->* と i*->* である。
デフォルトは特定の型構成子あるいはクラスが後の依存グループあるいは
プログラムのその他の部分でつかわれている方法とは関係なく、それぞれの
依存グループに対して適用される。たとえば、以下のような定義を上に追加
してもその影響は Tree に対する種の推論には及ばない(たとえば、
種を (*->*)->* に変更する)。そのかわり、静的エラーとなる。なぜ
なら、[] の種 *->* は Tree の引数として期待され
る種 * とマッチしないからである。
type FunnyTree = Tree [] -- invalid
このことは、各構成子とクラスが同じスコープで同じ種をもつという整合 性を保って使用されていることを保証するので、重要である。
The Haskell 98 Report
top | back | next | contents | function index
December 2002