packages feed

haxl-effectful 1.1.0 → 1.1.1

raw patch · 5 files changed

+52/−9 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/). +## [1.1.1] - 2026-10-01++### Changed++- `haxl`, `haxlWithWrites`, `haxlWithStats` no longer clone the environment.+ ## [1.1.0] - 2026-08-04  ### Added
haxl-effectful.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: haxl-effectful-version: 1.1.0+version: 1.1.1 category: Concurrency synopsis: Effectful bindings for Haxl description:@@ -67,7 +67,7 @@    build-depends:     base >=4.10 && <5,-    effectful >=2.6 && <2.7,+    effectful >=2.6 && <2.8,     hashable >=1.5 && <1.6,  library
src/Effectful/Haxl.hs view
@@ -256,13 +256,13 @@ haxl :: forall u w es a. (Monoid w) => GenHaxl u w es a -> Eff (Haxl u w ': es) a haxl g = do     env <- initEnv-    inject $ unsafeConcUnliftIO Persistent Unlimited $ Haxl.runHaxl env . unHaxl g+    inject $ unsafeSeqUnliftIO $ Haxl.runHaxl env . unHaxl g  -- | Like 'haxl', but also return everything written via the 'w' monoid during the run. haxlWithWrites :: forall u w es a. (Monoid w) => GenHaxl u w es a -> Eff (Haxl u w ': es) (a, w) haxlWithWrites g = do     env <- initEnv-    inject $ unsafeConcUnliftIO Persistent Unlimited $ Haxl.runHaxlWithWrites env . unHaxl g+    inject $ unsafeSeqUnliftIO $ Haxl.runHaxlWithWrites env . unHaxl g  -- | Like 'haxl', but also return the 'Stats' collected during the run. --@@ -271,7 +271,7 @@ haxlWithStats :: forall u w es a. (Monoid w) => GenHaxl u w es a -> Eff (Haxl u w ': es) (a, Stats) haxlWithStats g = do     env <- initEnv-    a <- inject $ unsafeConcUnliftIO Persistent Unlimited $ Haxl.runHaxl env . unHaxl g+    a <- inject $ unsafeSeqUnliftIO $ Haxl.runHaxl env . unHaxl g     stats <- unsafeEff_ . readIORef $ statsRef env     pure (a, stats) 
test/ExampleDataSource.hs view
@@ -28,7 +28,7 @@ import System.IO (hPutStr, stderr) import Prelude --- Here is an example minimal data source.  Our data source will have+-- Here is an example minimal data source. Our data source will have -- two requests: -- --   countAardvarks :: String -> Haxl Int@@ -48,13 +48,13 @@     ListWombats :: Id -> ExampleReq [Id]  -- The request type (ExampleReq) is parameterized by the result type of--- each request.  Each request might have a different result, so we use a+-- each request. Each request might have a different result, so we use a -- GADT - a data type in which each constructor may have different type -- parameters. Here CountAardvarks is a request that takes a String -- argument and its result is Int, whereas ListWombats takes an Id -- argument and returns a [Id]. --- The request type needs instances for 'Eq1' and 'Hashable1'.  These+-- The request type needs instances for 'Eq1' and 'Hashable1'. These -- are like 'Eq' and 'Hashable', but for types with one parameter -- where the parameter is irrelevant for hashing and equality. -- These two instances are used to support caching of requests.
test/Main.hs view
@@ -5,6 +5,8 @@  module Main where +import Control.Concurrent (forkIO, newEmptyMVar, putMVar, takeMVar)+import Control.Monad (replicateM) import Data.Functor ((<&>)) import Data.List qualified as List import Data.Maybe (mapMaybe)@@ -18,7 +20,7 @@ import Effectful.Haxl as Haxl import Effectful.Hspec import Effectful.Prim (Prim, runPrim)-import Effectful.Prim.IORef (modifyIORef', newIORef, readIORef)+import Effectful.Prim.IORef (atomicModifyIORef', modifyIORef', newIORef, readIORef) import ExampleDataSource (Id (..)) import ExampleDataSource qualified import GHC.Stack (HasCallStack)@@ -66,6 +68,9 @@                 asyncExceptionTest             describe "cache dump" do                 dumpCacheTest+            describe "liftEff" do+                liftEffTest+                concurrentLiftEffTest  -- * Helpers @@ -368,6 +373,38 @@     r `shouldSatisfy` \case         Left ThreadKilled -> True         _ -> False++-- * liftEff++liftEffTest :: forall es. (Haxl () () :> es, Hspec :> es, Prim :> es) => Expectation es+liftEffTest = it "runs an Eff action and sees its effect" do+    ref <- newIORef (0 :: Int)+    r <- inject . haxl @() @() $ do+        liftEff $ modifyIORef' ref (+ 1)+        liftEff $ readIORef ref+    r `shouldBe` 1+    final <- readIORef ref+    final `shouldBe` 1++concurrentLiftEffTest+    :: forall es+     . (Haxl () () :> es, Hspec :> es, Prim :> es, IOE :> es)+    => Expectation es+concurrentLiftEffTest = it "runs liftEff correctly across many concurrent haxl calls" do+    ref <- newIORef (0 :: Int)+    let go :: GenHaxl () () es Int+        go = do+            n <- liftEff $ atomicModifyIORef' ref (\n -> (n + 1, n + 1))+            _ <- ExampleDataSource.countAardvarks "a" -- also exercise a real fetch alongside liftEff+            pure n+    results <- concurrently 64 . inject $ haxl @() @() go+    List.sort results `shouldBe` [1 .. 64]++concurrently :: (IOE :> es) => Int -> Eff es a -> Eff es [a]+concurrently n act = withEffToIO (ConcUnlift Persistent Unlimited) \unlift -> do+    mvs <- replicateM n newEmptyMVar+    mapM_ (\mv -> forkIO (unlift act >>= putMVar mv)) mvs+    mapM takeMVar mvs  -- * Cache dump