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 s3
s1
のタイミングの 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 ができなくなってしまうように思います。