Repeated evaluation of pure expression in IO action
I have a procedure that (a) does some IO, (b) constructs a lookup table, and (c) returns an IO action that uses the lookup table. But when compiled with -O
, GHC (version 6.12.1) inlines the construction the lookup table, so that it is reevaluated for every call of the IO action.
Example:
module Main where
import Data.Array
import Data.IORef
import Control.Monad
makeAction getX getY sumRef = do
x <- getX
let a = listArray (0, 1000) [x ..]
return $ do
y <- getY
modifyIORef sumRef (\sum -> sum + a ! y)
main = do
sumRef <- newIORef 0
action <- makeAction getX g开发者_如何学GoetY sumRef
replicateM_ 100000 action
n <- readIORef sumRef
putStrLn (show n)
where
getX = return (1 :: Int)
getY = return 0
Is this issue well-known enough to have a standard GHC-foolproof workaround - or how would you adjust the program so that a
isn't repeatedly being allocated?
The easiest workaround is to force evaluation by using strictness annotations.
{-# LANGUAGE BangPatterns #-}
Then force allocation by simply making a
strict using a !
("bang").
let !a = listArray (0, 1000) [x ..]
Alternatively, if you are working in the IO
monad, strictness annotations may not always help. To force evaluation of an expression before some IO
action is run, you can use evaluate
. For example:
let a = listArray (0, 1000) [x ..]
evaluate a
Try forcing a
when constructing the monadic value to return:
makeAction getX getY sumRef = do
x <- getX
let a = listArray (0, 1000) [x ..]
return $ a `seq` do
y <- getY
modifyIORef sumRef (\sum -> sum + a ! y)
精彩评论