Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memoized IO function?

Tags:

haskell

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 ?

like image 302
David Unric Avatar asked Feb 26 '12 15:02

David Unric


2 Answers

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.

like image 102
hammar Avatar answered Oct 16 '22 13:10

hammar


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
like image 4
Daniel Wagner Avatar answered Oct 16 '22 11:10

Daniel Wagner