minus1216
            
          IOの正体はRealWorldに対する読み書きを表す(State# RealWorld -> (# State# RealWorld, a #))だと聞いたのですが、RealWorldからの読み出しのみすることを表す(State# RealWorld -> a)に対応する型はありますか?
        f :: State# RealWorld -> a f = const undefined
State# RealWorld -> a は RealWorld からの読み出しを表さないState# RealWorld -> a を表す newtype は普通のライブラリからはエクスポートされてないnewtype Bad a = Bad (State# RealWorld -> a) ってやれば自分で定義することはできるnewtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #)) なのだから、次の ioToBad 関数が書けるので、任意の IO a から Bad a が作れる。読み書きのどっちであるかは関係ない。 そしてこの Bad a の値は(きちんと意味のある I/O 操作をするために I/O操作たちの間で順序を強制するための)役には立たない。ioToBad :: IO a -> Bad a ioToBad (IO f) = Bad $ \s0 -> case (f s0) of (# s1, a #) -> a
State# RealWorld の値は世界全体を保存している訳ではないので、例えばnewIORef# :: a -> State# RealWorld -> (# State# RealWorld, IORef a #) newIORef# a = case newIORef a of IO x -> x writeIORef# :: IORef a -> a -> State# RealWorld -> (# State# RealWorld, () #) writeIORef# ref a = case writeIORef ref a of IO f -> f readIORef# :: IORef a -> State# RealWorld -> (# State# RealWorld, a #) readIORef# ref = case readIORef ref of IO f -> f badReadIORef# :: IORef a -> State# RealWorld -> a badReadIORef# ref = case ioToBad (readIORef ref) of Bad f -> f
\s0 -> let (# s1, ref1 #) = newIORef# (0::Int) s0
           a              = badReadIORef# ref s1
           (# s2, () #)   = writeIORef# (2::Int) s1
           (# s3, ref2 #) = seq a (newIORef# a) s2
        in readIORef# ref2 s3s1 のタイミングの ref1 の値 0 ではなく、`a` が評価されたタイミング (`s2` のタイミング) の値 2 になってしまうのでo って、現状多くののユースケースで (# State# s, a #) の形をしていますが、 runST やら unsafePerformIO で使うなら別に実際の実装のようにrunST (ST f) = case runRW# f of (# !_, a #) -> a
runST (ST f) = runRW# $ \s -> case f s of (# !_, a #) -> a
newtype を作ることはできますが、定義から原理的に読み出ししかできない型っていうのは無理じゃないかなと思います…。State# RealWorld -> a 単体で実行しちゃうと上記の「変化した後のリアルワールド」を見失ってしまうのでその後はもう IO ができなくなってしまうように思います。