The Haskell 98 Report
top | back | next | contents | function index


4  宣言と束縛

この章では、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 はトップレベルでも、入れ子になったスコープ(たとえば、 letwhere の構成の中)でも使用が許されている。

説明のために、宣言を次の 3 つのグループにわける:

Haskell には (整数や浮動小数のようにな)「処理系に直接組み込まれた」 プリミティブ型があるが、ほとんどの「組込み」データ型はtype宣 言やdata宣言を用いた標準のHaskellコードで定義されている.こ れらの「組み込み」データ型については 6.1節で詳しく説明する.

4.1  型およびクラスの概要

Haskell は、型の静的セマンティクスを与えるのに、伝統的な Hindley-Milner の多相型システム[35]を用いるが、これを多重定義 関数を導入するための構造的方法をもたらす、クラス(ある いは単にクラス)で拡張し、それを用いる。

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 のインスタンスであるのは、その型の上で、適切な型の(多重 定義)クラスメソッドが存在するときである。」

そして、IntFloat がこのクラスのインスタンスで あることを宣言することができる:

  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

ここで addIntnegateIntaddFloat および negateFloat はここでは組み込み関数であるとしておく。しかし、 一般には、どのようなユーザ定義の関数であってもよい。上の最初の宣言は、 「Int はクラス Num のインスタンスのひとつであること は、(クラスメソッド) (+) および negate の定義により 確かたど証明される」と読むことができる。

型と構成子クラスのさらなる例は、Jones の論文 [7]、あるいは、Wadler と Blott の論文[12]に見ること ができる。「型クラス」という用語は、元々は Haskell 1.0 の型システムを 記述するのに用いられていたものであり、「構成子クラス」の用語は元々の 型クラスを拡張を記述するのに用いられていたものである。このレポートで はすでに、これらの用語を分けて用いる理由はなく、「型クラス」は元々の Haskell 型クラスとJones によって導入された構成子クラスの両方を含んで いる。

4.1.1  

型の式の正当性を確かめるために、これらを別のに分類する。 種は次の 2 つの可能性のどちらかの形式である。

値の式の正当性が型推論により検査されるのと同様の方法で、型の式の正 当性は、種の推論により検査される。 しかしながら型推論の場合とちがい、 種の推論は全面的に暗黙のうちに行われ、この言語の目に見える部分ではな い。種の推論は 4.6 節で議論す る。

4.1.2  型の構文

-->
type -> btype [-> type] (関数の型)
btype -> [btype] atype (型の適用)
atype -> gtycon
| tyvar
| ( type1 , ... , typek ) (タプルの型、k>=2)
| [ type ] (リストの型)
| ( type ) (括弧でくくられた構成子)
gtycon -> qtycon
| () (ユニットの型)
| [] (リストの構成子)
| (->) (関数の構成子)
| (,{,}) (tupling constructor) (タプルの構成子)

Haskell の型の式の構文は上記で与えられる。データの値がデータ構成子 により構築されるのとちょうど同じように、型の値は、型構成子により構築 される。データ構成子と同様に、型構成子の名前は大文字ではじまる。デー タ構成子とはちがい、中置型構成子は認められていない(ただし、 (->) は例外)。

型の式の基本形式は以下のとおりである。

  1. 型変数は小文字ではじまる識別子として書く。型変数の種はそれが出 現した文脈により暗黙裡に決定される。

  2. 型構成子。殆どの型構成子は大文字ではじまる認識子として書く。 たとえば、

    型構成子のなかには特別な構文をもつものもある。 定数 (->) および [] の使い方について以下で詳し くのべる。

  3. 型適用。もし、t1 が種 k1 -> k2 の型で t2 が種 k1 の型ならば、 t1 t2 は 種 k2 の型式である。

  4. (t) という形式の括弧でくくられた 型は、型 t と同一である。

例えば、型式 IO a は定数 IO の変数 a への適用と理解することができる。IO 型構成子は種 * -> * であるから、変数 a および、式全体 IO a の種は * になるはずである。一般に種の推論(4.6 節を見よ)の過程はユーザ定義デー タ型、型シノニムおよびクラスの種を正しく決定するのに必要である。

