IO モナドとの付き合い方(1)

Haskell でとにかく評判が悪いのが IO モナドだ。理論的なことのわかりにくさももちろんだが、do 記法の中に普通の手続き型のプログラムを書けばいいと考えてプログラムを書くと、わけのわからないエラーが出て、その原因が全く分からないという事態が起きてしまう。

例えば、次のプログラムの do 記法の中身は手続き型のプログラムのものとよく似ている。

ファイル名: example1.hs
main = do
  cs <- getLine
  putStrLn cs

これは次のようにきちんと動く。
Prelude> :e example1.hs
Prelude> :l example1.hs
[1 of 1] Compiling Main ( example1.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
hello, world
hello, world

ところが、手続き型のプログラムとのアナロジーで次のようなプログラムを作ると、エラーが出てしまうが、何でエラーになるのか見当もつかない。

ファイル名: example2.hs
main = do
  name <- getLine
  greeting <- "hello, " ++ name
  putStrLn greeting

example2.hs の実行例は次のようになるが、コンパイルできずエラーになる。
*Main> :e example2.hs
*Main> :l example2.hs
[1 of 1] Compiling Main ( example2.hs, interpreted )

example2.hs:3:2:
    Couldn't match expected type `[Char]'
        against inferred type `IO Char'
    In a stmt of a 'do' expression: greeting <- "hello, " ++ name
    In the expression:
        do { name <- getLine;
            greeting <- "hello, " ++ name;
            putStrLn greeting }
    In the definition of `main':
        main = do { name <- getLine;
            greeting <- "hello, " ++ name;
        putStrLn greeting }
Failed, modules loaded: none.

次のようなプログラムにするのが正解で、これはコンパイルできて実行できる。

ファイル名: example3.hs
main = do
  name <- getLine
  greeting <- return ("hello, " ++ name)
  putStrLn greeting

実行例
Prelude> :e example3.hs
Prelude> :l example3.hs
[1 of 1] Compiling Main ( example3.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
world
hello, world

要は "hello, " ++ name の値を return 関数の引数にするだけだが、この例では何で、return 関数の引数にすると良いのか合点がいかない。

上の例で見てきたように、IO モナドをプログラムを手続き型のプログラムからの類推で組もうとすると、どうしても意味不明のエラーに突き当たって、IO モナドは難しい、Haskell は使えないという結論になりがちだ。

しかし、これは、IO モナドのプログラムを、手続き型のプログラムの類推で組もうとするから起こる困難で、一旦手続き型のプログラムのことは忘れて、本来のIO モナドのプログラム規則に従ってプログラムを作成してみると、IO モナドのプログラムの規則が非常に単純で分かりやすいことが実感できる。

実はこのプログの System.IO モジュールのテストに用いたプログラムが、この「本来のIOモナドのプログラム様式に素直に従ったプログラム」だ。記事の通りに ghci のコマンドライン上で入力して実行してみれば、IO モナドのプログラミングの規則が非常に単純で、すっきりしているのが分かるだろう。あんなに難解だったIOモナドのプログラムを、ほとんどエラーもなく組めるようになっているのに気づくと思う。

次からの記事は、IOモナドプログラムのこの単純なルールに焦点をあて、IOモナドのプログラミングのルールが、実は、非常に単純ですっきりしたものだということを示そうと思う。


IO モナドとの付き合い方(2)へ
[PR]
by tnomura9 | 2011-09-28 18:39 | Haskell | Comments(0)
<< IOモナドとの付き合いかた(2) System.IO モジュール... >>