packages feed

effectful-data-cache 0.2.1.0 → 0.2.2.0

raw patch · 5 files changed

+120/−5 lines, 5 filesdep +stmdep +unordered-containersPVP ok

version bump matches the API change (PVP)

Dependencies added: stm, unordered-containers

API changes (from Hackage documentation)

+ Effectful.Cache.STM: data Store k v
+ Effectful.Cache.STM: newStore :: Maybe TimeSpec -> IO (Store k v)
+ Effectful.Cache.STM: runCacheSTM :: forall k v (es :: [Effect]) a. IOE :> es => Maybe TimeSpec -> Eff (Cache k v ': es) a -> Eff es a
+ Effectful.Cache.STM: runCacheSTMWith :: forall k v (es :: [Effect]) a. IOE :> es => Store k v -> Eff (Cache k v ': es) a -> Eff es a

Files

CHANGELOG.md view
@@ -1,3 +1,9 @@+## [2.2.0](https://github.com/haskell-github-trust/effectful-data-cache/compare/2.1.0...2.2.0) (2026-10-27)++### Features++* add stm-based interpreter ([5b76933](https://github.com/haskell-github-trust/effectful-data-cache/commit/5b7693322b1fef87f9753aaf24e527764bf688a2))+ ## [2.1.0](https://github.com/haskell-github-trust/effectful-data-cache/compare/2.0.2...2.1.0) (2026-10-26)  ### Features
effectful-data-cache.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           effectful-data-cache-version:        0.2.1.0+version:        0.2.2.0 synopsis:       Data cache effect for the `effectful` system description:    Please see the README on GitHub at <https://github.com/haskell-github-trust/effectful-data-cache#readme> category:       cache@@ -29,6 +29,7 @@ library   exposed-modules:       Effectful.Cache+      Effectful.Cache.STM   other-modules:       Paths_effectful_data_cache   autogen-modules:@@ -42,12 +43,15 @@     , clock ==0.8.*     , effectful-core >=2.3 && <3     , hashable >=1.5 && <2+    , stm >=2.5 && <3+    , unordered-containers ==0.2.*   default-language: Haskell2010  test-suite effectful-data-cache-test   type: exitcode-stdio-1.0   main-is: Spec.hs   other-modules:+      Effectful.Cache.STMSpec       Effectful.CacheSpec       Paths_effectful_data_cache   autogen-modules:@@ -65,4 +69,6 @@     , effectful-data-cache     , hashable >=1.5 && <2     , hspec+    , stm >=2.5 && <3+    , unordered-containers ==0.2.*   default-language: Haskell2010
+ src/Effectful/Cache/STM.hs view
@@ -0,0 +1,91 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}++-- | 'Cache' interpreter over a 'HashMap' in a 'TVar', without "Data.Cache".+module Effectful.Cache.STM (+  Store,+  newStore,+  runCacheSTM,+  runCacheSTMWith,+) where++import Control.Concurrent.STM (+  STM,+  TVar,+  atomically,+  modifyTVar',+  newTVarIO,+  readTVar,+  writeTVar,+ )+import qualified Data.HashMap.Strict as HM+import Effectful (Eff, IOE, MonadIO (liftIO), type (:>))+import Effectful.Cache (Cache (..))+import Effectful.Dispatch.Dynamic (interpret)+import System.Clock (Clock (Monotonic), TimeSpec, getTime)++-- | Entries with their absolute expiration, and the default expiration.+--+-- @since 0.2.2.0+data Store k v = Store (TVar (HM.HashMap k (v, Maybe TimeSpec))) (TVar (Maybe TimeSpec))++-- | Fresh store with the given default expiration.+--+-- @since 0.2.2.0+newStore :: Maybe TimeSpec -> IO (Store k v)+newStore ts = Store <$> newTVarIO HM.empty <*> newTVarIO ts++-- | Run against a fresh store with the given default expiration.+--+-- @since 0.2.2.0+runCacheSTM+  :: forall k v es a+   . IOE :> es+  => Maybe TimeSpec+  -> Eff (Cache k v : es) a+  -> Eff es a+runCacheSTM ts eff = do+  s <- liftIO (newStore ts)+  runCacheSTMWith s eff++-- | Run against an existing 'Store', e.g. one shared between threads.+--+-- @since 0.2.2.0+runCacheSTMWith+  :: forall k v es a+   . IOE :> es+  => Store k v+  -> Eff (Cache k v : es) a+  -> Eff es a+runCacheSTMWith s = interpret $ \_ op -> liftIO $ do+  now <- getTime Monotonic+  atomically (withStore s now op)++withStore :: Store k v -> TimeSpec -> Cache k v m a -> STM a+withStore (Store ref def) now op = case op of+  Insert k v -> readTVar def >>= \ts -> modifyTVar' ref (HM.insert k (v, (now +) <$> ts))+  Insert' ts k v -> modifyTVar' ref (HM.insert k (v, (now +) <$> ts))+  Lookup k -> do+    m <- HM.lookup k <$> readTVar ref+    case m of+      Just (v, e) | live e -> pure (Just v)+      Just _ -> Nothing <$ modifyTVar' ref (HM.delete k)+      Nothing -> pure Nothing+  Lookup' k -> do+    m <- HM.lookup k <$> readTVar ref+    pure $ case m of+      Just (v, e) | live e -> Just v+      _ -> Nothing+  Keys -> HM.keys <$> readTVar ref+  Delete k -> modifyTVar' ref (HM.delete k)+  FilterWithKey p -> modifyTVar' ref (HM.filterWithKey (\k (v, _) -> p k v))+  Purge -> writeTVar ref HM.empty+  PurgeExpired -> modifyTVar' ref (HM.filter (live . snd))+  Size -> HM.size <$> readTVar ref+  DefaultExpiration -> readTVar def+  SetDefaultExpiration ts -> writeTVar def ts+ where+  live = maybe True (> now)
+ test/Effectful/Cache/STMSpec.hs view
@@ -0,0 +1,9 @@+module Effectful.Cache.STMSpec (spec) where++import Effectful (runEff)+import Effectful.Cache.STM (runCacheSTM)+import Effectful.CacheSpec (specWith)+import Test.Hspec (Spec)++spec :: Spec+spec = specWith (\ts -> runEff . runCacheSTM ts)
test/Effectful/CacheSpec.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeApplications #-} -module Effectful.CacheSpec (spec) where+module Effectful.CacheSpec (spec, specWith) where  import Data.List (sort) import Effectful@@ -10,14 +11,16 @@ import Test.Hspec import Prelude hiding (lookup) -run :: Maybe TimeSpec -> Eff '[Cache String Int, IOE] a -> IO a-run ts = runEff . runCache ts+type Runner = forall a. Maybe TimeSpec -> Eff '[Cache String Int, IOE] a -> IO a  expired :: Maybe TimeSpec expired = Just (TimeSpec (-1) 0)  spec :: Spec-spec = do+spec = specWith (\ts -> runEff . runCache ts)++specWith :: Runner -> Spec+specWith run = do   it "looks up what it inserted" $ do     r <- run Nothing $ do       insert "a" (1 :: Int)