いくつかの型式を旧来の方法で表記するための特別な構文が用意されてい る。

  1. 関数の型t1 -> t2 という形式で、これは、型 (->) t1 t2 と同等である。関数の矢印は右結合である。

  2. タプル型(t1,..., tk) (このとき k>=2) という形式で、これは、型 (,...,) t1 ... tk (ここでは括弧のなかには k-1 個のコンマがあ る)と同等である。これは、k-タプルの型を表しており、最初の要素の 型は t1、二番目は t2、 などとなっている(3.8 節および 6.1.4 節を見よ)。

  3. リスト型[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 -> aforall a . (a -> a) を意味する。

4.1.3  クラス表明および文脈の構文

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 と書く。文脈 cxt で参照 される型変数を 1 つだけ含むものでなければならない。利便のために、文脈 cx が空の場合にもcx => t と書くが、 この場合には具象構文では=> は含まれない。

4.1.4  型およびクラスのセマンティクス

この節では、型システムの非形式的な詳細を示す。(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 である。 doubleInt 型の値に(aInt) に インスタンス化することにより)適用することができる。それは、 Int はクラス Num のインスタンス故に Num Int が保存されるからである。 しかしながら、double は型 Char の値に通常は適用する ことはできない。それは Char は通常ではクラス Num の インスタンスではないからである。ユーザはインスタンス宣言して、 doubleChar に適用できるようにするということもで きる。

4.2  ユーザ定義のデータ型

この節では、代数的データ型(data 宣言)、名前付替データ型 (newtype 宣言および型シノニム(type 宣言)について解 説する。これらの宣言はモジュールのトップレベルにしか現れない。

4.2.1  代数的データ型宣言

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 aa : (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 中あるいは右辺にあらわれた場合は、静的エラーとなる。新たな型定数 Tk1->...->kk->* という形式の種である。ここで、 引数変数 ui の種 ki は種の推論により4.6 節で説明するように決定される。 すなわち、T0k との間の引数のどこの型式の なかで使ってもよい。

たとえば、宣言

  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 は、もし、 siti の形式 であれば、遅延適用関数 $ であり、もし、! ti の形式であれば、正格適用関数 $! (6.2 節を見よ)である。 K へのパターン照合は、正格フラグには影響しない。

4.2.2  型シノニム宣言

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 の種 k4.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] は許されない。

型シノニムは型シグネチャの可読性を向上させるための構文上の機構にす ぎない。型シノニムとその定義は完全に置き換え可能である。

4.2.3  データ型名の付け替え

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 ( _|_) )( d1 ( D1 _|_) ) および ( s _|_) はすべて 42 である。特に、( N _|_)_|_ である一方で、( D1 _|_)_|_ と同等ではない。

newtype 宣言の必須ではない導出部は、data 宣言にお ける導出構成要素と同じに扱う。4.3.3 節を見よ。

newtype 宣言ではフィールド名の構文を使うことができる。もち ろん一フィールドだけのものである。すなわち、

  newtype Age = Age { unAge :: Int }

は、構成子と脱構成子の有効範囲に

  Age   :: Int -> Age
  unAge :: Age -> Int

を導入する。

4.3  型クラスと多重定義

4.3.1  クラス宣言

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 の有効範 囲は、クラス本体のクラスメソッドのシグネチャのみである。文脈 cxC のスーパークラスを以下に解説するように指定する。 cx 中で参照できる型変数は u のみである。

スーパークラスの関連は循環してはならない。すなわち、非循環有向グラ フを形成しなければならない。

class 宣言の cdecls 部分は、3 種類の宣言を含む。

これら以外の宣言は、cdecls では許されない。

where 部分をもたない class 宣言はコレクションクラ スを元のクラスのクラスメソッドをすべて含むさらに大きなコレクションク ラスに結合するのに役立つ。たとえば、

  class  (Read a, Show a) => Textual a

このような場合、もしある型がすべてのスーパークラスのインスタンスであ る場合、たとえ、サブクラスが直接クラスメソッドをもたないにせよ。自動 的にそのサブクラスのインスタンスであるというわけではない。 instance 宣言は where 部分なしで、明示的に与えねば ならない。

4.3.2  インスタンス宣言

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 に適用し たものである。これらの型変数は別々のものでなければならない。

このことは以下のようなインスタンス宣言を禁じるものである。



  instance C (a,a) where ...
  instance C (Int,a) where ...
  instance C [[a]] where ...

宣言 d はクラス C のクラスメソッドに対する束縛のみを含 むことができる。スコープ内にないクラスメソッドに対する束縛を与えるこ とは正しくない。しかし、スコープ内でその名前は immaterial ????? であ る。とくに、被修飾名の場合はそうである。(この規則はエクスポートリスト (5.2 節)内の従属名に対する ものと同等である)。例えば、次の場合、range は 被修飾名 Ix.range のときのみ有効範囲であるにもかかわらず、正しい。

  module A where
    import qualified Ix

    instance Ix.Ix T where
      range = ...

宣言は型シグネチャあるいは結合性宣言を含むことはできない。それは、型 シグネチャや結合性宣言は既に class 宣言で与えられているから である。デフォルトクラスメソッド (4.3.1 節)の場合と同様にメソッド宣言 は変数または関数定義の形式をとらなければならない。

もし、いくつかのクラスメソッドに対して束縛が与えられなければ、 class 宣言中の対応するデフォルトクラスメソッドが使用される。 もし、このとき、そのようなデフォルトクラスメソッドが存在しない場合に はこのインスタンスのこのクラスメソッドは undefined に束縛さ れ、コンパイル時エラーとはならない。

T をクラス C のインスタンスとする instance 宣言は C-T インスタンス宣言と呼ばれ、以下 の制限を受ける。

次はスーパークラスのインスタンスによる制限を説明するものである。

  class Foo a => Bar a where ...
  
  instance (Eq a, Show a) => Foo [a] where ...
  
  instance Num a => Bar [a] where ...

この例は正しい Haskell である。FooBar のスーパー クラスであるから、ふたつめのインスタンス宣言は Num a と いう仮定のもとで、[a]Foo のインスタンスである場 合にのみ正しい。最初のインスタンス宣言はまさに、[a] はこの仮 定のもとで Foo のインスタンスであるということを言っているの である。それは、Eq および ShowNum のスー パークラスだからである。

もし、二つのインスタンスをつぎのように

  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.3.3  導出インスタンス

4.2.1 節で言及したように、 data 宣言および newtype 宣言は、必須ではない deriving 形式をもつ。もしこの形式が含まれている場合には、 導出インスタンス宣言は自動的にその名前付のクラスのそれぞれの データ型に対して生成される。これらのインスタンスはユーザていぎ のイン スタンスと同じ制限に支配される。もし型 T について、クラス Cを導出したら、明示的な instance 宣言を通じてか、ある いは、deriving 節中のスーパークラスをインクルードすることで、 C のすべてのスーパークラスに対するインスタンスが、T に 対して存在しなければならない。

導出インスタンスはユーザ定義のデータ型に対して便利に共通に使える演 算を提供するものである。たとえば、Eq クラス中のデータ型に対 する導出インスタンスは演算 == および /= を定義し、 プログラマがそれを定義する手間を省く。

プレリュード中のクラスでそれに対する導出インスタンスが許されているのは EqOrdEnumBoundedShow、および Read だけである。これらすべては、 図 5 で定義されている。これ らのクラスそれぞれについてどのように導出インスタンスが生成されるかの 詳細は 10 章にある。そこ では、どのような場合にそのような導出インスタンスが可能かとういう仕様 も含まれている。標準ライブラリにより定義されているクラスも導出可能で ある。

もし、deriving 形式中で、名前付きのクラス上の instance 宣言を導出することができなければ、静的エラーとなる。 たとえば、すべてのデータ型が Enum のクラスメソッドをただしく サポートできるわけではない。導出されたされたクラスにたいして明示的 に instance 宣言を与えようとすると、この場合も静的エラーとな る。

deriving 形式が data 宣言や newtype 宣言 で省略された場合、そのデータ型に対しては、いかなるインスタンス宣言も 導出されない。すなわち、deriving 形式を省略するとい うことは、空の導出形式、deriving () と同等である。

4.3.4  曖昧な型と多重定義された数値演算のデフォ ルト定義

topdecl -> default (type1 , ... , typen) (n>=0)

Haskell-スタイルの多重定義に付随する問題は曖昧な型が出来る可能 性があることである。たとえば、10 章で定義されている関数 readshow を利用して、Int および BoolRead および Show のメンバーだとし た場合、式

  let x = read "..." in show x -- invalid

は曖昧である。show および read に対応する型

show :: forall a. Show a =>a ->String
read :: forall a. Read a =>String ->a

aInt にインスタンス化しても、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 であり、各 tiNum ti が保存されなければなら ない。曖昧な型が発見されるような状況では、曖昧な型変数 v は 以下の 3 つを満す場合にデフォルト宣言可能である。

default 宣言可能なそれぞれの変数は、すべての曖昧な変数のクラスの一 つのインスタンスであるデフォルトリスト中の最初の型で置き換えられる。 このような型が見つからない場合には静的エラーとなる。

ひとつのモジュールではデフォルト宣言はひとつだけ許されており、その効 果はそのモジュール内に限られる。もし、あるモジュールでデフォルト宣言が ないばあいには以下が仮定される。

  default (Integer, Double)

空のデフォルト宣言、default () はそのモジュールのすべて のデフォルトをオフにする。

4.4  入れ子になった宣言

以下の宣言はモジュールのトップレベルを含むどの宣言リスト中でも使用 できる。

4.4.1  型シグネチャ

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 を与 えることを可能にしている。

4.4.2  結合性宣言

gendecl -> fixity [integer] ops
fixity -> infixl | infixr | infix
ops -> op1 , ... , opn (n>=1)
op -> varop | conop

結合性宣言は1つ以上の演算子の結合性と束縛の優先順位を与えるものであ る。結合性宣言は型シグネチャが現われるところならどこに現われてもよく、 型シグネチャと同様、特定の演算子の性質を宣言するものである。また、こ れも型シグネチャと同様に、結合性宣言は、その演算子の宣言と同じ宣言グ ループのなかで 1 度だけ出現することができ、どの演算子に対しても高々 1 度の結合性宣言を与えることができる。(クラスメソッドの結合性宣言はめだ たぬ例外で、これはクラス宣言中あるいはトップレベルのどちらかで出現す ることができる。)

結合性には、無結合、左結合、右結合(それぞれ、infixinfixlinfixr) の 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`

Table 2

プレリュード演算子の優先順位と結合性

結合性は特定のエンティティ(構成子あるいは変数)の属性であり、型とお なじようなものである。結合性はエンティティの名前の属性ではな い。例えば、

  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` の定義に入れ子の結合性定義を与えることもできる。)

