本旨
IO
値を生成する操作と値自体のHaskellの違いを明確にしてください。
おすすめの読み物
モナドの理論的基礎を知りたくないと思われるHaskellのIOの良い参考資料は、sigfpeの
「単に気にしない人々のためのIOモナド」です。
彼があなたが気にしないと仮定しているのはモナド理論のビットであることに注意してください。彼はあなたがIOをすることに関心があると仮定しています。それはそれほど長くはありません、そしてそれはあなたがあなたに気づいていないいくつかの「ルール」を明示するのであなたにとって非常に読む価値があると思います。それはあなたに苛立ちを引き起こします。
あなたのコード
とにかく、あなたのコードで
x <- readFile "foo.txt"
readFile "foo.txt"
ビットの型IO String
は、文字列を生成する操作であることを意味します。を行うときは、それが生成する文字列を参照するためにx <- readFile "foo.txt"
使用します。出力とそれを生成した操作x
の違いに注意してください。x
readFile "foo.txt"
次に、を見てみましょうy
。を定義するlet y = read x :: [Int]
と、y
指定したIntのリストも定義されます。ただし、y
それを定義するチャンク全体と同じではありません。
example = do
x <- readFile "foo.txt"
let y = read x :: [Int]
return y
ここでexample :: IO [Int]
は、それy
自体はタイプ[Int]
です。
あなたの欲求不満の原因
命令型言語を使用している場合、これは最初はイライラします。値を使用する場所ならどこでも値を生成する関数を使用できることに慣れていますが、任意のIO操作の実行も許可されている関数にも慣れています。 。
Haskellでは、「純粋」関数(IOを使用しない)を使用して好きなことを実行できますが、IO操作は実行できません。Haskellプログラマーは、他のIO操作でのみ再利用できる値を返すIO操作と、どこでも使用できる純粋関数との違いの世界全体を見ています。
これは、厄介なIOモナドに常に閉じ込められてしまう可能性があり、すべての関数がIOデータ型でいっぱいになることを意味します。これは不便で、面倒なコードを書くことになります。
IOの混乱を回避する方法
まず、外部(ファイルまたはユーザー)データを使用せずに、問題全体を解決します。
- 通常ファイルまたはユーザーから読み取るサンプルデータをソースコードの値として書き込みます。
- 関数のパラメーターとして、定義に必要なデータを使用して関数を記述します。これは、純粋なコードを記述しているときにデータを取得できる唯一の方法です。最初に純粋なコードを記述します。
- 標本データで関数をテストします。(必要に応じて、新しい関数を作成するたびにghciをリロードして、期待どおりに動作することを確認できます。)
- IOなしでプログラムが完成したら、最後に純粋なコードのラッパーとしてプログラムを導入できます。
readFile
つまり、プログラムでは、ほぼ完了するまで、他のIOコードを記述すべきではないと思います。
これは完全に異なるワークフローです。命令型言語では、データを読み取るためのコードを記述し、次に何かを実行してから、データを書き込みます。Haskellでは、機能が正しいことがわかったら、最初に処理を実行するコードを記述し、次にデータを読み書きするコードを記述したほうがよいでしょう。