0

なぜ2つの異なる結果が得られるのか理解できませんがIO、それは私が嫌いになり始めていると関係があると確信しています。

例えば:

  ghci> x <- readFile "foo.txt"
  ghci> let y = read x :: [Int]
  ghci> :t y
  y :: [Int]

今、私がそのファイルを作成して同じことをすると、それはIO [Int]

foo.txtこれだけを含むtxtファイルです:12345

これを私に説明できる人はいますか?スナップしようとしています!

洞察をありがとう!

4

4 に答える 4

6

ghciについて読んでください。引用するには

GHCiプロンプトで受け入れられるステートメントの構文は、Haskelldo式のステートメントの構文とまったく同じです。ただし、ここではモナドのオーバーロードはありません。プロンプトで入力されたステートメントは、IOモナドに含まれている必要があります。

基本的にIO、ghciで何かを書いているときは、モナドの中にいます。

于 2012-09-30T19:01:43.543 に答える
5

本旨

IO値を生成する操作と値自体のHaskellの違いを明確にしてください。

おすすめの読み物

モナドの理論的基礎を知りたくないと思われるHaskellのIOの良い参考資料は、sigfpeの 「単に気にしない人々のためのIOモナド」です

彼があなたが気にしないと仮定しているのはモナド理論のビットであることに注意してください。彼はあなたがIOをすることに関心があると仮定しています。それはそれほど長くはありません、そしてそれはあなたがあなたに気づいていないいくつかの「ルール」を明示するのであなたにとって非常に読む価値があると思います。それはあなたに苛立ちを引き起こします。

あなたのコード

とにかく、あなたのコードで

x <- readFile "foo.txt"

readFile "foo.txt"ビットの型IO Stringは、文字列を生成する操作であることを意味します。を行うときは、それが生成する文字列を参照するためにx <- readFile "foo.txt"使用します。出力とそれを生成した操作xの違いに注意してください。xreadFile "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では、機能が正しいことがわかったら、最初に処理を実行するコードを記述し、次にデータを読み書きするコードを記述したほうがよいでしょう。

于 2012-09-30T22:13:56.120 に答える
4

Haskellソースファイルで実際にまったく同じことを行うことはできないので、実際に行ったことは次のようになっていると思います。

readFoo = do
    x <- readFile "foo.txt"
    let y = read x :: [Int]
    return y

そして、タイプが戻ってきたのに、タイプがとしてreadFoo出てくることに驚いています。IO [Int]y[Int]

これが事実である場合、あなたの混乱の原因はreturn、Haskellでは命令型言語からのreturnステートメントではないということです。

returnHaskellでは関数です。完全に普通の機能。他の関数と同様に、あるタイプの値を入力として受け取り、他のタイプの値を出力として提供します。IOreturn任意のモナドで使用できますが、ここでは単純にしておきます)の場合に特化して、次のタイプになります。

a -> IO a

したがって、任意のタイプの値を取り、IOモナドにラップされた同じタイプの値を提供します。したがって、yがタイプの場合[Int]、次にreturn yタイプIO [Int]があり、それが。の結果として得られるものですreadFoo

[Int]の「外」を取得する方法はありませんIO [Int]。これは意図的なものです。の要点はIO、プログラムの「外部」に依存する値は、IO x型にのみ表示できるということです。したがって、内部で「foo.txt」を読み取り、その中の整数のリストを返す関数は、Haskellで書き込むことが不可能である必要があります。そうしないと、カードの家全体が機能しなくなります。あなたが書き込もうとしていたように、のような型の関数を見た場合、それは整数の1つの特定のリストにしかなり得ないことがreadFoo :: [Int]わかります。readFooディスク上のファイルの内容に依存するリストにすることはできません。

于 2012-10-01T02:50:05.343 に答える
2

GHCiは、コマンドプロンプトですばやくテストできるようにするために、少し魔法をかけています。これがプログラムの一部である場合、それIO [Int]は確かに正しいタイプになります。GHCiを使用するとInt、入力を少し節約するためだけに扱うことができます。

それが「理由」です。

さて、「型署名がこれだとしたら、どうすればXを実行できますか?」のような特定の質問がある場合...

于 2012-09-30T19:50:18.887 に答える