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
。したがって、getLine
has 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: 実際にはもっと一般的なタイプがありますが、現時点では関係ありません。