人気ブログランキング | 話題のタグを見る

RWH の読み方(9) 第2章

Real World Haskell の第2章 Types and Functions のまとめの続き。

Polymorphism in Haskell

原著に忠実なまとめが難しかったので、以下は、管理人の視点からの要約になる。

この節では多形性 polymorphism を扱っている。しかし、多形性とは何かという定義を厳密に記述するより、リストの関数 last という具体的な例を上げて直観的に説明しようとしているように見える。

last をリスト [1,2,3,4,5] に適用するとリストの最後尾の要素 5 が返される。また、last を文字列 "baz"に適用すると戻値は最後尾の要素 z だ。[1,2,3,4,5] の型は [Int] であり、"baz" の型は [Char] だから、last は型の異なる値に対してそれぞれに相応しい結果を返すことができる。

型の異なる値に適用できるので last は polymorphic function だ。

polymorphic function の type signature は type variable を含むのですぐ分かる。(ことさら英語の用語を使うつもりはないのだが、これらの用語を日本語でどう訳すのかわからないのだ。日本で Haskell を本格的に広めるためには、日本語の Haskell 用語委員会のようなものを作る必要があるのではないだろうか。)

Prelude> :type last
last :: [a] -> a

上の type signature の a が type variable (型変数) だ。これを見ると、last は型 a の要素のリストを引数に取り、型 a の値を返すことが分かる。また、引き数のリストの要素の型と結果の値の型は同じ型だ。

また、型 a の部分には Int や Char などいろいろな型を当てはめることができる。このようなタイプの関数の多形性を parametric polymorphism と呼ぶ。

このような関数の type signature からは、実際の値の型がどういうものかを推測することはできない。last は、Intのリストを操作するとか文字列を操作するという具体的なものを離れた、リスト一般という抽象的なものを操作する関数だからだ。

type variable によって表される、[a] はリスト一般という抽象的なリストを表している。この型は parameterized type または polymorphic type と呼ばれる。[a] はリストという枠組みそのものを指し示すことができるのだ。last はこの一般的なリストに作用する関数だ。したがって、last は具体的な値を作り出すこともできないし、具体的な値をチェックすることもできない。しかし、この抽象性の故に、[a] の a にIntやChar を当てはめた具体的なリストのどれに対しても同じ last という関数を適用させることができる。

関数の多形性は、その関数が抽象的なデータを加工していることを意味しているのだ。

Haskell の多形性は他のプログラム言語の多形性とはことなる特質があり、それについての考察がなされているが省略する。

原著の内容を勝手に編集してどうするのだという意見もあるかもしれないが、管理人の視点から見た原著の内容を記述してみた。原著に忠実でない場合が、かえって原著のアイディアに焦点を当てることが出来る場合もあるのではないだろうか。単に、原著の骨組みを抜き出すよりはノートをとる意味があるような気がする。

Reasoning about Polymorphic Functions

上のサブタイトルは、Polymorphism in Haskell 節の中の小見出しだ。ここでは fst を例にとって、fst の type signature から fst の動作がどのようなものであるかを推測して見せている。Reason about は推測するという意味。:type コマンドで fst の型を表示させると次のようになる。

Prelude> :type fst
fst :: (a, b) -> a

上の fst の type signature からいろいろなことが推測できる。まず fst の引き数は pair でその要素の型は a と b とあるように異なっていてもいい。また、fst の戻値の型は、pair の第1の要素と同じ型であって、第2の要素の型とは必ずしも一致しない。

ここで、気をつけておかないといけないのは、(a, b) -> a は引き数の第1要素の型と戻値の型が同じということを示しているだけで、第1引き数の値と戻値の値が同じであるということを意味しているわけではないということだ。そこを混同してしまうと誤解が生じてしまう。

いずれにせよ、関数の型から関数の動作を推測することができるということが大切だ。Haskell のプログラムを書くときの、関数の型宣言は煩雑に感じるが、この型宣言は関数を解読する場合の重要なドキュメントになる。

Further Reading

関数の型による推測についての文献が紹介してあるが、管理人には読みこなせそうにないので省略。

Polymorphism in Haskell 節を読んで思ったのが、Haskell の多形性はデータを抽象化する Haskell の能力と深い関係があるということだ。

Haskell の type variable はその抽象化の働きをサポートする使い勝手のいい道具なのかもしれない。Haskell でプログラムを組むことによって、自然に抽象的な問題についてのプログラムをその抽象性を損なわないまま記述してしまうことができるのではないかという気がする。

head や tail や last などのリスト操作の関数は直観的にたやすく扱うことができるが、これらの関数は実際は一般的なリストという抽象的なデータを加工しているのだ。

そのため、文字列の構造がどうかとかいちいち実装のことを考えなくても、何となく「リストの先頭の部分」とか「リストの残りの部分」などという曖昧な発想でプログラムを書くことができる。あまりに簡単にできるので気にも止まらないが、他の言語と比較した時にはとんでもないことをやっているのかもしれない。

Haskell は抽象的な思考をそのまま具体的なコードとして記述するための道具を備えているようにみえる。

だんだん Real World Haskell の記述の癖のようなものが分かってきた。この本は Haskell の具体的な使い方と言うよりは、Hakell を使う時の考え方を伝えようとしているのだろう。

この記事で一気に2章の最後まで行こうと思っていたがどうやら無理なようだ。
by tnomura9 | 2012-02-21 20:02 | Haskell | Comments(0)
<< RWH の読み方(10) 第2章 メランコリック >>