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 モナド |