cached-io 1.3.1.0 → 1.3.2.0
raw patch · 3 files changed
+55/−8 lines, 3 filesnew-uploaderPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Control.Concurrent.CachedIO: Config :: (UTCTime -> Timestamped a -> Bool) -> (Maybe (Timestamped a) -> m a) -> Config (m :: Type -> Type) a
+ Control.Concurrent.CachedIO: [isFresh] :: Config (m :: Type -> Type) a -> UTCTime -> Timestamped a -> Bool
+ Control.Concurrent.CachedIO: [refreshAction] :: Config (m :: Type -> Type) a -> Maybe (Timestamped a) -> m a
+ Control.Concurrent.CachedIO: cachedIOFromConfig :: forall m (t :: Type -> Type) a. (MonadIO m, MonadIO t, MonadCatch t) => Config t a -> m (Cached t a)
+ Control.Concurrent.CachedIO: cachedSTMFromConfig :: forall (m :: Type -> Type) a. (MonadIO m, MonadCatch m) => Config m a -> STM (Cached m a)
+ Control.Concurrent.CachedIO: data Config (m :: Type -> Type) a
+ Control.Concurrent.CachedIO: instance GHC.Base.Functor m => GHC.Base.Functor (Control.Concurrent.CachedIO.Cached m)
+ Control.Concurrent.CachedIO: type Timestamped a = (UTCTime, a)
Files
- CHANGELOG.md +4/−0
- cached-io.cabal +1/−1
- src/Control/Concurrent/CachedIO.hs +50/−7
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for cached-io +## 1.3.2.0++- Add `Functor` instance for `Cached`.+ ## 1.3.1.0 - Add `cachedSTM` versions of all bindings.
cached-io.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: cached-io-version: 1.3.1.0+version: 1.3.2.0 synopsis: A simple library to cache IO actions description: Provides functions that convert an IO action into a cached one by storing the
src/Control/Concurrent/CachedIO.hs view
@@ -1,3 +1,6 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DerivingStrategies #-}+ -- | Example usage: -- -- > -- Downloads a large payload from an external data store.@@ -16,12 +19,15 @@ -- it. In that case, the stored value will be returned in the meantime. module Control.Concurrent.CachedIO ( Cached (..),+ Config (..),+ Timestamped, -- * IO cachedIO, cachedIOWith, cachedIO', cachedIOWith',+ cachedIOFromConfig, -- * STM -- $stm@@ -29,6 +35,7 @@ cachedSTMWith, cachedSTM', cachedSTMWith',+ cachedSTMFromConfig, ) where @@ -49,9 +56,22 @@ -- -- Note that using 'Control.Monad.join' when the cached action and the outer monad are the same will ignore caching. newtype Cached m a = Cached {runCached :: m a}+ deriving stock (Functor) data State a = Uninitialized | Initializing | Updating a | Fresh UTCTime a +-- | A cached value and when it was cached.+type Timestamped a = (UTCTime, a)++data Config m a = Config+ { -- | @isFresh now (timestamp, value)@ should return 'False' if @value@ is+ -- considered to have expired, 'True' otherwise.+ isFresh :: UTCTime -> Timestamped a -> Bool,+ -- | Produces a new value. The currently-cached value is supplied if it is+ -- available.+ refreshAction :: Maybe (Timestamped a) -> m a+ }+ -- | Cache an IO action, producing a version of this IO action that is cached -- for 'interval' seconds. The cache begins uninitialized. --@@ -105,16 +125,27 @@ cachedIOWith' :: (MonadIO m, MonadIO t, MonadCatch t) => -- | Test function:- -- If 'isCacheStillFresh' 'lastUpdated' 'now' returns 'True'+ -- If @isCacheStillFresh lastUpdated now@ returns 'True' -- the cache is considered still fresh and returns the cached IO action (UTCTime -> UTCTime -> Bool) -> -- | Action to cache. The stale value and its refresh date -- are passed so that the action can perform external staleness checks (Maybe (UTCTime, a) -> t a) -> m (Cached t a)-cachedIOWith' isCacheStillFresh refreshAction =- liftIO . atomically $ cachedSTMWith' isCacheStillFresh refreshAction+cachedIOWith' isFresh' refreshAction' =+ cachedIOFromConfig+ Config+ { isFresh = \now (lastUpdated, _) -> isFresh' lastUpdated now,+ refreshAction = refreshAction'+ } +-- | Cache an IO action; the cache begins uninitialized.+--+-- The outer IO is responsible for setting up the cache. Use the inner one to+-- either get the cached value or refresh+cachedIOFromConfig :: (MonadIO m, MonadIO t, MonadCatch t) => Config t a -> m (Cached t a)+cachedIOFromConfig = liftIO . atomically . cachedSTMFromConfig+ -- $stm -- -- The following actions are the exactly same as the 'cachedIO' versions, except@@ -159,14 +190,26 @@ cachedSTMWith' :: (MonadIO m, MonadCatch m) => -- | Test function:- -- If @'isCacheStillFresh' lastUpdated now@ returns 'True'+ -- If @isCacheStillFresh lastUpdated now@ returns 'True' -- the cache is considered still fresh and returns the cached IO action (UTCTime -> UTCTime -> Bool) -> -- | Action to cache. The stale value and its refresh date -- are passed so that the action can perform external staleness checks (Maybe (UTCTime, a) -> m a) -> STM (Cached m a)-cachedSTMWith' isCacheStillFresh refreshAction = do+cachedSTMWith' isFresh' refreshAction' =+ cachedSTMFromConfig+ Config+ { isFresh = \now (lastUpdated, _) -> isFresh' lastUpdated now,+ refreshAction = refreshAction'+ }++-- | Set up a cached IO action in a transaction; the cache begins uninitialized.+cachedSTMFromConfig ::+ (MonadIO m, MonadCatch m) =>+ Config m a ->+ STM (Cached m a)+cachedSTMFromConfig config = do cachedT <- newTVar Uninitialized pure . Cached $ do now <- liftIO getCurrentTime@@ -175,7 +218,7 @@ case cached of previousState@(Fresh lastUpdated value) -- There's data in the cache and it's recent. Just return.- | isCacheStillFresh lastUpdated now -> pure (pure value)+ | isFresh config now (lastUpdated, value) -> pure (pure value) -- There's data in the cache, but it's stale. Update the cache state -- to prevent a second thread from also executing the action. The second -- thread will get the stale data instead.@@ -198,7 +241,7 @@ Fresh lastUpdated value -> Just (lastUpdated, value) _ -> Nothing newValue <-- refreshAction previous+ refreshAction config previous `onException` liftIO (atomically (writeTVar cachedT previousState)) liftIO $ do now <- getCurrentTime