Haskell の IO は、使い慣れた言語の IO のようには機能しません。Haskell のすべての関数は純粋でなければなりません。つまり、関数fが引数xで呼び出される場合、1 回、2 回、または 100 回呼び出しても違いがあってはなりません。ただし、これが IO にとって何を意味するかを考えてみましょう。単純にgetLine、タイプgetLine :: Stringが 、またはおそらくである必要がありgetLine :: () -> Stringます。(()は unit 型で、値は のみです()。C ライクな言語の void 型のようなものですが、値は 1 つしかありません。) しかし、これは、 を書くたびにgetLine、 を返さなければならないことを意味します。同じ string、これはあなたが望むものではありません。これが型の目的です:アクションIOをカプセル化する. これらのアクションは関数とは異なります。それらは不純な計算を表します (それら自体は純粋ですが)。type の値はIO a、実行時に type の値を返すアクションを表しますa。したがって、getLinehas type getLine :: IO String: アクションが評価されるたびに、String(ユーザーからの読み取りによって) が生成されます。同様に、putStrタイプがputStr :: String -> IO ()あります。これは、文字列を受け取り、実行時に有用な情報を返さないアクションを返す関数ですが、副作用として、画面に何かを出力します。
タイプ の関数を記述しようとしていますIO () -> ([Char], Int)。これは、入力としてアクションを取り、タプルを返す関数になりますが、これはあなたが望むものではありません。実行時に、文字列 ( のシノニム) と整数IO (String, Int)で構成されるタプルを生成するアクションが必要です。[Char]あなたも現在のコードでほとんどそこにいます! 代わりに必要なものは次のとおりです。
investinput :: IO (String, Int)
investinput = do
putStrLn "Enter Username : "
username <- getLine
putStrLn "Enter Invest Amount : "
tempamount <- getLine
let amount = read tempamount
return (username, amount)
2 つの変更 (および空白行の削除) しか行っていないことに注意してください。まず、上で述べたように、関数の型を変更しました。次に、 に変更showしましreadた。関数のshow型Show a => a -> Stringは : 表示可能なものすべてを取り、それを表す文字列を生成する関数です。 タイプがread: 文字列を指定すると、Read a => String -> aそれを解析し、読み取り可能な値を返します。
あなたが尋ねたもう1つのこと(String, Int)は、アクションの代わりにタプルを返すことですIO (String, Int)。これを行う純粋な方法はありません。つまり、純粋な関数はありませんIO a -> a。どうしてこれなの?なぜならIO a、現実世界に依存する不純な行為を表しているからです。そのような関数がある場合、その関数は純粋でなければならないので、impossibleRunIO :: IO a -> aそのようにしたいと思います。しかし、現実の世界と実際にやり取りできるようにしimpossibleRunIO getLine == impossibleRunIO getLineたいので、これは役に立ちません! impossibleRunIOしたがって、この純粋な関数は不可能です。入ったものIOは決して出ることはありません。これが何をするかreturnです: これは、この場合は1の型を持つ関数であり、return :: a -> IO a純粋な値を に配置できるようにしますIO。についてはx、return xは、実行時に常に を生成するアクションxです。doこれが、ブロックをreturn:で終了する必要がある理由です。usernameアクションから抽出した純粋な値であるため、doブロック内でのみ表示されます。IO外の世界がそれを見ることができる前に、それを持ち上げる必要があります。amount/も同様ですtempamount。
完全を期すために、この背後にはいくつかの包括的な理論があり、それを結び付けています。しかし、Haskell プログラミングを始めるにはまったく必要ありません。私がお勧めするのは、ほとんどのコードを純粋な関数として構造化して、データを折りたたんだり、紡錘したり、切断したりすることです。次に、前述の機能と相互作用する薄い (できるだけ薄い)IOフロント レイヤーを構築します。必要な IO の少なさに驚かれることでしょう。
1: 実際にはもっと一般的なタイプがありますが、現時点では関係ありません。