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

System.IO モジュール(2) IO モナド型の関数とは何か

Haskell を習得しようとする時、最大の難関が IO モナドだ。この記事ではIOモナドとは何かということについての詳しい記述はしない(というか管理人にはできない)。IOモナドについて初心者の立場から管理人が挑戦した記事は、このプログの「Haskell 記事リスト」の 9. IOモナドのところに一連の記事を置いてある。

この記事では、ある程度のIOモナドの操作はできると想定して、IOモナドのプログラミングの特徴を検討してみたい。一口で言うと、IOモナドのプログラムとは、IOモナド型の関数を >>= 演算子または >> 演算子で結合させたものなのだ。

それでは、IOモナド型の関数とは何だろうか。

それは、単に、「引数が1個で、戻値がIO a 型の関数」である。この条件を満たせば、どのような関数もIOモナド型の関数になってしまうのだ。では、IOモナド型の関数は、IOモナドのプログラミングにどのような形で現れてくるのだろうか。

そもそも、Haskell では全ての入出力操作は IO モナドでプログラムしないといけない。IO モナドは、main 関数の do 記法の中で記述すれば、一見手続き型のプログラム言語風に入出力のプログラムを記述することができる。次のプログラムは、文字列を入力して表示するためのプログラムだ。

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

これは次のようにきちんと実行できる。

*Main> :e sample1.hs
*Main> :l sample1.hs
[1 of 1] Compiling Main ( sample1.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
Dolly
Dolly

しかし、上のプログラムの類推で次のようなプログラムを書いても、コンパイルできない。

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

sample2.hs は次のようにコンパイルできない。
*Main> :e sample2.hs
*Main> :l sample2.hs
[1 of 1] Compiling Main ( sample2.hs, interpreted )

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

この理由は、sample2.hs では、"Hello, " ++ name という関数が IO a 型の戻値を戻さず、IOモナド型の関数ではないからだ。sample1.hs の getLine は IO a 型の戻値を戻すし、puStrLn は IO () を戻値として戻す。

それでは、"Hello, " ++ name が IO a 型を戻すことができるように return 関数の引数として戻値を渡したらどうだろうか。

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

これは次のようにきちんとコンパイルされ実行することができる。
Prelude> :e sample2a.hs
Prelude> :l sample2a.hs
[1 of 1] Compiling Main ( sample2.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
Dolly
Hello, Dolly

この例で、IOモナドのプログラミングでは全ての関数が IO モナド型でなければならないこと、IOモナド型の関数は IO a 型の戻値を返さなければならないことが分かる。

IO モナド型の関数のもう一つの条件、「引数が1個である」ということについては次の記事で述べる。
by tnomura9 | 2011-09-07 07:29 | Haskell | Comments(0)
<< System.IO モジュール... System.IO モジュール... >>