haskell-jp / beginners #22 at 2022-10-18 12:00:34 +0900

遅延評価についてよく分からなくなってきました。
module Main (main) where

import qualified  as SIO

main :: IO ()
main = do
    h <- SIO.openFile "./input.txt" SIO.ReadMode
    content <- SIO.hGetContents h
    SIO.hClose h
    print content

これは実行するとエラーになる理由はわかります。`content` を評価する時には既にファイルがクローズされているから。

module Main (main) where

main :: IO ()
main = do
    putStrLn "please input1 "
    input <- getLine
    putStrLn "please input2 "
    putStrLn $ "your input is " ++ input

同様に考えて、 inputを評価する時に標準入力を受け付けることになり、下のような出力順になるのでは?と考えたのですが、
そうはならず。コードに書いてある順序通りの出力になりました。

please input1 
please input2 
your input is aaa

これまでちゃんと理解せずにいたのですが、混乱しています。

getLineがlazyではないから評価された時点で入力待ち状態になる、と考えると納得できそうですが、
そもそも getLine が評価されるのは「please input2」 を出力した後なのでは?
たぶん「遅延評価」「式を評価する」ということの意味を正確に理解できていないと思うのですが、どなたかご教示いただけないでしょうか。
getLineは遅延IOじゃないので
これ、要はそもそも「遅延評価」の問題ではなくて、 IO モナドの評価自体は(hGetContents であっても)行単位で毎回発生していると考えて構わないが、そのデータの中身自体が hGetContents だと「遅延IO」と呼ばれる特殊なデータ構造だから実際のデータがまだメモリ上に読まれていない、という風にかんがえればいいのですよね?(←わたしもよくわかってない
標準の関数のうち、getContents, hGetContents, readFileは遅延IOなので最初の例のような挙動をしますが、それ以外のIOは(中でgetContentsなどを読んでいない限り)順番通りに実行される、と言う認識で問題ありません。
IO モナドの評価自体は(hGetContents であっても)行単位で毎回発生していると考えて構わない
厳密には行単位ではなく、何バイトかおきだったと記憶しています。
なるほど。ありがとうございます(便乗質問になってしまった^^;)
遅延 IO(遅延評価ではない)はややこしいので、もう忘れてしまって getContents なんかは使わないようにするのがいいと自分は思ってます……
tanakh さんの Lazy I/O must go! っていう記事を読んで以来、私も遅延IOってのはダメなんだろうと思っています^^; (この記事のほとんどの部分はいまさら読むには大分古い内容ですが…)
「your input is~~」を出力する時点で getLine が評価される、という理解は間違ってますよね?
getLine は遅延IOではないから評価された時点で入力待ちになる。
getContents は遅延IOだから評価はされてもメモリにファイルは読み込まれていない。
>>= の定義からして putStrLnの前に getLine は評価されている。
「your input is~~」を出力する時点で getLine が評価される、という理解は間違ってますよね?
はい。
手前味噌ですが、すこし前に以下のようなメモをつくりました。参考になるかもしれません。
私自身は、可能であれば関数的に考えてプログラミングをしたい派なので、遅延I/O :heart: です。