{-# LANGUAGE BangPatterns #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE RankNTypes #-} module Reanimate.Memo ( Key(..) , memo ) where import Data.Dynamic import Data.IORef import qualified Data.Map as Map import Data.Maybe import Data.Typeable import System.IO.Unsafe import System.Mem.StableName data DynamicName = forall a. DynamicName !(StableName a) | forall a. (Eq a, Ord a, Typeable a) => DynamicKey a instance Eq DynamicName where DynamicName a == DynamicName b = eqStableName a b DynamicKey a == DynamicKey b = case cast a of Nothing -> False Just a' -> a'==b _ == _ = False instance Ord DynamicName where DynamicName a `compare` DynamicName b = hashStableName a `compare` hashStableName b DynamicName{} `compare` _ = LT DynamicKey a `compare` DynamicKey b = case cast a of Nothing -> typeOf a `compare` typeOf b Just a' -> a' `compare` b _ `compare` _ = GT data CacheMap = CacheMap !(Map.Map DynamicName CacheMap) !(Map.Map DynamicName Dynamic) emptyCacheMap :: CacheMap emptyCacheMap = CacheMap Map.empty Map.empty cacheMapLookup :: [DynamicName] -> CacheMap -> Maybe Dynamic cacheMapLookup [] _ = Nothing cacheMapLookup [k] (CacheMap _ vals) = Map.lookup k vals cacheMapLookup (k:ks) (CacheMap sub _) = cacheMapLookup ks =<< Map.lookup k sub cacheMapInsert :: [DynamicName] -> Dynamic -> CacheMap -> CacheMap cacheMapInsert [] _ m = m cacheMapInsert [k] v (CacheMap sub vals) = CacheMap sub (Map.insert k v vals) cacheMapInsert (k:ks) v (CacheMap sub vals) = CacheMap (Map.alter fn k sub) vals where fn = Just . cacheMapInsert ks v . fromMaybe emptyCacheMap {-# NOINLINE cacheMap #-} cacheMap :: IORef CacheMap cacheMap = unsafePerformIO (newIORef emptyCacheMap) data Key = forall a. Key !a | forall a. (Typeable a, Eq a, Ord a) => KeyPrim !a fromKey :: Key -> IO DynamicName fromKey (Key val) = DynamicName <$> makeStableName val fromKey (KeyPrim val) = pure (DynamicKey val) memo :: Typeable a => [Key] -> a -> a memo !k v = unsafePerformIO $ do keys <- mapM fromKey k atomicModifyIORef' cacheMap $ \m -> case fromDynamic =<< cacheMapLookup keys m of Just v' -> (m, v') Nothing -> (cacheMapInsert keys (toDyn v) m, v)