4.4.3  関数束縛およびパターン束縛

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 構成内であらわれる。

4.4.3.1  関数束縛

関数束縛は変数を関数値に束縛する。変数 x に対する関数束縛の 一般的形式は、

x p11 ... p1k match1
...
x pn1 ... pnk matchn

ここで、各 pij はパターンであり、各 matchi は以下の形式、

= ei where { declsi }

あるいは

| gi1 = ei1
...
| gimi = eimi
where { declsi }

であり、ここで、n>=11<=i<=nmi>=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

(p11, ..., p1k) match1
...
(pn1, ..., pnk) matchn

ここで、xi は新しい識別子

4.4.3.2  パターン束縛

パターン束縛は変数を値に束縛する。単純パターン束縛は p = e という形式である。パターン p は前に ~ が暗黙 上ついているかのように反駁不可パターンと同様に遅延照合される。3.12 節の変換を見よ。

パターン束縛の一般形は p match であり、ここでは match は上の関数束縛に対するのと同じ構造をしている。いいかえれ ばパターン束縛は

p | g1 = e1
| g2 = e2
...
| gm = em
where { decls }

変換:

上のパターン束縛は以下の単純パターン束縛とセマンティクスとしては同 等である。

p = let decls in
if g1 then e1 else
if g2 then e2 else
...
if gm then em else error "Unmatched pattern"

