Here is what I was think of doing with IORef
:
import Data.IORef
import System.IO.Unsafe
import Control.Monad
val_ :: IORef (Maybe Integer)
val_ = unsafePerformIO $ newIORef Nothing
val :: IO Integer
val = do
v <- readIORef val_
case v of
Just v' -> return v'
Nothing -> do
v' <- readFile "large.txt"
-- replace this part with your actual computation
let l = sum $ map (fromIntegral . fromEnum) v'
writeIORef val_ $ Just l
return l
main = do
writeFile "large.txt" (replicate (10^7) '0')
putStrLn "reading"
replicateM_ 10 (val >>= print)
You ensure that the time consuming operation is only ever evaluated once. When you execute val
the first time, it will write the value to the IORef
and retrieve it from there every subsequent time. When I ran main
, it will take a few seconds to print the number the first time and no time at all afterwards.
You need to have unsafePerformIO
because IORef x
can't be garbage collected but IO (IORef x)
will be.
Keep in mind that writing to the IORef
does not evaluate anything, it will be evaluated the first time it is used, even if you call val
earlier.
The simpler solution is probably to use monad transformers. You didn't provide an example of where in your snap program this table will be used, so I can't really give a satisfactory example.