やさしい Haskell 入門 (バージョン 98)
back next top


12  型付けの落し穴

この短いセクションでは Haskell の型システムを使うさい、初心者に共通の 2、 3 の問題について直観的な説明をします。

12.1  let-束縛の多相性

Hidler-Milner の型システムをつかうどの言語にもlet-束縛の多相性 ( let-bound polymorphism ) と呼ばれるものがあります。これは、 let あるいは where 節をもちいた束縛のない識別子はその 多相性に関して制限があるからです。特に、ラムダ束縛された ( lambda-bound ) 関数 (すなわち、別の関数の引数になった関数) はふ たつの方法ではインスタンス化できません。たとえば、以下のプログラムは不正 なプログラムです。

let f g  =  (g [], g 'a')                       -- ill-typed expression
in f (\x->x)

なぜなら、主型が a->a であるようなラムダ抽象に束縛された関数 gf 内でふたつの別の方法でつかわれているからです。 一度は [a]->[a] という型で、そして、もう一度は Char->Char という型で使われています。

12.2  数値の多重定義

数が多重定義されること、数の型変換が他の言語とおなじように暗黙のうちに起 こることはないことをよく忘れてしまします。より一般的な数値式が本当に総称 的なものにはなれないこともあります。よくある数値の型付けエラーは次のよう なものです。

average xs              =  sum xs / length xs           -- Wrong!

(/) は分数の引数を必要とします。しかし、length の結果 は Int です。この型の不一致は明示的な型変換によって訂正しなけれ ばなりません。

average                 :: (Fractional a) => [a] -> a
average xs              =  sum xs / fromIntegral (length xs)

12.3  単相性限定

Haskell の型システムはふつうの Hidley-Milner の型システムにはない型クラ スに関連した制限があります。これを、単相性限定 ( monomorphism restriction ) といいます。この制限がある理由は subtle 型の曖昧性に関連しています。これについての詳細は、レポート( §4.5.5 ) を参照してください。

この単相性限定の謂は(単一の識別子への束縛を含む)パターン束縛により束縛を うける識別子で明示的な型シグネチャをもたないものは、単相的 ( monomorphic ) でなければならない、というものです。 識別子は、多重定義されていないか、あるいは、多重定義されていても少くとも ひとつの限定的な多重定義のなかで用いられ、エクスポートされていないときに は、単相的識別子です。

この制限に違反すると、静的型エラーがおこります。この問題を回避するもっと も単純な方法は明示的な型シグネチャをつけることです。あらゆる型 シグネチャ(これが正しいものであれば)この問題を回避できます。

よくあるこの制限違反は高階関数的な手法で関数を定義する際におこります。標 準プレリュードにある sum の定義から例をあげましょう。

sum                     =  foldl (+) 0

ここにあるようにすると、静的型エラーがおこります。この問題は明示的な型シ グネチャを付加することで解決します。

sum                     :: (Num a) => [a] -> a

また、この問題は以下のように書けば起こりませんでした。

sum xs                  =  foldl (+) 0 xs

なぜなら、この制限はパターン束縛にのみ適用されるからです。


A Gentle Introduction to Haskell, Version 98
back next top