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

Haskell 記事リストのまとめ

State モナドの記事が終わって一段落ついた気がするので、当ブログのHaskell関連の記事はお休みします。

Haskell 記事応用編を書き始めたきっかけは、「なぜ、Haskell では普通のプログラムが作れないのか」という疑問だった。ghci で純粋関数の世界のプログラムがある程度作れるようになったが、コマンドプロンプトや、ターミナルエミュレータから直接使えるプログラムが書けない。出来ればGUIのプログラムも作ってみたくなったのだ。

最初は、標準ライブラリをよく知らないからだと考え、ListモジュールとIOモジュールを調べてみた。そうして、IOモジュールを調べるうちに、もう一度IOモナドについてきちんと考えてみる必要があるのに気づいた。

そこで、圏論の解説書に挑戦してみたがさっぱり分からない。しかし、抽象的な理論は具体的なモデルがあると、比較的分かりやすいので、圏論の概念を Haskell でプログラミングしてみてはどうかと考えた。これは部分的には成功し、部分的には失敗だった。圏論の概念を正確に理解するところまで行かなかったので失敗だったが、Hakell のプログラムが圏であること。IOモナドの世界も圏であることに気がついた。

Haskell の世界が圏であること、IOモナドの世界が圏であること、圏とは、データと関数からなる世界で、これらのデータや関数は圏の世界の中で完結していること、つまり、ひとつの圏の中のデータと関数は、その圏の世界から外に出ることはないことが分かった。

圏のデータと関数が圏の世界を出ることがないというのは抽象的なので、もう少し具体的に考えてみよう。Haskell のデータ 2 を Haskell 圏の関数 f x = 2 * x の引数として与えると、戻値が Haskell 圏のデータ 4 になる。つまり、引数、関数、戻値とも Haskell 圏の要素だ。さらに、この4を g x = x + 1 の引数として与えると、g の戻値は 5 でこれもやはり Haskell 圏のデータだ。したがって、2 -> f x -> 4 -> g x -> 5 というデータへの関数適用の連鎖でも、これらのデータや関数が Haskell 圏を出ることがない。

関数型のプログラミング言語である Haskell のプログラムは、結局のところ関数の合成と関数のデータに対する関数適用の連鎖でしかない。Haskell の関数やデータがHaskell 圏にとどまっているということは、Haskell のプログラムは全て、Haskell 圏で完結しているということだ。

同様の状況は、IO モナドについても言える。return "hello" で IO a 型のデータが作成されるが、これを >>= 演算子で putStrLn 関数に渡してやると、"hello" が表示された後、putStrLn 関数から戻値 IO () が返される。この場合も return "hello" -> IO "hello" -> putStrLn -> IO () という、IOモナド型のデータとIOモナド型の関数の連鎖は、IOモナドの圏の範囲から出ることはなく完結している。従って、このような操作をどんなに繰り返しても、IOモナド型のデータしか作られることはないので、IOモナドの圏の中で作成されたデータが Haskell 圏に流れ出すことはない。そのため、IO関係の副作用に関連するプログラムは、IOモナドの圏の中に閉じ込められてしまうことになる。

モナドのモジュール性だとか、IOモナドによる副作用の隔離だとかいっても、たったこれだけのことだ。つまり、データに関数を適用しても戻値のデータはやはりその圏のデータであるということ。そのため、関数をどんなに合成してもその戻値がその圏のデータであることに変わりがないということだ。

圏の世界を構成する要素が、データと関数という二種類しかなく、それらが圏の世界で完結しているというイメージを持つことができたので、ひとつの圏の世界を撮影し、別の圏の世界に映しだすビデオカメラのような役割を関手がもっているというイメージを作ることができた。

そうして、一番重要だったのは、IOモナドがこのビデオカメラ(関手)を指しているということが分かったことだ。IOモナドの解説を読みながら、IOモナドという言葉が、IO a型のデータを指しているのか、IOモナド型の関数を指しているのか判然としなかったので、もう一つ理解した気がしなかったが、IOモナドは実はデータや関数ではなく、関手を指す言葉だったのだ。

一担、IOモナドのイメージができると、IOモナドの関数をプログラムするコツが見えてきた。それは、「1引数、戻値IO型」の関数と retrun, >>=, >>, ラムダ記法という5つの要素を操作するだけの単純な手順だった。また、IOモナドの再帰関数の作り方に悩んだが、プログラム例を探しまわっているうちに、再帰的定義の部分で、do 記法を使って IO a 型の戻値のコンテナから生の a を取り出して加工し、それを return で再び IO a 型に包むと良いことが分かった。

IOモナドの使い方が分かると、今度は、Maybe や List や State などの他のモナドが気になった。しかし、それも簡単だった。これらは、return と >>= と >> を共有していたのだ。IOモナドでのこれらの記号の使い方は、ちょっと工夫するだけで、Maybe や List や State に適用することができた。このことで、モナドがプログラムのモジュール化に重要な働きをしていることが分かった。副作用の隔離はこのモジュール化の応用でしかない。

IOモナドは Haskell の鬼門であるだけでなく、プログラミングのモジュール化が簡単であるという、Haskell の重要な特徴を担うパラダイムだったのだ。

Haskell はいろいろなアイディアが詰まっており、これを全て知るのは大変だ。攻殻機動隊ではないが、「Haskell の世界は広大だ」。このまま、Haskell の記事を続けても、広大な世界観の中で迷子になってしまうだけだろう。これからは、もう一度自分が探索した記事を読み返して、整理してみたい。もちろん、これで Real World Haskell に挑戦できる装備が整ったように思えるので、そちらの方も楽しみだ。しかし、Haskell についての自分の情報の発信はしばらくお休みにしたい。

たまたま Ubuntu に Hugs をインストールしたというきっかけでやり始めた Haskell 探検だったが、楽しかった。Haskell は面白い。
by tnomura9 | 2011-11-08 07:49 | Haskell | Comments(0)
<< モナドの世界をコントロールする... IO モナド、Maybe モナ... >>