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 | |