構文に関するノート

束縛がパターン束縛であるか関数束縛であるかを見分けるのは簡単である が、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 パターンになりえないからである。

4.5  関数束縛およびパターン束縛の 静的セマンティクス

let あるいは where 節の関数束縛およびパターン束縛 の静的セマンティクスについてこの節で議論する。

4.5.1  依存性解析

一般的にセマンティクスは通常の Hindley-Milner 推論規則によって与え られる。依存性解析変換はまず多相性を強化するために行なわれる。 値の宣言によって束縛されるふたつの変数は次のどれかの場合、同じ宣 言グループに属する。

  1. どちらも同じパターン束縛により束縛される。
  2. それらの束縛は相互再帰的 (おそらくはそのグループの一部である別の宣 言を通じて)

以下の規則の適用により、各 let あるいは where 構 成(モジュールのトップレベルの束縛を定義する where 節を含む) は、ひとつの宣言グループの変数のみを束縛し、必要とされる依存性解析を 捕捉する。(同様の変換は、Payton Jones の本 [9] で解説されている。)

  1. where/let 構成における宣言の順序は、無関係。
  2. let {d1d2} in e = let {d1} in (let {d2} in e)
    (d2 内の認識子が d1 内で無束縛ではあらわれない場合)

4.5.2  一般化

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.5.3  文脈簡約エラー

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) を生成し、これは既約であり、単純ではな い。すなわち、静的エラーとなる。

