cached-io 1.2.0.0 → 1.3.0.0
raw patch · 4 files changed
+43/−17 lines, 4 filesdep ~basedep ~timenew-uploaderPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, time
API changes (from Hackage documentation)
+ Control.Concurrent.CachedIO: Cached :: m a -> Cached m a
+ Control.Concurrent.CachedIO: [runCached] :: Cached m a -> m a
+ Control.Concurrent.CachedIO: newtype Cached m a
- Control.Concurrent.CachedIO: cachedIO :: (MonadIO m, MonadIO t, MonadCatch t) => NominalDiffTime -> t a -> m (t a)
+ Control.Concurrent.CachedIO: cachedIO :: (MonadIO m, MonadIO t, MonadCatch t) => NominalDiffTime -> t a -> m (Cached t a)
- Control.Concurrent.CachedIO: cachedIO' :: (MonadIO m, MonadIO t, MonadCatch t) => NominalDiffTime -> (Maybe (UTCTime, a) -> t a) -> m (t a)
+ Control.Concurrent.CachedIO: cachedIO' :: (MonadIO m, MonadIO t, MonadCatch t) => NominalDiffTime -> (Maybe (UTCTime, a) -> t a) -> m (Cached t a)
- Control.Concurrent.CachedIO: cachedIOWith :: (MonadIO m, MonadIO t, MonadCatch t) => (UTCTime -> UTCTime -> Bool) -> t a -> m (t a)
+ Control.Concurrent.CachedIO: cachedIOWith :: (MonadIO m, MonadIO t, MonadCatch t) => (UTCTime -> UTCTime -> Bool) -> t a -> m (Cached t a)
- Control.Concurrent.CachedIO: cachedIOWith' :: (MonadIO m, MonadIO t, MonadCatch t) => (UTCTime -> UTCTime -> Bool) -> (Maybe (UTCTime, a) -> t a) -> m (t a)
+ Control.Concurrent.CachedIO: cachedIOWith' :: (MonadIO m, MonadIO t, MonadCatch t) => (UTCTime -> UTCTime -> Bool) -> (Maybe (UTCTime, a) -> t a) -> m (Cached t a)
Files
- CHANGELOG.md +20/−0
- cached-io.cabal +5/−5
- src/Control/Concurrent/CachedIO.hs +15/−9
- test/test-cachedIO.hs +3/−3
CHANGELOG.md view
@@ -1,5 +1,25 @@ # Revision history for cached-io +## 1.3.0.0++- **Breaking** Caching functions previously returned `m (t a)`, but it was easy to accidentally use `join` when `m` and `t` were the same monad (eg. `IO (IO a)`), and not get any caching at all. These functions now use a `Cached` newtype for `t a` to make it more difficult to misuse.++### Migrating from <=1.2.0.0 to 1.3.0.0++```haskell+-- Previous versions+f :: IO ()+f = do+ cachedAction <- cachedIO action :: IO (IO a)+ cachedResult <- cachedAction++-- New version+f :: IO ()+f = do+ cachedAction <- cachedIO action :: IO (Cached IO a)+ cachedResult <- runCached cachedAction+```+ ## 1.2.0.0 Thank you [glasserc](https://github.com/glasserc) for your work on previous versions, and a special thanks to
cached-io.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: cached-io-version: 1.2.0.0+version: 1.3.0.0 synopsis: A simple library to cache IO actions description: Provides functions that convert an IO action into a cached one by storing the@@ -12,8 +12,8 @@ maintainer: Bellroy Tech Team <haskell@bellroy.com> category: Development build-type: Simple-tested-with: GHC ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.1-extra-source-files:+tested-with: GHC ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.4 || ==9.4.5 || ==9.6.2 || ==9.8.1+extra-doc-files: CHANGELOG.md README.md @@ -22,7 +22,7 @@ location: https://github.com/bellroy/haskell-cached-io.git common deps- build-depends: base >=4.13.0.0 && <4.18+ build-depends: base >=4.13.0.0 && <4.20 library import: deps@@ -30,7 +30,7 @@ build-depends: , exceptions >=0.10.4 && <0.11 , stm >=2.5 && <2.6- , time >=1.9.3 && <1.13+ , time >=1.9.3 && <1.15 , transformers >=0.5.6.2 && <0.7 hs-source-dirs: src/
src/Control/Concurrent/CachedIO.hs view
@@ -3,7 +3,7 @@ -- > -- Downloads a large payload from an external data store. -- > downloadData :: IO ByteString -- >--- > cachedDownloadData :: IO ByteString+-- > cachedDownloadData :: IO (Cached IO ByteString) -- > cachedDownloadData = cachedIO (secondsToNominalDiffTime 600) downloadData -- -- The first time @cachedDownloadData@ is called, it calls @downloadData@,@@ -14,6 +14,7 @@ -- result again. -- module Control.Concurrent.CachedIO (+ Cached(..), cachedIO, cachedIOWith, cachedIO',@@ -26,6 +27,11 @@ import Control.Monad.IO.Class (liftIO, MonadIO) import Data.Time.Clock (NominalDiffTime, addUTCTime, getCurrentTime, UTCTime) +-- | A cached IO action in some monad @m@. Use 'runCached' to extract the action when you want to query it.+--+-- 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}+ data State a = Uninitialized | Initializing | Updating a | Fresh UTCTime a -- | Cache an IO action, producing a version of this IO action that is cached@@ -37,7 +43,7 @@ cachedIO :: (MonadIO m, MonadIO t, MonadCatch t) => NominalDiffTime -- ^ Number of seconds before refreshing cache -> t a -- ^ IO action to cache- -> m (t a)+ -> m (Cached t a) cachedIO interval = cachedIOWith (secondsPassed interval) -- | Cache an IO action, producing a version of this IO action that is cached@@ -50,7 +56,7 @@ => NominalDiffTime -- ^ Number of seconds before refreshing cache -> (Maybe (UTCTime, a) -> t a) -- ^ action to cache. The stale value and its refresh date -- are passed so that the action can perform external staleness checks- -> m (t a)+ -> m (Cached t a) cachedIO' interval = cachedIOWith' (secondsPassed interval) -- | Check if @starting time@ + @seconds@ is after @end time@@@ -70,7 +76,7 @@ -- If 'isCacheStillFresh' 'lastUpdated' 'now' returns 'True' -- the cache is considered still fresh and returns the cached IO action -> t a -- ^ action to cache.- -> m (t a)+ -> m (Cached t a) cachedIOWith f io = cachedIOWith' f (const io) -- | Cache an IO action, The cache begins uninitialized.@@ -84,10 +90,10 @@ -- the cache is considered still fresh and returns the cached IO action -> (Maybe (UTCTime, a) -> t a) -- ^ action to cache. The stale value and its refresh date -- are passed so that the action can perform external staleness checks- -> m (t a)+ -> m (Cached t a) cachedIOWith' isCacheStillFresh io = do cachedT <- liftIO (atomically (newTVar Uninitialized))- return $ do+ pure . Cached $ do now <- liftIO getCurrentTime join . liftIO . atomically $ do cached <- readTVar cachedT@@ -100,12 +106,12 @@ -- thread will get the stale data instead. | otherwise -> do writeTVar cachedT (Updating value)- return $ refreshCache previousState cachedT+ pure (refreshCache previousState cachedT) -- Another thread is already updating the cache, just return the stale value- Updating value -> return (return value)+ Updating value -> pure (pure value) -- The cache is uninitialized. Mark the cache as initializing to block other -- threads. Initialize and return.- Uninitialized -> return $ refreshCache Uninitialized cachedT+ Uninitialized -> pure (refreshCache Uninitialized cachedT) -- The cache is uninitialized and another thread is already attempting to -- initialize it. Block. Initializing -> retry
test/test-cachedIO.hs view
@@ -2,7 +2,7 @@ main ) where -import Control.Concurrent.CachedIO (cachedIO)+import Control.Concurrent.CachedIO (cachedIO, Cached(..)) import Data.List (isInfixOf) crawlTheInternet :: IO [String]@@ -13,9 +13,9 @@ return ["website about Haskell", "website about Ruby", "slashdot.org", "The Monad.Reader", "haskellwiki"] -searchEngine :: String -> IO [String] -> IO [String]+searchEngine :: String -> Cached IO [String] -> IO [String] searchEngine query internet = do- pages <- internet+ pages <- runCached internet return $ filter (query `isInfixOf`) pages main :: IO ()