module Control.Memoization.Utils
( memoize
, memoizeLru
, memoizeTime
, constMemoize
, constMemoizeTime
) where
import Control.Concurrent.MVar (newMVar, putMVar, readMVar, swapMVar,
takeMVar)
import qualified Data.Cache.LRU.IO as LRU
import qualified Data.Map.Strict as Map
import Data.Time (diffUTCTime, getCurrentTime)
import Data.Time.Units (Millisecond, toMicroseconds)
memoize :: Ord a => (a -> IO b) -> IO (a -> IO b)
memoize action = do
mm <- newMVar Map.empty
return $ \arg -> do
m <- takeMVar mm
case Map.lookup arg m of
Just ret -> do
putMVar mm m
return ret
Nothing -> do
ret <- action arg
putMVar mm (Map.insert arg ret m)
return ret
memoizeLru :: Ord a => (Maybe Integer) -> (a -> IO b) -> IO (a -> IO b)
memoizeLru msize action = do
lru <- LRU.newAtomicLRU msize
return $ \arg -> do
mret <- LRU.lookup arg lru
case mret of
Just ret -> return ret
Nothing -> do
ret <- action arg
LRU.insert arg ret lru
return ret
memoizeTime :: Ord a => Millisecond -> (a -> IO b) -> IO (a -> IO b)
memoizeTime delay action = do
let delayDiff :: Double
delayDiff = (fromInteger (toMicroseconds delay)) / 1000000
mm <- newMVar Map.empty
return $ \arg -> do
m <- takeMVar mm
case Map.lookup arg m of
Just (ret, lastcall) -> do
now <- getCurrentTime
let diff = fromRational $ toRational $ diffUTCTime now lastcall
if diff >= delayDiff
then do
ret' <- action arg
putMVar mm (Map.insert arg (ret', now) m)
return ret'
else do
putMVar mm m
return ret
Nothing -> do
now <- getCurrentTime
ret <- action arg
putMVar mm (Map.insert arg (ret, now) m)
return ret
constMemoize :: IO b -> IO (IO b)
constMemoize action = do
retmvar <- newMVar Nothing
return $ do
mret <- readMVar retmvar
case mret of
Just ret -> return ret
Nothing -> do
ret <- action
_ <- swapMVar retmvar (Just ret)
return ret
constMemoizeTime :: Millisecond -> IO b -> IO (IO b)
constMemoizeTime delay action = do
let delayDiff :: Double
delayDiff = (fromInteger (toMicroseconds delay)) / 1000000
retmvar <- newMVar Nothing
return $ do
mret <- readMVar retmvar
case mret of
Just (ret, lastcall) -> do
now <- getCurrentTime
let diff = fromRational $ toRational $ diffUTCTime now lastcall
if diff >= delayDiff
then do
ret' <- action
_ <- swapMVar retmvar (Just (ret, now))
return ret'
else return ret
Nothing -> do
ret <- action
lastcall <- getCurrentTime
_ <- swapMVar retmvar (Just (ret, lastcall))
return ret