| Prev: List モナド | TOC: 目次 | Next: State モナド |
| 計算のタイプ: | I/O を実行する計算 |
|---|---|
| 束縛の戦略: | I/O 動作はそれらが束縛された順番に実行される。 失敗すると I/O エラーが投げられ、それを捕捉、処理することができる |
| 利用場面: | Haskell のプログラム内で I/O を実行する |
| ゼロおよびプラス: | なし |
| 型の例: | IO a |
入出力は、参照透明ではなく、副作用があるので、純粋な関数型言語とは 相容れないものです。IO モナドはこの問題を、入出力の実行を IO モナド内に 閉じ込めることで解決します。
IO モナドの定義はプラットフォーム依存です。データ構築子もエクスポート されていなければ、IO モナドからデータを取り除くための関数も提供されて いません。というわけで、IO モナドは一方向モナドであり、 副作用および非参照透明な動作を IO モナド命令型スタイルの計算に隔離する ことで、関数プログラムの安全性を確保するのに不可欠です。
このチュートリアルを通じて、モナド値を計算と呼びならわしてきましたが、 IO モナドの値は、しばしば I/O 動作 と呼ばれます。ここでは こちらの用語を使います。
Haskell では、トップレベルの main 関数の型は
IO () でなければなりません。したがって、プログラムは
トップレベルでは、命令型スタイルの入出力動作の並びとして構成され、
それが関数型スタイルのコードを呼ぶことになります。IO
モジュールからエクスポートされている関数は、入出力そのものを
実行するわけではありません。入出力動作を返し、そこに実行すべき
入出力操作が記述されています。入出力動作は IO モナド内で(純粋に
関数的に)合成され、さらに複雑な入出力動作を生成します。その結果として
最終的な入出力動作は、そのプログラムの main 関数の値と
なります。
標準プレリュードと
IO module
では、IO モナド中で使える多くの関数が定義されています。Haskell の
プログラマであれば、まちがいなく、そのうちいくつかには慣れていること
でしょう。このチュートリアルでは、IO モナドのモナドとしての側面について
議論し、入出力を実行する関数のすべてについては議論しません。
IO 型構築子は Monad クラスおよび
MonadError クラスのメンバーです。そのエラーの型は
IOErrorです。fail は文字列引数から構成された
エラーを投げるように定義されています。IO モナド内では、
Control.Monad.Error モジュールをインポートしていれば、
そのモナドテンプレートライブラリで例外機構を使うことができます。
同じ機構は IO モジュールでエクスポートされている
ioError および catch ででも可能です。
instance Monad IO where
return a = ... -- function from a -> IO a
m >>= k = ... -- executes the I/O action m and binds the value to k's input
fail s = ioError (userError s)
data IOError = ...
ioError :: IOError -> IO a
ioError = ...
userError :: String -> IOError
userError = ...
catch :: IO a -> (IOError -> IO a) -> IO a
catch = ...
try :: IO a -> IO (Either IOError a)
try f = catch (do r <- f
return (Right r))
(return . Left)
|
IO モナドはモナドテンプレートライブラリフレームワークに
MonadError クラスのインスタンスとして組込まれています。
instance Error IOError where
...
instance MonadError IO where
throwError = ioError
catchError = catch
|
IO モジュールは try という便利な関数を
エクスポートしています。この関数は入出力動作を実行し、その動作が
成功すれば、Right result を、入出力エラーが捕捉されれば、
Left IOError を返します。
この例は、標準入力ストリームを標準出力ストリームに、コマンドライン引数
にしたがって、文字変換を行いつつ複写する、"tr" コマンドの部分的な実装
です。これは、IO モナドでの MonadError の
例外処理機構の使いかたを例示したものです。
| example14.hs で使えるコード |
|---|
import Monad
import System
import IO
import Control.Monad.Error
-- set1 内の文字と対応する set2 内の文字に変換する
translate :: String -> String -> Char -> Char
translate [] _ c = c
translate (x:xs) [] c = if x == c then ' ' else translate xs [] c
translate (x:xs) [y] c = if x == c then y else translate xs [y] c
translate (x:xs) (y:ys) c = if x == c then y else translate xs ys c
-- 文字列全体を変換する
translateString :: String -> String -> String -> String
translateString set1 set2 str = map (translate set1 set2) str
usage :: IOError -> IO ()
usage e = do putStrLn "Usage: ex14 set1 set2"
putStrLn "Translates characters in set1 on stdin to the corresponding"
putStrLn "characters from set2 and writes the translation to stdout."
-- コマンドライン引数にしたがって、標準入力を標準出力へ変換する
main :: IO ()
main = (do [set1,set2] <- getArgs
contents <- hGetContents stdin
putStr $ translateString set1 set2 contents)
`catchError` usage
|
| Prev: List モナド | TOC: 目次 | Next: State モナド |