module Control.Monad.Sharing.Memoization (
Untyped(..), typed,
ThunkStore(..), emptyThunkStore, Shared(..), shared, memo
) where
import qualified Data.IntMap as M
import Data.Monoid ()
import Control.Monad.State
import Control.Monad.Writer
import Unsafe.Coerce
data Untyped = forall a . Untyped a
typed :: Untyped -> a
typed (Untyped x) = unsafeCoerce x
data ThunkStore = ThunkStore { freshKey :: Int, thunks :: M.IntMap Untyped }
data Thunk m a = Uneval (m a) | Eval !a
emptyThunkStore :: ThunkStore
emptyThunkStore = ThunkStore { freshKey = 1, thunks = M.empty }
getFreshKey :: MonadState ThunkStore m => m Int
getFreshKey = do
key <- gets freshKey
modify (\s -> s { freshKey = succ key })
return key
insertThunk :: MonadState ThunkStore m => Int -> Thunk m a -> m ()
insertThunk key thunk =
modify (\s -> s { thunks = M.insert key (Untyped thunk) (thunks s) })
lookupThunk :: MonadState ThunkStore m => Int -> m (Thunk m a)
lookupThunk key = liftM (typed . M.findWithDefault err key) (gets thunks)
where err = error $ "lookupThunk: unbound key " ++ show key
newtype Shared = Shared { isShared :: Bool }
instance Monoid Shared
where mempty = Shared False
mappend = flip const
shared :: MonadWriter Shared m => m a -> m a
shared = censor (const (Shared True))
memo :: (MonadState ThunkStore m, MonadWriter Shared m) => m a -> m (m a)
memo a = do
key <- getFreshKey
insertThunk key (Uneval a)
return . shared $ do
thunk <- lookupThunk key
case thunk of
Eval x -> return x
Uneval b -> do
x <- b
insertThunk key (Eval x)
return x