HaskellのIOモナドは副作用は有るかも知れないけど、参照透明性は失われてないと思う

すごいH本を読み終えての感想。
IOモナド、あるいはアクションと呼ばれるものは、Haskellの中ではリストやMybeと同列。
ただIO型はモナドインスタンスにしかなってないだけ。
(まあEqもOrdも出来ないからインスタンスになれないよね…)
なのでリストの中にputStrとかを入れたりできる。

IO型の関数はモナドで逐次処理をエミュレートしながらも、遅延評価のおかげで参照透明性は失われてないと思う。
エミュレートは所詮エミュレートだと言う事。

例えば、
文字を入力して出力するプログラム。

main = do
    x <- getContents
    putStr x

一見、putStr xから先に実行すると破綻しそうだが、遅延評価なのでputStrが必要になったらgetContentsを呼びに行く。
だから入力→出力の順で実行される。
(というか、実際そんな順序で実行されてる)
もちろん、コード通りにgetContentsから実行されれば普通の逐次処理。
ちなみに、ループが書かれてないけどこれは入力と出力を永遠に続ける無限ループします。
というのは、ファイルの終端まで入力してから出力するはずが、標準入力にはファイルの終端が無いため、遅延評価により一行毎(入力確定毎)に出力を繰り返します。
作ったプログラムにリダイレクトでファイルを入力するようにすると、期待通りファイル内容を表示して終了します。

次に、入力だけのプログラム。

main = do
     x <- getContents

これはdo表記を使わないとこうなる。

main = getContents >>= \x -> x

つまり、xに値が入ってから入力が開始されるのはおかしい。
では、Haskellはどう解決するのか?
遅延評価は求められない限り何もしない。
つまり、出力しない値は無いも同じなので、入力も何もせずに終了する。
ほらね?
参照透明性は失われてないでしょ?

すごいH本のファンクターまで読んだときに思ったのは、IO型はプログラム型とかアプリ型とか呼びたくなるって事。
リストやMybeとかと同じ値を入れる型だけど、すごいH本で言う文章付き型(複数の値を入れる型や、失敗の可能性のある型)とすれば、プログラムとして動作する型とでも言おうか…。
型としてのプログラム型は同じ形の入れ物。(だから、Haskellの文法上副作用はない)
プログラム型がプログラムとして実行されると副作用を伴う的な。
済みません。
まだ頭の中で整理できてない。