{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

{-|
Maintainer  : ylilarry@gmail.com
Stability   : Experimental
Portability : GHC

This module has everything you need to cache a function @(a -> b)@

@
   let cache = newCache Nothing :: Cache Int Int
   let f = do
      withCache doSomethingSlow 3 -- Slow; the result is cached in a map using 3 as the key.
      withCache doSomethingSlow 3 -- Read from the cache.
      withCache doSomethingSlow 3 :: SCache Int Int
   evalState f cache 
@

-}
module Cache.State (
      Cache,
      SCacheT,
      SCache,
      IsSCacheT(..)
   ) where

import Prelude hiding (lookup)
import Cache.Internal as I
import Control.Monad.State

type SCacheT k v n = StateT (Cache k v) n v
type SCache k v = State (Cache k v) v
 

class (IsCache m k, Monad n) => IsSCacheT m k n where  
   -- | @withCache action x@, "x" is an argument for the action.
   -- 
   -- If "x" is a key in the cache, ignore the "action" and return the cached value;
   --
   -- Otherwise perform the action and cache the result using "x" as a key. 
   withCache :: (k -> n v) -> k -> StateT (m k v) n v
   withCache action k = do
      cache <- get
      case lookup k cache of
         Just v  -> return v
         Nothing -> do
            v <- lift $ action k
            modify (insert k v)
            return v

instance (IsCache m k, Monad n) => IsSCacheT m k n