just curious how to rewrite the following function to be called only once during program's lifetime ?
getHeader :: FilePath -> IO String
getHeader fn = readFile fn >>= return . take 13
Above function is called several times from various functions. How to prevent reopening of the file if function gets called with the same parameter, ie. file name ?
I would encourage you to seek a more functional solution, for example by loading the headers you need up front and passing them around in some data structure like for example a Map
. If explicitly passing it around is inconvenient, you can use a Reader
or State
monad transformer to handle that for you.
That said, you can accomplish this the way you wanted using by using unsafePerformIO
to create a global mutable reference to hold your data structure.
import Control.Concurrent.MVar
import qualified Data.Map as Map
import System.IO.Unsafe (unsafePerformIO)
memo :: MVar (Map.Map FilePath String)
memo = unsafePerformIO (newMVar Map.empty)
{-# NOINLINE memo #-}
getHeader :: FilePath -> IO String
getHeader fn = modifyMVar memo $ \m -> do
case Map.lookup fn m of
Just header -> return (m, header)
Nothing -> do header <- take 13 `fmap` readFile fn
return (Map.insert fn header m, header)
I used an MVar
here for thread safety. If you don't need that, you might be able to get away with using an IORef
instead.
Also, note the NOINLINE
pragma on memo
to ensure that the reference is only created once. Without this, the compiler might inline it into getHeader
, giving you a new reference each time.
The simplest thing is to just call it once at the beginning of main
and pass the resulting String
around to all the other functions that need it:
main = do
header <- getHeader
bigOldThingOne header
bigOldThingTwo header
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With