2026-09-07
ThreeRefs.hs
import Data.IORef
import Control.Monad (replicateM)
main :: IO ()
main = do
ref <- newIORef (0 :: Int)
refs <- replicateM 3 (return ref)
mapM_ (\(i, r) -> writeIORef r i) (zip [1, 2, 3] refs)
vals <- mapM readIORef refs
print vals
このコードは[1,2,3]という3つの独立した値を期待していますが、実際には[3,3,3]が出力されます。何が問題で、どう直せばよいでしょうか?
Answer
replicateM 3 (return ref)は、newIORefを3回実行しているわけではなく、すでに1つだけ作られているrefという同じ参照を3回複製してrefsのリストに詰めているだけです。returnはアクションを実行するのではなく、その値をそのままIOアクションでくるむだけなので、refsの3要素は文字通り同じセルを指す3つの別名です。そのためwriteIORefを3回呼ぶたびに同じセルが上書きされ、最後の書き込み(3)だけが残ります。修正は、参照そのものを複製するのではなく、newIORefを3回実行して3つの独立したセルを作ることです:
refs <- replicateM 3 (newIORef 0)