packages feed

haxl-effectful 1.0.0 → 1.1.0

raw patch · 4 files changed

+74/−83 lines, 4 filesdep ~hspec-effectfulPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: hspec-effectful

API changes (from Hackage documentation)

- Effectful.Haxl: GenHaxl :: (Env u w -> Eff es (Result u w a)) -> GenHaxl u w (es :: [Effect]) a
- Effectful.Haxl: [unHaxl] :: GenHaxl u w (es :: [Effect]) a -> Env u w -> Eff es (Result u w a)
- Effectful.Haxl: newtype GenHaxl u w (es :: [Effect]) a
+ Effectful.Haxl: data GenHaxl u w (es :: [Effect]) a
+ Effectful.Haxl: liftEff :: forall (es :: [Effect]) a u w. Eff es a -> GenHaxl u w es a

Files

CHANGELOG.md view
@@ -5,6 +5,20 @@ 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.0] - 2026-08-04++### Added++- `liftEff`, a function to lift arbitrary action into `GenHaxl`.++### Removed++- `GenHaxl` constructor export.++### Changed++- Internal representation of `GenHaxl`, improving performance.+ ## [1.0.0] - 2026-07-15  ### Added
haxl-effectful.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: haxl-effectful-version: 1.0.0+version: 1.1.0 category: Concurrency synopsis: Effectful bindings for Haxl description:@@ -47,6 +47,7 @@     FlexibleInstances     GADTs     ImportQualifiedPost+    ImpredicativeTypes     LambdaCase     MultiParamTypeClasses     NamedFieldPuns@@ -91,5 +92,5 @@   build-depends:     filepath >=1.5 && <1.6,     haxl-effectful,-    hspec-effectful >=1.0 && <1.1,+    hspec-effectful >=1.0 && <1.2,     text >=2.1 && <2.2,
src/Effectful/Haxl.hs view
@@ -147,8 +147,9 @@     , initEnv     , env     , withEnv-    , GenHaxl (..)+    , GenHaxl     , liftGenHaxl+    , liftEff     , dataFetch     , uncachedRequest     , cacheRequest@@ -255,15 +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 \unlift ->-        Haxl.runHaxl env $ Haxl.GenHaxl (unlift . unHaxl g)+    inject $ unsafeConcUnliftIO Persistent Unlimited $ 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 \unlift ->-        Haxl.runHaxlWithWrites env $ Haxl.GenHaxl (unlift . unHaxl g)+    inject $ unsafeConcUnliftIO Persistent Unlimited $ Haxl.runHaxlWithWrites env . unHaxl g  -- | Like 'haxl', but also return the 'Stats' collected during the run. --@@ -272,8 +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 \unlift ->-        Haxl.runHaxl env $ Haxl.GenHaxl (unlift . unHaxl g)+    a <- inject $ unsafeConcUnliftIO Persistent Unlimited $ Haxl.runHaxl env . unHaxl g     stats <- unsafeEff_ . readIORef $ statsRef env     pure (a, stats) @@ -292,31 +290,22 @@ -- | The monad in which data-fetching computations are written. -- -- Note that 'GenHaxl' is not expressed in terms of 'Eff'.--- Its 'Applicative' instance batches independent fetches combined with @('<*>')@ into a single round trip.-newtype GenHaxl u w es a = GenHaxl {unHaxl :: Env u w -> Eff es (Haxl.Result u w a)}+-- Its 'Applicative' instance batches independent fetches combined with @('<*>')@+-- into a single round trip.+newtype GenHaxl u w es a = GenHaxl+    { unHaxl :: (forall r. Eff es r -> IO r) -> Haxl.GenHaxl u w a+    }  instance Functor (GenHaxl u w es) where-    fmap f (GenHaxl m) = GenHaxl $ (fmap . fmap . fmap) f m+    fmap f (GenHaxl m) = GenHaxl $ fmap f . m  instance Applicative (GenHaxl u w es) where-    pure = GenHaxl . const . pure . Done-    f <*> a = GenHaxl $ \env -> do-        unsafeConcUnliftIO Ephemeral Unlimited $ \unlift ->-            Haxl.unHaxl-                ( Haxl.GenHaxl (unlift . unHaxl f)-                    <*> Haxl.GenHaxl (unlift . unHaxl a)-                )-                env+    pure a = GenHaxl \_unlift -> pure a+    GenHaxl f <*> GenHaxl a = GenHaxl \unlift -> f unlift <*> a unlift  instance Monad (GenHaxl u w es) where     return = pure-    m >>= k = GenHaxl $ \env -> do-        unsafeConcUnliftIO Ephemeral Unlimited $ \unlift ->-            Haxl.unHaxl-                ( Haxl.GenHaxl (unlift . unHaxl m)-                    >>= \a -> Haxl.GenHaxl (unlift . unHaxl (k a))-                )-                env+    GenHaxl m >>= k = GenHaxl \unlift -> m unlift >>= \a -> unHaxl (k a) unlift  instance MonadFail (GenHaxl u w es) where     fail = liftGenHaxl . fail@@ -357,85 +346,73 @@ -- Warning: 'Haxl.GenHaxl' exposes functions such as 'Haxl.unsafeLiftIO' -- that allow arbitrary 'IO' to be injected without it being reflected in @es@. liftGenHaxl :: Haxl.GenHaxl u w a -> GenHaxl u w es a-liftGenHaxl = GenHaxl . (unsafeEff_ .) . Haxl.unHaxl--unliftGenHaxl :: GenHaxl u w es a -> Eff es (Haxl.GenHaxl u w a)-unliftGenHaxl g =-    unsafeConcUnliftIO Ephemeral Unlimited $ pure . Haxl.GenHaxl . (. unHaxl g)--unliftGenHaxl1 :: (a -> GenHaxl u w es b) -> Eff es (a -> Haxl.GenHaxl u w b)-unliftGenHaxl1 f =-    unsafeConcUnliftIO Ephemeral Unlimited \unlift -> pure \a ->-        Haxl.GenHaxl $ unlift . unHaxl (f a)+liftGenHaxl h = GenHaxl $ const h -unliftGenHaxl2 :: (a -> b -> GenHaxl u w es c) -> Eff es (a -> b -> Haxl.GenHaxl u w c)-unliftGenHaxl2 f =-    unsafeConcUnliftIO Ephemeral Unlimited \unlift -> pure \a b ->-        Haxl.GenHaxl $ unlift . unHaxl (f a b)+-- | Run an @'Eff' es@ action as part of a 'GenHaxl' computation.+--+-- The action runs on the effect environment captured by 'haxl' (or whichever+-- @haxlWith*@ function started the run), so effect state is shared with the+-- surrounding @'Eff' es@ computation and between all lifted actions.+--+-- Note that the action is opaque to Haxl: it is not batched, cached or+-- deduplicated, and it runs as soon as the computation reaches it rather than+-- when a round of fetches is dispatched. Use 'cacheResult' to have its result+-- cached against a request.+liftEff :: Eff es a -> GenHaxl u w es a+liftEff eff = GenHaxl \unlift -> Haxl.unsafeLiftIO $ unlift eff  mapGenHaxl :: (Haxl.GenHaxl u w a -> Haxl.GenHaxl u w b) -> GenHaxl u w es a -> GenHaxl u w es b-mapGenHaxl f g = GenHaxl \env -> do-    g <- unliftGenHaxl g-    unsafeEff_ $ Haxl.unHaxl (f g) env+mapGenHaxl f g = GenHaxl $ f . unHaxl g  mapGenHaxl1     :: ((a -> Haxl.GenHaxl u w b) -> Haxl.GenHaxl u w c)     -> (a -> GenHaxl u w es b)     -> GenHaxl u w es c-mapGenHaxl1 f g = GenHaxl \env -> do-    g <- unliftGenHaxl1 g-    unsafeEff_ $ Haxl.unHaxl (f g) env+mapGenHaxl1 f g = GenHaxl \unlift -> f \a -> unHaxl (g a) unlift  mapGenHaxl2     :: ((a -> b -> Haxl.GenHaxl u w c) -> Haxl.GenHaxl u w d)     -> (a -> b -> GenHaxl u w es c)     -> GenHaxl u w es d-mapGenHaxl2 f g = GenHaxl \env -> do-    g <- unliftGenHaxl2 g-    unsafeEff_ $ Haxl.unHaxl (f g) env+mapGenHaxl2 f g = GenHaxl \unlift -> f \a b -> unHaxl (g a b) unlift  mapGenHaxlCatch     :: (Haxl.GenHaxl u w a -> (e -> Haxl.GenHaxl u w a) -> Haxl.GenHaxl u w a)     -> GenHaxl u w es a     -> (e -> GenHaxl u w es a)     -> GenHaxl u w es a-mapGenHaxlCatch f g h = GenHaxl \env -> do-    g <- unliftGenHaxl g-    h <- unliftGenHaxl1 h-    unsafeEff_ $ Haxl.unHaxl (f g h) env+mapGenHaxlCatch f g h = GenHaxl \unlift ->+    f (unHaxl g unlift) \e -> unHaxl (h e) unlift --- | Lifted 'Haxl.env'+-- | Lifted 'Haxl.env'. env :: (Env u w -> a) -> GenHaxl u w es a env = liftGenHaxl . Haxl.env --- | Lifted 'Haxl.withEnv'+-- | Lifted 'Haxl.withEnv'. withEnv :: Env u w -> GenHaxl u w es a -> GenHaxl u w es a withEnv = mapGenHaxl . Haxl.withEnv --- | Lifted 'Haxl.dataFetch'+-- | Lifted 'Haxl.dataFetch'. dataFetch :: (DataSource u r, Request r a) => r a -> GenHaxl u w es a dataFetch = liftGenHaxl . Haxl.dataFetch --- | Lifted 'Haxl.uncachedRequest'+-- | Lifted 'Haxl.uncachedRequest'. uncachedRequest :: (DataSource u r, Request r a) => r a -> GenHaxl u w es a uncachedRequest = liftGenHaxl . Haxl.uncachedRequest --- | Lifted 'Haxl.cacheRequest'+-- | Lifted 'Haxl.cacheRequest'. cacheRequest :: (Request req a) => req a -> Either SomeException a -> GenHaxl u w es () cacheRequest = (liftGenHaxl .) . Haxl.cacheRequest --- | Lifted 'Haxl.dupableCacheRequest'+-- | Lifted 'Haxl.dupableCacheRequest'. dupableCacheRequest :: (Request req a) => req a -> Either SomeException a -> GenHaxl u w es () dupableCacheRequest = (liftGenHaxl .) . Haxl.dupableCacheRequest --- | Lifted 'Haxl.cacheResult'+-- | Lifted 'Haxl.cacheResult'. cacheResult :: (Request r a) => r a -> Eff es a -> GenHaxl u w es a-cacheResult r eff =-    GenHaxl \env ->-        unsafeConcUnliftIO Ephemeral Unlimited \unlift ->-            Haxl.unHaxl (Haxl.cacheResult r (unlift eff)) env+cacheResult r eff = GenHaxl \unlift -> Haxl.cacheResult r (unlift eff) --- | Lifted 'Haxl.cacheResultWithShow'+-- | Lifted 'Haxl.cacheResultWithShow'. cacheResultWithShow     :: (Hashable (r a), Typeable (r a))     => ShowReq r a@@ -443,11 +420,9 @@     -> Eff es a     -> GenHaxl u w es a cacheResultWithShow showReq req eff =-    GenHaxl \env ->-        unsafeConcUnliftIO Ephemeral Unlimited \unlift ->-            Haxl.unHaxl (Haxl.cacheResultWithShow showReq req (unlift eff)) env+    GenHaxl \unlift -> Haxl.cacheResultWithShow showReq req (unlift eff) --- | Lifted 'Haxl.cachedComputation'+-- | Lifted 'Haxl.cachedComputation'. cachedComputation     :: (Hashable (req a), Typeable (req a), Monoid w)     => req a@@ -455,7 +430,7 @@     -> GenHaxl u w es a cachedComputation = mapGenHaxl . Haxl.cachedComputation --- | Lifted 'Haxl.preCacheComputation'+-- | Lifted 'Haxl.preCacheComputation'. preCacheComputation     :: (Hashable (req a), Typeable (req a), Monoid w)     => req a@@ -463,25 +438,25 @@     -> GenHaxl u w es a preCacheComputation = mapGenHaxl . Haxl.preCacheComputation --- | Lifted 'Haxl.memoize'+-- | Lifted 'Haxl.memoize'. memoize :: (Monoid w) => GenHaxl u w es a -> GenHaxl u w es (GenHaxl u w es a) memoize = fmap liftGenHaxl . mapGenHaxl Haxl.memoize --- | Lifted 'Haxl.memoize1'+-- | Lifted 'Haxl.memoize1'. memoize1     :: (Hashable a, Monoid w)     => (a -> GenHaxl u w es b)     -> GenHaxl u w es (a -> GenHaxl u w es b) memoize1 = fmap (liftGenHaxl .) . mapGenHaxl1 Haxl.memoize1 --- | Lifted 'Haxl.memoize2'+-- | Lifted 'Haxl.memoize2'. memoize2     :: (Hashable a, Hashable b, Monoid w)     => (a -> b -> GenHaxl u w es c)     -> GenHaxl u w es (a -> b -> GenHaxl u w es c) memoize2 = fmap ((liftGenHaxl .) .) . mapGenHaxl2 Haxl.memoize2 --- | Lifted 'Haxl.memo'+-- | Lifted 'Haxl.memo'. memo     :: ( Typeable a        , Typeable k@@ -493,7 +468,7 @@     -> GenHaxl u w es a memo = mapGenHaxl . Haxl.memo --- | Lifted 'Haxl.memoFingerprint'+-- | Lifted 'Haxl.memoFingerprint'. memoFingerprint     :: (Typeable a, Monoid w)     => MemoFingerprintKey a@@ -501,22 +476,22 @@     -> GenHaxl u w es a memoFingerprint = mapGenHaxl . Haxl.memoFingerprint --- | Lifted 'Haxl.rethrowAsyncExceptions'+-- | Lifted 'Haxl.rethrowAsyncExceptions'. rethrowAsyncExceptions :: SomeException -> Eff es () rethrowAsyncExceptions = unsafeEff_ . Haxl.rethrowAsyncExceptions  tryWithRethrow :: Eff es a -> Eff es (Either SomeException a) tryWithRethrow eff = (Right <$> eff) `Effectful.catch` \e -> rethrowAsyncExceptions e >> pure (Left e) --- | Lifted 'Haxl.throw'+-- | Lifted 'Haxl.throw'. throw :: (Exception e) => e -> GenHaxl u w es a throw = liftGenHaxl . Haxl.throw --- | Lifted 'Haxl.catch'+-- | Lifted 'Haxl.catch'. catch :: (Exception e) => GenHaxl u w es a -> (e -> GenHaxl u w es a) -> GenHaxl u w es a catch = mapGenHaxlCatch Haxl.catch --- | Lifted 'Haxl.catchIf'+-- | Lifted 'Haxl.catchIf'. catchIf     :: (Exception e)     => (e -> Bool)@@ -525,14 +500,14 @@     -> GenHaxl u w es a catchIf = mapGenHaxlCatch . Haxl.catchIf --- | Lifted 'Haxl.try'+-- | Lifted 'Haxl.try'. try :: (Exception e) => GenHaxl u w es a -> GenHaxl u w es (Either e a) try = mapGenHaxl Haxl.try --- | Lifted 'Haxl.dumpCacheAsHaskell'+-- | Lifted 'Haxl.dumpCacheAsHaskell'. dumpCacheAsHaskell :: GenHaxl u w es String dumpCacheAsHaskell = dumpCacheAsHaskellFn "loadCache" "GenHaxl u w es ()" "cacheRequest" --- | Lifted 'Haxl.dumpCacheAsHaskellFn'+-- | Lifted 'Haxl.dumpCacheAsHaskellFn'. dumpCacheAsHaskellFn :: String -> String -> String -> GenHaxl u w es String dumpCacheAsHaskellFn = ((liftGenHaxl .) .) . Haxl.dumpCacheAsHaskellFn
test/Main.hs view
@@ -21,6 +21,7 @@ import Effectful.Prim.IORef (modifyIORef', newIORef, readIORef) import ExampleDataSource (Id (..)) import ExampleDataSource qualified+import GHC.Stack (HasCallStack) import LoadCache (loadCache) import System.FilePath (dropFileName, (</>)) import Prelude hiding (readFile)