4.5.4  単相性

場合によっては、定義の型のなかで使われている全ての型変数に対して一 般化が可能であるとはかぎらない。たとえば、次の宣言を考えよう。

  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 は、もし aforall u. cx =>t において束縛されていなければ、型変数 a において 単相的であるという。

Haskell の明示的型シグネチャが単相的型変数を含む型を表現するのに十 分なパワーをもたないということは注目に値する。たとえば、次のように書くこ とはできない。

  f x = let 
          g :: a -> b -> ([a],b)
          g y z = ([x,y], z)
        in ...

なぜなら、これは、ga および b において、 多相的であると主張しているからである (4.4.1 節を見よ)。このプログラム では、g は、もし、その第一引数が型変数を含まない型に制限され ている場合にのみ型シグネチャを与えることができる。たとえば、

  g :: Int -> b -> ([Int],b)

である。このシグネチャは xInt 型を持つことも示し ている。

4.5.5  単相性制限

Haskell では一般化に関して、上で述べた標準的 Hindley-Milner 制限の他に、 特別な場合に、多相性を減ずる、さらにいくつかの制限がある。

単相性制限は変数束縛の構文に依存する。変数は関数束縛によっ ても、パターン束縛によっても束縛されること、また、単純 パターン束縛 (4.4.3 節) は単一の変数からなるパターンのパターン束縛であることを思いだすこと。

以下の二つの規則は単相制限を定義している。

単相性制限

規則 1.

与えられた宣言グループが制限を受けないというのは以下の場合であり、その場 合にかぎる。

(a):

そのグループ内のすべての変数それぞれが関数束縛あるいは 単純パターン束縛により束縛されている。且つ

(b):

そのグループ内の単純パターン束縛により束縛されているすべての変数 それぞれには明示的な型シグネチャが与えられている。

通常の多相性に関する Hindley-Milner 制限は、その環境で束縛されて いない型変数のみが一般化できるというものである。これに、そのグルー プに対する一般化の段階では被制限宣言グループの被制約型変数は一 般化できない というのが加わる。(型変数が,いくつかの型クラス に属さなければならないというのが、制約を受けるということの意味で あることを思いだすこと。 4.5.2 節を見よ)。

規則 2.

あるモジュール全体に対する型推論が完了した時点で、まだ、単相型変 数であるものはすべて、曖昧であると考え、これは default の規則(4.3.4 節)を使って特 定の型に解決する。

動機

規則 1 が必要なのは2つ理由があって,どちらも非常に微妙なものであ る。

規則 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)をもつことを見いだし、この型変 数 alen2 上での型推論をおこなったときに、 Rational として解決される。

4.6  種の推論

この節では種の推論を実行するため、すなわち、与えられたプロ グラム中に出現するクラスや型のそれぞれに対して適切な種を計算するため に用いられる規則を説明する。

この種の推論処理の最初の段階はデータ型、シノニム、およびクラスの定 義の集合を依存関係のグループにならべ直すことである。この段階は、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] を用いて決定される。たとえ ば、上の定義では、パラメータ abar の型の関数構 築子 (->) の引数として出現するによって種は * でなければな らない。そうであれば、DS は両方ともに種 * でな ければならず、クラス 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