{-# OPTIONS -O2 -Wall #-}

module Data.MList.Memo where

import Data.MList(MList(..), MListItem(..))
import Control.Concurrent.MVar(newMVar,modifyMVar)
import Control.Monad(liftM2)

-- TODO: Can this be expressed with mfoldr/mfoldr'?
memo :: MList IO a -> IO (MList IO a)
memo (MList acx) =
    do
      mv <- newMVar Nothing
      -- Return an mlist that calls fillMemo or getMemo on the mvar
      -- when the element is requested
      return . MList . modifyMVar mv $ maybe fillMemo getMemo
    where
      fillMemo = do
        cx <- acx
        res <- case cx of
                 MNil -> return MNil
                 MCons x xs -> (x `MCons`) `fmap` memo xs
        return (Just res, res)
      getMemo = return . liftM2 (,) Just id