2026-08-27
Counter.hs
import Data.IORef
import Control.Monad (replicateM_)
main :: IO ()
main = do
ref <- newIORef (0 :: Int)
replicateM_ 1000000 (modifyIORef ref (+1))
total <- readIORef ref
print total
このコードは意味的には正しそうに見えますが、実行すると極端に遅くなったりスタックオーバーフローでクラッシュしたりします。何が問題で、どう直せばよいでしょうか?
Answer
modifyIORefは中身を遅延したまま更新するため、refの中身は実際には計算されず、(((0+1)+1)+1)...+1という巨大なサンク(thunk)の連鎖として積み上がっていきます。最後にreadIORefで取り出してprintが評価しようとした瞬間、この深い連鎖を一気に評価することになり、スタックオーバーフローや大きなメモリ消費につながります。修正は、正格版のmodifyIORef'に変えることです — 各ステップで即座に値を評価するので、サンクが蓄積しません:
replicateM_ 1000000 (modifyIORef' ref (+1))