Namaran

Code daily. Without assist.

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)