diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,94 @@
+# effectful-2.6.1.0 (2025-08-30)
+* Add `MonadError`, `MonadReader`, `MonadState` and `MonadWriter` instances for
+  `Eff` for compatibility with existing code.
+* Re-export `writeTMVar` from `stm-2.5.1.0` in `Effectful.Concurrent.STM`.
+* Add `cancelMany` to `Effectful.Concurrent.Async`.
+
+# effectful-core-2.6.0.0 (2025-06-13)
+* Adjust `generalBracket` with `base >= 4.21` to make use of the new exception
+  annotation mechanism.
+* Add `withException` to `Effectful.Exception`.
+* Deprecate `Effectful.Reader.Dynamic.withReader` as it doesn't work correctly
+  for all potential interpreters.
+* Re-export `ThreadId` from `Effectful.Concurrent` for convenience.
+* **Breaking changes**:
+  - Change the order of type parameters in `raise` for better usability.
+  - `Effectful.Error.Static.ErrorWrapper` is no longer caught by `catchSync`.
+  - Remove deprecated function `Effectful.withConcEffToIO`.
+
+# effectful-2.5.1.0 (2024-11-27)
+* Add `passthrough` to `Effectful.Dispatch.Dynamic` for passing operations to
+  the upstream handler within `interpose` and `impose` without having to fully
+  pattern match on them.
+* **Bugfixes**:
+  - Fix a potential space leak related to `HasCallStack` quirks (see
+    https://gitlab.haskell.org/ghc/ghc/-/issues/25520 for more information).
+
+# effectful-2.5.0.0 (2024-10-23)
+* Add `plusEff` (specialized version of `<|>`) to `Effectful.NonDet` and make
+  `emptyEff` and `sumEff` generate better call stacks.
+* Explicitly define `setByteArray#` and `setOffAddr#` in the `Prim` instance of
+  `Ref` for `primitive` < 0.9.0.0.
+* **Bugfixes**:
+  - `OnEmptyRollback` strategy of the `NonDet` effect is no longer broken.
+* **Breaking changes**:
+  - Remove `restoreEnv` function from `Effectful.Dispatch.Static.Primitive`
+    since it was broken.
+  - Base `Effectful.Exception` on `Control.Exception` instead of the
+    `safe-exceptions` library for consistency with provided `MonadThrow` and
+    `MonadCatch` instances.
+
+# effectful-2.4.0.0 (2024-10-08)
+* Add utility functions for handling effects that take the effect handler as the
+  last parameter to `Effectful.Dispatch.Dynamic`.
+* Add utility functions for handling first order effects to
+  `Effectful.Dispatch.Dynamic`.
+* Improve `Effectful.Labeled`, add `Effectful.Labeled.Error`,
+  `Effectful.Labeled.Reader`, `Effectful.Labeled.State` and
+  `Effectful.Labeled.Writer`.
+* Add `throwErrorWith` and `throwError_` to `Effectful.Error.Static` and
+  `Effectful.Error.Dynamic`.
+* Add `HasCallStack` constraints where appropriate for better debugging
+  experience.
+* Add a `SeqForkUnlift` strategy to support running unlifting functions outside
+  of the scope of effects they capture.
+* Add `Effectful.Exception` with appropriate re-exports from the
+  `safe-exceptions` library.
+* Add `Effectful.Concurrent.Chan.Strict`.
+* Add `Effectful.Prim.IORef` and `Effectful.Prim.IORef.Strict`.
+* **Bugfixes**:
+  - Ensure that a `LocalEnv` is only used in a thread it belongs to.
+  - Properly roll back changes made to the environment when `OnEmptyRollback`
+    policy for the `NonDet` effect is selected.
+  - Fix a bug in `stateM` and `modifyM` of thread local `State` effect that
+    might've caused dropped state updates
+    ([#237](https://github.com/haskell-effectful/effectful/issues/237)).
+* **Breaking changes**:
+  - `localSeqLend`, `localLend`, `localSeqBorrow` and `localBorrow` now take a
+    list of effects instead of a single one.
+  - `Effectful.Error.Static.throwError` now requires the error type to have a
+    `Show` constraint. If this is not the case for some of your error types, use
+    `throwError_` for them.
+  - `ThrowError` operation from the dynamic version of the `Error` effect was
+    replaced with `ThrowErrorWith`.
+  - `stateEnv` and `modifyEnv` now take pure modification functions. If you rely
+    on their old forms, switch to a combination of `getEnv` and `putEnv`.
+  - `runStateMVar`, `evalStateMVar` and `execStateMVar` now take a strict
+    `MVar'` from the `strict-mutable-base` package.
+  - `Effectful.Concurrent.MVar.Strict` is now a lifted version of
+    `Control.Concurrent.MVar.Strict` from `strict-mutable-base`. The original
+    module was renamed to `Effectful.Concurrent.MVar.Strict.Compat` and
+    deprecated.
+
+# effectful-2.3.1.0 (2024-06-07)
+* Drop support for GHC 8.8.
+* Remove inaccurate information from the `Show` instance of `ErrorWrapper`.
+* Add `Effectful.Provider.List`, generalization of `Effectful.Provider`.
+* Respect `withFrozenCallStack` used by callers of `send`.
+* Support exchange of effects between the environment of the handler and the
+  local one via `localSeqLend`, `localLend`, `localSeqBorrow` and `localBorrow`
+  from `Effectful.Dispatch.Dynamic`.
+
 # effectful-2.3.0.0 (2023-09-13)
 * Deprecate `withConcEffToIO`.
 * Make `withEffToIO` take an explicit unlifting strategy for the sake of
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,13 +1,12 @@
 # effectful
 
-[![Build Status](https://github.com/haskell-effectful/effectful/workflows/Haskell-CI/badge.svg?branch=master)](https://github.com/haskell-effectful/effectful/actions?query=branch%3Amaster)
+[![CI](https://github.com/haskell-effectful/effectful/actions/workflows/haskell-ci.yml/badge.svg?branch=master)](https://github.com/haskell-effectful/effectful/actions/workflows/haskell-ci.yml)
 [![Hackage](https://img.shields.io/hackage/v/effectful.svg)](https://hackage.haskell.org/package/effectful)
-[![Dependencies](https://img.shields.io/hackage-deps/v/effectful.svg)](https://packdeps.haskellers.com/feed?needle=andrzej@rybczak.net)
 [![Stackage LTS](https://www.stackage.org/package/effectful/badge/lts)](https://www.stackage.org/lts/package/effectful)
 [![Stackage Nightly](https://www.stackage.org/package/effectful/badge/nightly)](https://www.stackage.org/nightly/package/effectful)
 
 
-<img src="https://user-images.githubusercontent.com/387658/127747903-f728437f-2ee4-47b8-9f0c-5102fd44c8e4.png" width="128">
+<img src="https://raw.githubusercontent.com/haskell-effectful/effectful/master/logo.svg" width="150">
 
 An easy to use, fast extensible effects library with seamless integration with
 the existing Haskell ecosystem.
@@ -161,7 +160,8 @@
 For the examples see the *Introduction* sections of
 [`Effectful.Dispatch.Dynamic`](https://hackage.haskell.org/package/effectful-core/docs/Effectful-Dispatch-Dynamic.html)
 and
-[`Effectful.Dispatch.Static`](https://hackage.haskell.org/package/effectful-core/docs/Effectful-Dispatch-Static.html).
+[`Effectful.Dispatch.Static`](https://hackage.haskell.org/package/effectful-core/docs/Effectful-Dispatch-Static.html)
+(when in doubt, start with dynamic dispatch).
 
 ## Acknowledgements
 
diff --git a/bench/Concurrency.hs b/bench/Concurrency.hs
--- a/bench/Concurrency.hs
+++ b/bench/Concurrency.hs
@@ -11,9 +11,9 @@
 
 import Control.Concurrent.Async
 import Control.Monad
-import qualified Effectful.Concurrent.Async as A
 
 import Effectful
+import Effectful.Concurrent.Async qualified as A
 import Effectful.Dispatch.Dynamic
 import Utils
 
diff --git a/bench/Countdown.hs b/bench/Countdown.hs
--- a/bench/Countdown.hs
+++ b/bench/Countdown.hs
@@ -6,51 +6,51 @@
 
 -- eff
 #ifdef VERSION_eff
-import qualified Control.Effect as L
+import Control.Effect qualified as L
 #endif
 
 -- cleff
 #ifdef VERSION_cleff
-import qualified Cleff as C
-import qualified Cleff.Reader as C
-import qualified Cleff.State as C
+import Cleff qualified as C
+import Cleff.Reader qualified as C
+import Cleff.State qualified as C
 #endif
 
 -- effectful
-import qualified Effectful as E
-import qualified Effectful.Dispatch.Dynamic as E
-import qualified Effectful.Labeled as E
-import qualified Effectful.Reader.Static as E
-import qualified Effectful.State.Dynamic as ED
-import qualified Effectful.State.Static.Local as EL
-import qualified Effectful.State.Static.Shared as ES
+import Effectful qualified as E
+import Effectful.Dispatch.Dynamic qualified as E
+import Effectful.Labeled qualified as E
+import Effectful.Reader.Static qualified as E
+import Effectful.State.Dynamic qualified as ED
+import Effectful.State.Static.Local qualified as EL
+import Effectful.State.Static.Shared qualified as ES
 
 -- freer-simple
 #ifdef VERSION_freer_simple
-import qualified Control.Monad.Freer as FS
-import qualified Control.Monad.Freer.Reader as FS
-import qualified Control.Monad.Freer.State as FS
+import Control.Monad.Freer qualified as FS
+import Control.Monad.Freer.Reader qualified as FS
+import Control.Monad.Freer.State qualified as FS
 #endif
 
 -- fused-effects
 #ifdef VERSION_fused_effects
-import qualified Control.Algebra as FE
-import qualified Control.Carrier.Reader as FE
-import qualified Control.Carrier.State.Strict as FE
+import Control.Algebra qualified as FE
+import Control.Carrier.Reader qualified as FE
+import Control.Carrier.State.Strict qualified as FE
 #endif
 
 -- mtl
 #ifdef VERSION_mtl
-import qualified Control.Monad.Reader as M
-import qualified Control.Monad.State as M
+import Control.Monad.Reader qualified as M
+import Control.Monad.State qualified as M
 import Data.Functor.Identity
 #endif
 
 -- polysemy
 #ifdef VERSION_polysemy
-import qualified Polysemy as P
-import qualified Polysemy.Reader as P
-import qualified Polysemy.State as P
+import Polysemy qualified as P
+import Polysemy.Reader qualified as P
+import Polysemy.State qualified as P
 #endif
 
 ----------------------------------------
@@ -282,6 +282,47 @@
   . runDoubleStateShared n
   . runR . runR . runR . runR . runR
   $ programEffectfulDynamic
+  where
+    runR = E.runReader ()
+
+----------------------------------------
+-- effectful (labeled-dynamic-send)
+
+programEffectfulLabeledDynamicSend
+  :: E.Labeled "s" (ED.State Integer) E.:> es
+  => E.Eff es Integer
+programEffectfulLabeledDynamicSend = do
+  n <- E.send . E.Labeled @"s" $ ED.Get @Integer
+  if n <= 0
+    then pure n
+    else do
+      E.send . E.Labeled @"s" $ ED.Put (n - 1)
+      programEffectfulLabeledDynamicSend
+{-# NOINLINE programEffectfulLabeledDynamicSend #-}
+
+countdownEffectfulLabeledDynSendLocal :: Integer -> (Integer, Integer)
+countdownEffectfulLabeledDynSendLocal n =
+  E.runPureEff . E.runLabeled @"s" (ED.runStateLocal n) $ programEffectfulLabeledDynamicSend
+
+countdownEffectfulLabeledDynSendShared :: Integer -> (Integer, Integer)
+countdownEffectfulLabeledDynSendShared n =
+  E.runPureEff . E.runLabeled @"s" (ED.runStateShared n) $ programEffectfulLabeledDynamicSend
+
+countdownEffectfulLabeledDynSendLocalDeep :: Integer -> (Integer, Integer)
+countdownEffectfulLabeledDynSendLocalDeep n = E.runPureEff
+  . runR . runR . runR . runR . runR
+  . E.runLabeled @"s" (ED.runStateLocal n)
+  . runR . runR . runR . runR . runR
+  $ programEffectfulLabeledDynamicSend
+  where
+    runR = E.runReader ()
+
+countdownEffectfulLabeledDynSendSharedDeep :: Integer -> (Integer, Integer)
+countdownEffectfulLabeledDynSendSharedDeep n = E.runPureEff
+  . runR . runR . runR . runR . runR
+  . E.runLabeled @"s" (ED.runStateShared n)
+  . runR . runR . runR . runR . runR
+  $ programEffectfulLabeledDynamicSend
   where
     runR = E.runReader ()
 
diff --git a/bench/FileSizes.hs b/bench/FileSizes.hs
--- a/bench/FileSizes.hs
+++ b/bench/FileSizes.hs
@@ -1,59 +1,58 @@
 {-# LANGUAGE BlockArguments #-}
 {-# LANGUAGE CPP #-}
-{-# LANGUAGE UndecidableInstances #-}
 module FileSizes where
 
 import Control.Exception
 import Control.Monad.IO.Class
 import Data.IORef
 import Data.Text (Text)
+import Data.Text qualified as T
 import System.Posix
-import qualified Data.Text as T
 
 -- effectful
-import qualified Effectful as E
-import qualified Effectful.Dispatch.Dynamic as E
-import qualified Effectful.Reader.Static as E
-import qualified Effectful.State.Static.Local as E
+import Effectful qualified as E
+import Effectful.Dispatch.Dynamic qualified as E
+import Effectful.Reader.Static qualified as E
+import Effectful.State.Static.Local qualified as E
 
 -- eff
 #ifdef VERSION_eff
-import qualified Control.Effect as L
+import Control.Effect qualified as L
 #endif
 
 -- cleff
 #ifdef VERSION_cleff
-import qualified Cleff as C
-import qualified Cleff.Reader as C
-import qualified Cleff.State as C
+import Cleff qualified as C
+import Cleff.Reader qualified as C
+import Cleff.State qualified as C
 #endif
 
 -- freer-simple
 #ifdef VERSION_freer_simple
-import qualified Control.Monad.Freer as FS
-import qualified Control.Monad.Freer.Reader as FS
-import qualified Control.Monad.Freer.State as FS
+import Control.Monad.Freer qualified as FS
+import Control.Monad.Freer.Reader qualified as FS
+import Control.Monad.Freer.State qualified as FS
 #endif
 
 -- fused-effects
 #ifdef VERSION_fused_effects
-import qualified Control.Algebra as FE
-import qualified Control.Effect.Sum as FE
-import qualified Control.Carrier.Reader as FE
-import qualified Control.Carrier.State.Strict as FE
+import Control.Algebra qualified as FE
+import Control.Effect.Sum qualified as FE
+import Control.Carrier.Reader qualified as FE
+import Control.Carrier.State.Strict qualified as FE
 #endif
 
 -- mtl
 #ifdef VERSION_mtl
-import qualified Control.Monad.State as M
-import qualified Control.Monad.Reader as M
+import Control.Monad.State qualified as M
+import Control.Monad.Reader qualified as M
 #endif
 
 -- polysemy
 #ifdef VERSION_polysemy
-import qualified Polysemy as P
-import qualified Polysemy.Reader as P
-import qualified Polysemy.State as P
+import Polysemy qualified as P
+import Polysemy.Reader qualified as P
+import Polysemy.State qualified as P
 #endif
 
 tryGetFileSize :: FilePath -> IO (Maybe Int)
@@ -102,7 +101,7 @@
 effectful_tryFileSize = E.send . Effectful_tryFileSize
 
 effectful_runFile :: E.IOE E.:> es => E.Eff (Effectful_File : es) a -> E.Eff es a
-effectful_runFile = E.interpret \_ -> \case
+effectful_runFile = E.interpret_ \case
   Effectful_tryFileSize path -> liftIO $ tryGetFileSize path
 
 data Effectful_Logging :: E.Effect where
@@ -116,7 +115,7 @@
 effectful_runLogging
   :: E.Eff (Effectful_Logging : es) a
   -> E.Eff es (a, [Text])
-effectful_runLogging = E.reinterpret (E.runState []) \_ -> \case
+effectful_runLogging = E.reinterpret_ (E.runState []) \case
   Effectful_logMsg msg -> E.modify (msg :)
 
 ----------
@@ -363,7 +362,7 @@
 fe_tryFileSize = FE.send . FE_tryFileSize
 
 newtype FE_FileC m a = FE_FileC { fe_runFileC :: m a }
-  deriving (Applicative, Functor, Monad, MonadIO)
+  deriving newtype (Applicative, Functor, Monad, MonadIO)
 
 instance
   ( MonadIO m
@@ -380,7 +379,7 @@
 fe_logMsg = FE.send . FE_logMsg . T.pack
 
 newtype FE_LoggingC m a = FE_LoggingC { fe_runLoggingC :: FE.StateC [Text] m a }
-  deriving (Applicative, Functor, Monad)
+  deriving newtype (Applicative, Functor, Monad)
 
 instance
   ( FE.Algebra sig m
@@ -440,7 +439,7 @@
   mtl_tryFileSize :: FilePath -> m (Maybe Int)
 
 newtype FileT m a = FileT { runFileT :: m a }
-  deriving (Functor, Applicative, Monad, MonadIO)
+  deriving newtype (Functor, Applicative, Monad, MonadIO)
 
 instance M.MonadTrans FileT where
   lift = FileT
@@ -459,7 +458,7 @@
   mtl_logMsg :: String -> m ()
 
 newtype LoggingT m a = LoggingT (M.StateT [Text] m a)
-  deriving (Functor, Applicative, Monad, MonadIO, M.MonadTrans)
+  deriving newtype (Functor, Applicative, Monad, MonadIO, M.MonadTrans)
 
 instance {-# OVERLAPPABLE #-}
   ( MonadLog m
diff --git a/bench/Main.hs b/bench/Main.hs
--- a/bench/Main.hs
+++ b/bench/Main.hs
@@ -64,6 +64,10 @@
     [ bench "shallow" $ nf countdownEffectfulDynLocal n
     , bench "deep"    $ nf countdownEffectfulDynLocalDeep n
     ]
+  , bgroup "effectful (local/dynamic/labeled/send)"
+    [ bench "shallow" $ nf countdownEffectfulLabeledDynSendLocal n
+    , bench "deep"    $ nf countdownEffectfulLabeledDynSendLocalDeep n
+    ]
   , bgroup "effectful (shared/static)"
     [ bench "shallow" $ nf countdownEffectfulShared n
     , bench "deep"    $ nf countdownEffectfulSharedDeep n
@@ -71,6 +75,10 @@
   , bgroup "effectful (shared/dynamic)"
     [ bench "shallow" $ nf countdownEffectfulDynShared n
     , bench "deep"    $ nf countdownEffectfulDynSharedDeep n
+    ]
+  , bgroup "effectful (shared/dynamic/labeled/send)"
+    [ bench "shallow" $ nf countdownEffectfulLabeledDynSendShared n
+    , bench "deep"    $ nf countdownEffectfulLabeledDynSendSharedDeep n
     ]
 #ifdef VERSION_cleff
   , bgroup "cleff (local)"
diff --git a/bench/Unlift.hs b/bench/Unlift.hs
--- a/bench/Unlift.hs
+++ b/bench/Unlift.hs
@@ -9,12 +9,12 @@
 import Test.Tasty.Bench
 #endif
 
-import qualified UnliftIO as U
+import UnliftIO qualified as U
 
 import Effectful
+import Effectful.Concurrent.Async qualified as A
 import Effectful.State.Dynamic
 import Utils
-import qualified Effectful.Concurrent.Async as A
 
 unliftBenchmark :: Benchmark
 unliftBenchmark = bgroup "unlifting"
diff --git a/effectful.cabal b/effectful.cabal
--- a/effectful.cabal
+++ b/effectful.cabal
@@ -1,7 +1,7 @@
-cabal-version:      2.4
+cabal-version:      3.0
 build-type:         Simple
 name:               effectful
-version:            2.3.0.0
+version:            2.6.1.0
 license:            BSD-3-Clause
 license-file:       LICENSE
 category:           Control
@@ -22,8 +22,7 @@
   CHANGELOG.md
   README.md
 
-tested-with: GHC ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.7 || ==9.6.2
-              || ==9.8.1
+tested-with: GHC == { 8.10.7, 9.0.2, 9.2.8, 9.4.8, 9.6.7, 9.8.4, 9.10.2, 9.12.2, 9.14.1 }
 
 bug-reports:   https://github.com/haskell-effectful/effectful/issues
 source-repository head
@@ -35,7 +34,11 @@
     default: False
 
 common language
-    ghc-options:        -Wall -Wcompat -Wno-unticked-promoted-constructors
+    ghc-options:        -Wall
+                        -Wcompat
+                        -Wno-unticked-promoted-constructors
+                        -Wmissing-deriving-strategies
+                        -Werror=prepositive-qualified-module
 
     default-language:   Haskell2010
 
@@ -44,13 +47,16 @@
                         DataKinds
                         DeriveFunctor
                         DeriveGeneric
+                        DerivingStrategies
                         FlexibleContexts
                         FlexibleInstances
                         GADTs
                         GeneralizedNewtypeDeriving
+                        ImportQualifiedPost
                         LambdaCase
                         MultiParamTypeClasses
                         NoStarIsType
+                        PolyKinds
                         RankNTypes
                         RecordWildCards
                         RoleAnnotations
@@ -60,19 +66,20 @@
                         TypeApplications
                         TypeFamilies
                         TypeOperators
+                        UndecidableInstances
 
 library
     import:         language
 
-    build-depends:    base                >= 4.13      && < 5
-                    , async               >= 2.2.2
+    build-depends:    base                >= 4.14      && < 5
+                    , async               >= 2.2.5
                     , bytestring          >= 0.10
                     , directory           >= 1.3.2
-                    , effectful-core      >= 2.3.0.0   && < 2.3.1.0
+                    , effectful-core      >= 2.6.1.0   && < 2.6.2.0
                     , process             >= 1.6.9
-
+                    , strict-mutable-base >= 1.1.0.0
                     , time                >= 1.9.2
-                    , stm                 >= 2.5.0.0
+                    , stm                 >= 2.5.1.0
                     , unliftio            >= 0.2.20
 
     hs-source-dirs:  src
@@ -80,8 +87,10 @@
     exposed-modules: Effectful.Concurrent
                      Effectful.Concurrent.Async
                      Effectful.Concurrent.Chan
+                     Effectful.Concurrent.Chan.Strict
                      Effectful.Concurrent.MVar
                      Effectful.Concurrent.MVar.Strict
+                     Effectful.Concurrent.MVar.Strict.Compat
                      Effectful.Concurrent.STM
                      Effectful.Concurrent.QSem
                      Effectful.Concurrent.QSemN
@@ -94,6 +103,8 @@
                      Effectful.FileSystem.IO.ByteString.Builder
                      Effectful.FileSystem.IO.ByteString.Lazy
                      Effectful.FileSystem.IO.File
+                     Effectful.Prim.IORef
+                     Effectful.Prim.IORef.Strict
                      Effectful.Process
                      Effectful.Temporary
                      Effectful.Timeout
@@ -107,11 +118,17 @@
                          , Effectful.Dispatch.Static
                          , Effectful.Error.Static
                          , Effectful.Error.Dynamic
+                         , Effectful.Exception
                          , Effectful.Fail
                          , Effectful.Labeled
+                         , Effectful.Labeled.Error
+                         , Effectful.Labeled.Reader
+                         , Effectful.Labeled.State
+                         , Effectful.Labeled.Writer
                          , Effectful.NonDet
                          , Effectful.Prim
                          , Effectful.Provider
+                         , Effectful.Provider.List
                          , Effectful.Reader.Dynamic
                          , Effectful.Reader.Static
                          , Effectful.State.Dynamic
@@ -133,6 +150,8 @@
                     , exceptions
                     , lifted-base
                     , primitive
+                    , safe-exceptions
+                    , strict-mutable-base
                     , tasty
                     , tasty-hunit
                     , unliftio
@@ -147,6 +166,7 @@
                     EnvTests
                     EnvironmentTests
                     ErrorTests
+                    LabeledTests
                     NonDetTests
                     PrimTests
                     ReaderTests
@@ -169,11 +189,11 @@
        if impl(ghc < 9.9)
           build-depends: freer-simple >= 1.2.1.2
 
-       if impl(ghc < 9.9)
-          build-depends: fused-effects >= 1.1.2.2
+       if impl(ghc < 9.15)
+          build-depends: fused-effects >= 1.1.2.3
 
-       if impl(ghc < 9.7)
-          build-depends: polysemy >= 1.9.1.0
+       if impl(ghc < 9.15)
+          build-depends: polysemy >= 1.9.2.0
 
     build-depends:    base
                     , async
diff --git a/src/Effectful/Concurrent.hs b/src/Effectful/Concurrent.hs
--- a/src/Effectful/Concurrent.hs
+++ b/src/Effectful/Concurrent.hs
@@ -20,6 +20,7 @@
   , runConcurrent
 
     -- * Basic concurrency operations
+  , C.ThreadId
   , myThreadId
   , forkIO
   , forkFinally
@@ -59,13 +60,13 @@
   , C.rtsSupportsBoundThreads
   ) where
 
+import Control.Concurrent qualified as C
 import Control.Exception (Exception, SomeException)
 import Data.Bifunctor (second)
+import GHC.Conc qualified as GHC
 import System.Mem.Weak (Weak)
 import System.Posix.Types (Fd)
 import UnliftIO.STM (STM)
-import qualified Control.Concurrent as C
-import qualified GHC.Conc as GHC
 
 import Effectful
 import Effectful.Concurrent.Effect
@@ -81,14 +82,14 @@
 myThreadId = unsafeEff_ C.myThreadId
 
 -- | Lifted 'C.forkIO'.
-forkIO :: Concurrent :> es => Eff es () -> Eff es C.ThreadId
+forkIO :: (HasCallStack, Concurrent :> es) => Eff es () -> Eff es C.ThreadId
 forkIO k = unsafeEff $ \es -> do
   esF <- cloneEnv es
   C.forkIO $ unEff k esF
 
 -- | Lifted 'C.forkFinally'.
 forkFinally
-  :: Concurrent :> es
+  :: (HasCallStack, Concurrent :> es)
   => Eff es a
   -> (Either SomeException a -> Eff es ())
   -> Eff es C.ThreadId
@@ -98,7 +99,7 @@
 
 -- | Lifted 'C.forkIOWithUnmask'.
 forkIOWithUnmask
-  :: Concurrent :> es
+  :: (HasCallStack, Concurrent :> es)
   => ((forall a. Eff es a -> Eff es a) -> Eff es ())
   -> Eff es C.ThreadId
 forkIOWithUnmask = liftForkWithUnmask C.forkIOWithUnmask
@@ -115,14 +116,14 @@
 -- Threads with affinity
 
 -- | Lifted 'C.forkOn'.
-forkOn :: Concurrent :> es => Int -> Eff es () -> Eff es C.ThreadId
+forkOn :: (HasCallStack, Concurrent :> es) => Int -> Eff es () -> Eff es C.ThreadId
 forkOn n k = unsafeEff $ \es -> do
   esF <- cloneEnv es
   C.forkOn n (unEff k esF)
 
 -- | Lifted 'C.forkOnWithUnmask'.
 forkOnWithUnmask
-  :: Concurrent :> es
+  :: (HasCallStack, Concurrent :> es)
   => Int
   -> ((forall a. Eff es a -> Eff es a) -> Eff es ())
   -> Eff es C.ThreadId
@@ -180,14 +181,14 @@
 -- Bound threads
 
 -- | Lifted 'C.forkOS'.
-forkOS :: Concurrent :> es => Eff es () -> Eff es C.ThreadId
+forkOS :: (HasCallStack, Concurrent :> es) => Eff es () -> Eff es C.ThreadId
 forkOS k = unsafeEff $ \es -> do
   esF <- cloneEnv es
   C.forkOS $ unEff k esF
 
 -- | Lifted 'E.forkOSWithUnmask'.
 forkOSWithUnmask
-  :: Concurrent :> es
+  :: (HasCallStack, Concurrent :> es)
   => ((forall a. Eff es a -> Eff es a) -> Eff es ())
   -> Eff es C.ThreadId
 forkOSWithUnmask = liftForkWithUnmask C.forkOSWithUnmask
@@ -197,13 +198,13 @@
 isCurrentThreadBound = unsafeEff_ C.isCurrentThreadBound
 
 -- | Lifted 'C.runInBoundThread'.
-runInBoundThread :: Concurrent :> es => Eff es a -> Eff es a
+runInBoundThread :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es a
 runInBoundThread k = unsafeEff $ \es -> do
   esF <- cloneEnv es
   C.runInBoundThread $ unEff k esF
 
 -- | Lifted 'C.runInUnboundThread'.
-runInUnboundThread :: Concurrent :> es => Eff es a -> Eff es a
+runInUnboundThread :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es a
 runInUnboundThread k = unsafeEff $ \es -> do
   esF <- cloneEnv es
   C.runInUnboundThread $ unEff k esF
@@ -219,7 +220,8 @@
 -- Helpers
 
 liftForkWithUnmask
-  :: (((forall c. IO c -> IO c) -> IO a) -> IO C.ThreadId)
+  :: HasCallStack
+  => (((forall c. IO c -> IO c) -> IO a) -> IO C.ThreadId)
   -> ((forall c. Eff es c -> Eff es c) -> Eff es a)
   -> Eff es C.ThreadId
 liftForkWithUnmask fork action = unsafeEff $ \es -> do
diff --git a/src/Effectful/Concurrent/Async.hs b/src/Effectful/Concurrent/Async.hs
--- a/src/Effectful/Concurrent/Async.hs
+++ b/src/Effectful/Concurrent/Async.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE UndecidableInstances #-}
 -- | Lifted "Control.Concurrent.Async".
 module Effectful.Concurrent.Async
   ( -- * Effect
@@ -18,7 +17,8 @@
 
     -- ** Querying 'Async's
   , wait, poll, waitCatch, A.asyncThreadId
-  , cancel, uninterruptibleCancel, cancelWith, A.AsyncCancelled(..)
+  , cancel, cancelMany, uninterruptibleCancel, cancelWith
+  , A.AsyncCancelled(..)
   , A.compareAsyncs
 
     -- ** High-level utilities
@@ -80,9 +80,9 @@
 import Control.Exception (Exception, SomeException)
 import Control.Monad (forever)
 import Data.Kind (Type)
-import qualified Control.Concurrent.Async as A
-import qualified UnliftIO.Async as U
-import qualified UnliftIO.Internals.Async as I
+import Control.Concurrent.Async qualified as A
+import UnliftIO.Async qualified as U
+import UnliftIO.Internals.Async qualified as I
 
 import Effectful
 import Effectful.Concurrent.Effect
@@ -91,27 +91,27 @@
 import Effectful.Dispatch.Static.Unsafe
 
 -- | Lifted 'A.async'.
-async :: Concurrent :> es => Eff es a -> Eff es (Async a)
+async :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es (Async a)
 async = liftAsync A.async
 
 -- | Lifted 'A.asyncBound'.
-asyncBound :: Concurrent :> es => Eff es a -> Eff es (Async a)
+asyncBound :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es (Async a)
 asyncBound = liftAsync A.asyncBound
 
 -- | Lifted 'A.asyncOn'.
-asyncOn :: Concurrent :> es => Int -> Eff es a -> Eff es (Async a)
+asyncOn :: (HasCallStack, Concurrent :> es) => Int -> Eff es a -> Eff es (Async a)
 asyncOn cpu = liftAsync (A.asyncOn cpu)
 
 -- | Lifted 'A.asyncWithUnmask'.
 asyncWithUnmask
-  :: Concurrent :> es
+  :: (HasCallStack, Concurrent :> es)
   => ((forall b. Eff es b -> Eff es b) -> Eff es a)
   -> Eff es (Async a)
 asyncWithUnmask = liftAsyncWithUnmask A.asyncWithUnmask
 
 -- | Lifted 'A.asyncOnWithUnmask'.
 asyncOnWithUnmask
-  :: Concurrent :> es
+  :: (HasCallStack, Concurrent :> es)
   => Int
   -> ((forall b. Eff es b -> Eff es b) -> Eff es a)
   -> Eff es (Async a)
@@ -119,7 +119,7 @@
 
 -- | Lifted 'A.withAsync'.
 withAsync
-  :: Concurrent :> es
+  :: (HasCallStack, Concurrent :> es)
   => Eff es a
   -> (Async a -> Eff es b)
   -> Eff es b
@@ -127,7 +127,7 @@
 
 -- | Lifted 'A.withAsyncBound'.
 withAsyncBound
-  :: Concurrent :> es
+  :: (HasCallStack, Concurrent :> es)
   => Eff es a
   -> (Async a -> Eff es b)
   -> Eff es b
@@ -135,7 +135,7 @@
 
 -- | Lifted 'A.withAsyncOn'.
 withAsyncOn
-  :: Concurrent :> es
+  :: (HasCallStack, Concurrent :> es)
   => Int
   -> Eff es a
   -> (Async a -> Eff es b)
@@ -144,7 +144,7 @@
 
 -- | Lifted 'A.withAsyncWithUnmask'.
 withAsyncWithUnmask
-  :: Concurrent :> es
+  :: (HasCallStack, Concurrent :> es)
   => ((forall c. Eff es c -> Eff es c) -> Eff es a)
   -> (Async a -> Eff es b)
   -> Eff es b
@@ -152,7 +152,7 @@
 
 -- | Lifted 'A.withAsyncOnWithUnmask'.
 withAsyncOnWithUnmask
-  :: Concurrent :> es
+  :: (HasCallStack, Concurrent :> es)
   => Int
   -> ((forall c. Eff es c -> Eff es c) -> Eff es a)
   -> (Async a -> Eff es b)
@@ -174,6 +174,12 @@
 cancel :: Concurrent :> es => Async a -> Eff es ()
 cancel = unsafeEff_ . A.cancel
 
+-- | Lifted 'A.cancelMany'.
+--
+-- @since 2.6.1.0
+cancelMany :: Concurrent :> es => [Async a] -> Eff es ()
+cancelMany = unsafeEff_ . A.cancelMany
+
 -- | Lifted 'A.cancelWith'.
 cancelWith :: (Exception e, Concurrent :> es) => Async a -> e -> Eff es ()
 cancelWith a = unsafeEff_ . A.cancelWith a
@@ -268,22 +274,22 @@
 link2Only f a b = unsafeEff_ $ A.link2Only f a b
 
 -- | Lifted 'A.race'.
-race :: Concurrent :> es => Eff es a -> Eff es b -> Eff es (Either a b)
+race :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es b -> Eff es (Either a b)
 race ma mb = unsafeEff $ \es -> do
   A.race (unEff ma =<< cloneEnv es) (unEff mb =<< cloneEnv es)
 
 -- | Lifted 'A.race_'.
-race_ ::  Concurrent :> es => Eff es a -> Eff es b -> Eff es ()
+race_ :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es b -> Eff es ()
 race_ ma mb = unsafeEff $ \es -> do
   A.race_ (unEff ma =<< cloneEnv es) (unEff mb =<< cloneEnv es)
 
 -- | Lifted 'A.concurrently'.
-concurrently :: Concurrent :> es => Eff es a -> Eff es b -> Eff es (a, b)
+concurrently :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es b -> Eff es (a, b)
 concurrently ma mb = unsafeEff $ \es -> do
   A.concurrently (unEff ma =<< cloneEnv es) (unEff mb =<< cloneEnv es)
 
 -- | Lifted 'A.concurrently_'.
-concurrently_ :: Concurrent :> es => Eff es a -> Eff es b -> Eff es ()
+concurrently_ :: (HasCallStack, Concurrent :> es) => Eff es a -> Eff es b -> Eff es ()
 concurrently_ ma mb = unsafeEff $ \es -> do
   A.concurrently_ (unEff ma =<< cloneEnv es) (unEff mb =<< cloneEnv es)
 
@@ -293,7 +299,7 @@
 
 -- | Lifted 'A.mapConcurrently'.
 mapConcurrently
-  :: (Traversable f, Concurrent :> es)
+  :: (HasCallStack, Traversable f, Concurrent :> es)
   => (a -> Eff es b)
   -> f a
   -> Eff es (f b)
@@ -302,7 +308,7 @@
 
 -- | Lifted 'A.mapConcurrently_'.
 mapConcurrently_
-  :: (Foldable f, Concurrent :> es)
+  :: (HasCallStack, Foldable f, Concurrent :> es)
   => (a -> Eff es b)
   -> f a
   -> Eff es ()
@@ -311,7 +317,7 @@
 
 -- | Lifted 'A.forConcurrently'.
 forConcurrently
-  :: (Traversable f, Concurrent :> es)
+  :: (HasCallStack, Traversable f, Concurrent :> es)
   => f a
   -> (a -> Eff es b)
   -> Eff es (f b)
@@ -320,7 +326,7 @@
 
 -- | Lifted 'A.forConcurrently_'.
 forConcurrently_
-  :: (Foldable f, Concurrent :> es)
+  :: (HasCallStack, Foldable f, Concurrent :> es)
   => f a
   -> (a -> Eff es b)
   -> Eff es ()
@@ -328,12 +334,20 @@
   U.forConcurrently_ t (\a -> unEff (f a) =<< cloneEnv es)
 
 -- | Lifted 'A.replicateConcurrently'.
-replicateConcurrently :: Concurrent :> es => Int -> Eff es a -> Eff es [a]
+replicateConcurrently
+  :: (HasCallStack, Concurrent :> es)
+  => Int
+  -> Eff es a
+  -> Eff es [a]
 replicateConcurrently n f = unsafeEff $ \es -> do
   U.replicateConcurrently n (unEff f =<< cloneEnv es)
 
 -- | Lifted 'A.replicateConcurrently_'.
-replicateConcurrently_ :: Concurrent :> es => Int -> Eff es a -> Eff es ()
+replicateConcurrently_
+  :: (HasCallStack, Concurrent :> es)
+  => Int
+  -> Eff es a
+  -> Eff es ()
 replicateConcurrently_ n f = unsafeEff $ \es -> do
   U.replicateConcurrently_ n (unEff f =<< cloneEnv es)
 
@@ -342,7 +356,7 @@
 
 -- | Lifted 'U.pooledMapConcurrentlyN'.
 pooledMapConcurrentlyN
-  :: (Concurrent :> es, Traversable t)
+  :: (HasCallStack, Concurrent :> es, Traversable t)
   => Int
   -> (a -> Eff es b)
   -> t a
@@ -352,7 +366,7 @@
 
 -- | Lifted 'U.pooledMapConcurrently'.
 pooledMapConcurrently
-  :: (Concurrent :> es, Traversable t)
+  :: (HasCallStack, Concurrent :> es, Traversable t)
   => (a -> Eff es b)
   -> t a
   -> Eff es (t b)
@@ -361,7 +375,7 @@
 
 -- | Lifted 'U.pooledMapConcurrentlyN'.
 pooledMapConcurrentlyN_
-  :: (Concurrent :> es, Foldable f)
+  :: (HasCallStack, Concurrent :> es, Foldable f)
   => Int
   -> (a -> Eff es b)
   -> f a
@@ -371,7 +385,7 @@
 
 -- | Lifted 'U.pooledMapConcurrently_'.
 pooledMapConcurrently_
-  :: (Concurrent :> es, Foldable f)
+  :: (HasCallStack, Concurrent :> es, Foldable f)
   => (a -> Eff es b)
   -> f a
   -> Eff es ()
@@ -380,7 +394,7 @@
 
 -- | Lifted 'U.pooledForConcurrentlyN'.
 pooledForConcurrentlyN
-  :: (Concurrent :> es, Traversable t)
+  :: (HasCallStack, Concurrent :> es, Traversable t)
   => Int
   -> t a
   -> (a -> Eff es b)
@@ -390,7 +404,7 @@
 
 -- | Lifted 'U.pooledForConcurrently'.
 pooledForConcurrently
-  :: (Concurrent :> es, Traversable t)
+  :: (HasCallStack, Concurrent :> es, Traversable t)
   => t a
   -> (a -> Eff es b)
   -> Eff es (t b)
@@ -399,7 +413,7 @@
 
 -- | Lifted 'U.pooledForConcurrentlyN'.
 pooledForConcurrentlyN_
-  :: (Concurrent :> es, Foldable f)
+  :: (HasCallStack, Concurrent :> es, Foldable f)
   => Int
   -> f a
   -> (a -> Eff es b)
@@ -409,7 +423,7 @@
 
 -- | Lifted 'U.pooledForConcurrently_'.
 pooledForConcurrently_
-  :: (Concurrent :> es, Foldable f)
+  :: (HasCallStack, Concurrent :> es, Foldable f)
   => f a
   -> (a -> Eff es b)
   -> Eff es ()
@@ -417,22 +431,40 @@
   U.pooledForConcurrently_ t (\a -> unEff (f a) =<< cloneEnv es)
 
 -- | Lifted 'U.pooledReplicateConcurrentlyN'.
-pooledReplicateConcurrentlyN :: Concurrent :> es => Int -> Int -> Eff es a -> Eff es [a]
+pooledReplicateConcurrentlyN
+  :: (HasCallStack, Concurrent :> es)
+  => Int
+  -> Int
+  -> Eff es a
+  -> Eff es [a]
 pooledReplicateConcurrentlyN threads n f = unsafeEff $ \es -> do
   U.pooledReplicateConcurrentlyN threads n (unEff f =<< cloneEnv es)
 
 -- | Lifted 'U.pooledReplicateConcurrently'.
-pooledReplicateConcurrently :: Concurrent :> es => Int -> Eff es a -> Eff es [a]
+pooledReplicateConcurrently
+  :: (HasCallStack, Concurrent :> es)
+  => Int
+  -> Eff es a
+  -> Eff es [a]
 pooledReplicateConcurrently n f = unsafeEff $ \es -> do
   U.pooledReplicateConcurrently n (unEff f =<< cloneEnv es)
 
 -- | Lifted 'U.pooledReplicateConcurrentlyN_'.
-pooledReplicateConcurrentlyN_ :: Concurrent :> es => Int -> Int -> Eff es a -> Eff es ()
+pooledReplicateConcurrentlyN_
+  :: (HasCallStack, Concurrent :> es)
+  => Int
+  -> Int
+  -> Eff es a
+  -> Eff es ()
 pooledReplicateConcurrentlyN_ threads n f = unsafeEff $ \es -> do
   U.pooledReplicateConcurrentlyN_ threads n (unEff f =<< cloneEnv es)
 
 -- | Lifted 'U.pooledReplicateConcurrently_'.
-pooledReplicateConcurrently_ :: Concurrent :> es => Int -> Eff es a -> Eff es ()
+pooledReplicateConcurrently_
+  :: (HasCallStack, Concurrent :> es)
+  => Int
+  -> Eff es a
+  -> Eff es ()
 pooledReplicateConcurrently_ n f = unsafeEff $ \es -> do
   U.pooledReplicateConcurrently_ n (unEff f =<< cloneEnv es)
 
@@ -448,7 +480,7 @@
   Alt    :: Conc es a -> Conc es a -> Conc es a
   Empty  :: Conc es a
 
-deriving instance Functor (Conc es)
+deriving stock instance Functor (Conc es)
 
 instance Applicative (Conc es) where
   pure   = Pure
@@ -472,7 +504,7 @@
 conc = Action
 
 -- | Lifted 'U.runConc'.
-runConc :: Concurrent :> es => Conc es a -> Eff es a
+runConc :: (HasCallStack, Concurrent :> es) => Conc es a -> Eff es a
 runConc m = unsafeEff $ \es -> U.runConc (unliftConc es m)
   where
     unliftConc :: Env es -> Conc es a -> U.Conc IO a
@@ -513,7 +545,8 @@
 -- Helpers
 
 liftAsync
-  :: (IO a -> IO (Async a))
+  :: HasCallStack
+  => (IO a -> IO (Async a))
   -> Eff es a
   -> Eff es (Async a)
 liftAsync fork action = unsafeEff $ \es -> do
@@ -521,7 +554,8 @@
   fork $ unEff action esA
 
 liftAsyncWithUnmask
-  :: (((forall b. IO b -> IO b) -> IO a) -> IO (Async a))
+  :: HasCallStack
+  => (((forall b. IO b -> IO b) -> IO a) -> IO (Async a))
   -> ((forall b. Eff es b -> Eff es b) -> Eff es a)
   -> Eff es (Async a)
 liftAsyncWithUnmask fork action = unsafeEff $ \es -> do
@@ -530,7 +564,8 @@
   fork $ \unmask -> unEff (action $ reallyUnsafeLiftMapIO unmask) esA
 
 liftWithAsync
-  :: (IO a -> (Async a -> IO b) -> IO b)
+  :: HasCallStack
+  => (IO a -> (Async a -> IO b) -> IO b)
   -> Eff es a
   -> (Async a -> Eff es b)
   -> Eff es b
@@ -540,7 +575,8 @@
         (\a -> unEff (k a) es)
 
 liftWithAsyncWithUnmask
-  :: (((forall c. IO c -> IO c) -> IO a) -> (Async a -> IO b) -> IO b)
+  :: HasCallStack
+  => (((forall c. IO c -> IO c) -> IO a) -> (Async a -> IO b) -> IO b)
   -> ((forall c. Eff es c -> Eff es c) -> Eff es a)
   -> (Async a -> Eff es b)
   -> Eff es b
diff --git a/src/Effectful/Concurrent/Chan.hs b/src/Effectful/Concurrent/Chan.hs
--- a/src/Effectful/Concurrent/Chan.hs
+++ b/src/Effectful/Concurrent/Chan.hs
@@ -17,7 +17,7 @@
   ) where
 
 import Control.Concurrent.Chan (Chan)
-import qualified Control.Concurrent.Chan as C
+import Control.Concurrent.Chan qualified as C
 
 import Effectful
 import Effectful.Concurrent.Effect
diff --git a/src/Effectful/Concurrent/Chan/Strict.hs b/src/Effectful/Concurrent/Chan/Strict.hs
new file mode 100644
--- /dev/null
+++ b/src/Effectful/Concurrent/Chan/Strict.hs
@@ -0,0 +1,50 @@
+-- | Lifted "Control.Concurrent.Chan.Strict".
+--
+-- @since 2.4.0.0
+module Effectful.Concurrent.Chan.Strict
+  ( -- * Effect
+    Concurrent
+
+    -- ** Handlers
+  , runConcurrent
+
+    -- * Chan
+  , Chan'
+  , newChan'
+  , writeChan'
+  , readChan'
+  , dupChan'
+  , getChan'Contents
+  , writeList2Chan'
+  ) where
+
+import Control.Concurrent.Chan.Strict (Chan')
+import Control.Concurrent.Chan.Strict qualified as C
+
+import Effectful
+import Effectful.Concurrent.Effect
+import Effectful.Dispatch.Static
+
+-- | Lifted 'C.newChan''.
+newChan' :: Concurrent :> es => Eff es (Chan' a)
+newChan' = unsafeEff_ C.newChan'
+
+-- | Lifted 'C.writeChan''.
+writeChan' :: Concurrent :> es => Chan' a -> a -> Eff es ()
+writeChan' c = unsafeEff_ . C.writeChan' c
+
+-- | Lifted 'C.readChan''.
+readChan' :: Concurrent :> es => Chan' a -> Eff es a
+readChan' = unsafeEff_ . C.readChan'
+
+-- | Lifted 'C.dupChan''.
+dupChan' :: Concurrent :> es => Chan' a -> Eff es (Chan' a)
+dupChan' = unsafeEff_ . C.dupChan'
+
+-- | Lifted 'C.getChan'Contents'.
+getChan'Contents :: Concurrent :> es => Chan' a -> Eff es [a]
+getChan'Contents = unsafeEff_ . C.getChan'Contents
+
+-- | Lifted 'C.writeList2Chan''.
+writeList2Chan' :: Concurrent :> es => Chan' a -> [a] -> Eff es ()
+writeList2Chan' c = unsafeEff_ . C.writeList2Chan' c
diff --git a/src/Effectful/Concurrent/Effect.hs b/src/Effectful/Concurrent/Effect.hs
--- a/src/Effectful/Concurrent/Effect.hs
+++ b/src/Effectful/Concurrent/Effect.hs
@@ -17,7 +17,7 @@
 -- it possible to escape the scope of any scoped effect operation. Consider the
 -- following:
 --
--- >>> import qualified Effectful.Reader.Static as R
+-- >>> import Effectful.Reader.Static qualified as R
 --
 -- >>> printAsk msg = liftIO . putStrLn . (msg ++) . (": " ++) =<< R.ask
 --
@@ -54,14 +54,14 @@
 -- a mutable state. That's why statically dispatched @State@ and @Writer@
 -- effects come in two flavors, local and shared:
 --
--- >>> import qualified Effectful.State.Static.Local as SL
+-- >>> import Effectful.State.Static.Local qualified as SL
 -- >>> :{
 --   runEff . SL.execState "Hi" . runConcurrent $ do
 --     replicateConcurrently_ 3 $ SL.modify (++ "!")
 -- :}
 -- "Hi"
 --
--- >>> import qualified Effectful.State.Static.Shared as SS
+-- >>> import Effectful.State.Static.Shared qualified as SS
 -- >>> :{
 --   runEff . SS.execState "Hi" . runConcurrent $ do
 --     replicateConcurrently_ 3 $ SS.modify (++ "!")
@@ -78,7 +78,7 @@
 data instance StaticRep Concurrent = Concurrent
 
 -- | Run the 'Concurrent' effect.
-runConcurrent :: IOE :> es => Eff (Concurrent : es) a -> Eff es a
+runConcurrent :: (HasCallStack, IOE :> es) => Eff (Concurrent : es) a -> Eff es a
 runConcurrent = evalStaticRep Concurrent
 
 -- $setup
diff --git a/src/Effectful/Concurrent/MVar.hs b/src/Effectful/Concurrent/MVar.hs
--- a/src/Effectful/Concurrent/MVar.hs
+++ b/src/Effectful/Concurrent/MVar.hs
@@ -29,7 +29,7 @@
 
 import System.Mem.Weak (Weak)
 import Control.Concurrent.MVar (MVar)
-import qualified Control.Concurrent.MVar as M
+import Control.Concurrent.MVar qualified as M
 
 import Effectful
 import Effectful.Concurrent.Effect
@@ -81,34 +81,47 @@
 withMVar :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b
 withMVar var f = reallyUnsafeUnliftIO $ \unlift -> do
   M.withMVar var $ unlift . f
+{-# INLINE withMVar #-}
 
 -- | Lifted 'M.withMVarMasked'.
 withMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b
 withMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do
   M.withMVarMasked var $ unlift . f
+{-# INLINE withMVarMasked #-}
 
 -- | Lifted 'M.modifyMVar_'.
 modifyMVar_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es ()
 modifyMVar_ var f = reallyUnsafeUnliftIO $ \unlift -> do
   M.modifyMVar_ var $ unlift . f
+{-# INLINE modifyMVar_ #-}
 
 -- | Lifted 'M.modifyMVar'.
 modifyMVar :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b
 modifyMVar var f = reallyUnsafeUnliftIO $ \unlift -> do
   M.modifyMVar var $ unlift . f
+{-# INLINE modifyMVar #-}
 
 -- | Lifted 'M.modifyMVarMasked_'.
 modifyMVarMasked_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es ()
 modifyMVarMasked_ var f = reallyUnsafeUnliftIO $ \unlift -> do
   M.modifyMVarMasked_ var $ unlift . f
+{-# INLINE modifyMVarMasked_ #-}
 
 -- | Lifted 'M.modifyMVarMasked'.
 modifyMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b
 modifyMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do
   M.modifyMVarMasked var $ unlift . f
+{-# INLINE modifyMVarMasked #-}
 
 -- | Lifted 'M.mkWeakMVar'.
-mkWeakMVar :: Concurrent :> es => MVar a -> Eff es () -> Eff es (Weak (MVar a))
+--
+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes
+-- to thread local data will not be visible outside of it.
+mkWeakMVar
+  :: (HasCallStack, Concurrent :> es)
+  => MVar a
+  -> Eff es ()
+  -> Eff es (Weak (MVar a))
 mkWeakMVar var f = unsafeEff $ \es -> do
   -- The finalizer can run at any point and in any thread.
   M.mkWeakMVar var . unEff f =<< cloneEnv es
diff --git a/src/Effectful/Concurrent/MVar/Strict.hs b/src/Effectful/Concurrent/MVar/Strict.hs
--- a/src/Effectful/Concurrent/MVar/Strict.hs
+++ b/src/Effectful/Concurrent/MVar/Strict.hs
@@ -1,5 +1,6 @@
--- | Lifted "Control.Concurrent.MVar" with operations that force values put
--- inside an 'MVar' to WHNF.
+-- | Lifted "Control.Concurrent.MVar.Strict".
+--
+-- @since 2.4.0.0
 module Effectful.Concurrent.MVar.Strict
   ( -- * Effect
     Concurrent
@@ -8,29 +9,29 @@
   , runConcurrent
 
     -- * MVar
-  , MVar
-  , newEmptyMVar
-  , newMVar
-  , takeMVar
-  , putMVar
-  , readMVar
-  , swapMVar
-  , tryTakeMVar
-  , tryPutMVar
-  , isEmptyMVar
-  , withMVar
-  , withMVarMasked
-  , modifyMVar
-  , modifyMVar_
-  , modifyMVarMasked
-  , modifyMVarMasked_
-  , tryReadMVar
-  , mkWeakMVar
+  , MVar'
+  , newEmptyMVar'
+  , newMVar'
+  , takeMVar'
+  , putMVar'
+  , readMVar'
+  , swapMVar'
+  , tryTakeMVar'
+  , tryPutMVar'
+  , tryReadMVar'
+  , isEmptyMVar'
+  , withMVar'
+  , withMVar'Masked
+  , modifyMVar'
+  , modifyMVar'_
+  , modifyMVar'Masked
+  , modifyMVar'Masked_
+  , mkWeakMVar'
   ) where
 
 import System.Mem.Weak (Weak)
-import Control.Concurrent.MVar (MVar)
-import qualified Control.Concurrent.MVar as M
+import Control.Concurrent.MVar.Strict (MVar')
+import Control.Concurrent.MVar.Strict qualified as M
 
 import Effectful
 import Effectful.Concurrent.Effect
@@ -38,86 +39,90 @@
 import Effectful.Dispatch.Static.Primitive
 import Effectful.Dispatch.Static.Unsafe
 
--- | Lifted 'M.newEmptyMVar'.
-newEmptyMVar :: Concurrent :> es => Eff es (MVar a)
-newEmptyMVar = unsafeEff_ M.newEmptyMVar
+-- | Lifted 'M.newEmptyMVar''.
+newEmptyMVar' :: Concurrent :> es => Eff es (MVar' a)
+newEmptyMVar' = unsafeEff_ M.newEmptyMVar'
 
--- | Lifted 'M.newMVar' that evaluates the value to WHNF.
-newMVar :: Concurrent :> es => a -> Eff es (MVar a)
-newMVar a = unsafeEff_ $ a `seq` M.newMVar a
+-- | Lifted 'M.newMVar''.
+newMVar' :: Concurrent :> es => a -> Eff es (MVar' a)
+newMVar' = unsafeEff_ . M.newMVar'
 
--- | Lifted 'M.takeMVar'.
-takeMVar :: Concurrent :> es => MVar a -> Eff es a
-takeMVar = unsafeEff_ . M.takeMVar
+-- | Lifted 'M.takeMVar''.
+takeMVar' :: Concurrent :> es => MVar' a -> Eff es a
+takeMVar' = unsafeEff_ . M.takeMVar'
 
--- | Lifted 'M.putMVar'.
-putMVar :: Concurrent :> es => MVar a -> a -> Eff es ()
-putMVar var a = unsafeEff_ $ a `seq` M.putMVar var a
+-- | Lifted 'M.putMVar''.
+putMVar' :: Concurrent :> es => MVar' a -> a -> Eff es ()
+putMVar' var = unsafeEff_ . M.putMVar' var
 
--- | Lifted 'M.readMVar'.
-readMVar :: Concurrent :> es => MVar a -> Eff es a
-readMVar = unsafeEff_ . M.readMVar
+-- | Lifted 'M.readMVar''.
+readMVar' :: Concurrent :> es => MVar' a -> Eff es a
+readMVar' = unsafeEff_ . M.readMVar'
 
--- | Lifted 'M.swapMVar' that evaluates the new value to WHNF.
-swapMVar :: Concurrent :> es => MVar a -> a -> Eff es a
-swapMVar var a = unsafeEff_ $ a `seq` M.swapMVar var a
+-- | Lifted 'M.swapMVar''.
+swapMVar' :: Concurrent :> es => MVar' a -> a -> Eff es a
+swapMVar' var = unsafeEff_ . M.swapMVar' var
 
--- | Lifted 'M.tryTakeMVar'.
-tryTakeMVar :: Concurrent :> es => MVar a -> Eff es (Maybe a)
-tryTakeMVar = unsafeEff_ . M.tryTakeMVar
+-- | Lifted 'M.tryTakeMVar''.
+tryTakeMVar' :: Concurrent :> es => MVar' a -> Eff es (Maybe a)
+tryTakeMVar' = unsafeEff_ . M.tryTakeMVar'
 
--- | Lifted 'M.tryPutMVar' that evaluates the new value to WHNF.
-tryPutMVar :: Concurrent :> es => MVar a -> a -> Eff es Bool
-tryPutMVar var a = unsafeEff_ $ a `seq` M.tryPutMVar var a
+-- | Lifted 'M.tryPutMVar''.
+tryPutMVar' :: Concurrent :> es => MVar' a -> a -> Eff es Bool
+tryPutMVar' var = unsafeEff_ . M.tryPutMVar' var
 
--- | Lifted 'M.isEmptyMVar'.
-isEmptyMVar :: Concurrent :> es => MVar a -> Eff es Bool
-isEmptyMVar = unsafeEff_ . M.isEmptyMVar
+-- | Lifted 'M.tryReadMVar''.
+tryReadMVar' :: Concurrent :> es => MVar' a -> Eff es (Maybe a)
+tryReadMVar' = unsafeEff_ . M.tryReadMVar'
 
--- | Lifted 'M.tryReadMVar'.
-tryReadMVar :: Concurrent :> es => MVar a -> Eff es (Maybe a)
-tryReadMVar = unsafeEff_ . M.tryReadMVar
+-- | Lifted 'M.isEmptyMVar''.
+isEmptyMVar' :: Concurrent :> es => MVar' a -> Eff es Bool
+isEmptyMVar' = unsafeEff_ . M.isEmptyMVar'
 
--- | Lifted 'M.withMVar'.
-withMVar :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b
-withMVar var f = reallyUnsafeUnliftIO $ \unlift -> do
-  M.withMVar var $ unlift . f
+-- | Lifted 'M.withMVar''.
+withMVar' :: Concurrent :> es => MVar' a -> (a -> Eff es b) -> Eff es b
+withMVar' var f = reallyUnsafeUnliftIO $ \unlift -> do
+  M.withMVar' var $ unlift . f
+{-# INLINE withMVar' #-}
 
--- | Lifted 'M.withMVarMasked'.
-withMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b
-withMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do
-  M.withMVarMasked var $ unlift . f
+-- | Lifted 'M.withMVar'Masked'.
+withMVar'Masked :: Concurrent :> es => MVar' a -> (a -> Eff es b) -> Eff es b
+withMVar'Masked var f = reallyUnsafeUnliftIO $ \unlift -> do
+  M.withMVar'Masked var $ unlift . f
+{-# INLINE withMVar'Masked #-}
 
--- | Lifted 'M.modifyMVar_' that evaluates the new value to WHNF.
-modifyMVar_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es ()
-modifyMVar_ var f = reallyUnsafeUnliftIO $ \unlift -> do
-  M.modifyMVar_ var $ \a0 -> do
-    a <- unlift $ f a0
-    a `seq` pure a
+-- | Lifted 'M.modifyMVar'_'.
+modifyMVar'_ :: Concurrent :> es => MVar' a -> (a -> Eff es a) -> Eff es ()
+modifyMVar'_ var f = reallyUnsafeUnliftIO $ \unlift -> do
+  M.modifyMVar'_ var $ unlift . f
+{-# INLINE modifyMVar'_ #-}
 
--- | Lifted 'M.modifyMVar' that evaluates the new value to WHNF.
-modifyMVar :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b
-modifyMVar var f = reallyUnsafeUnliftIO $ \unlift -> do
-  M.modifyMVar var $ \a0 -> do
-    (a, b) <- unlift $ f a0
-    a `seq` pure (a, b)
+-- | Lifted 'M.modifyMVar''.
+modifyMVar' :: Concurrent :> es => MVar' a -> (a -> Eff es (a, b)) -> Eff es b
+modifyMVar' var f = reallyUnsafeUnliftIO $ \unlift -> do
+  M.modifyMVar' var $ unlift . f
+{-# INLINE modifyMVar' #-}
 
--- | Lifted 'M.modifyMVarMasked_' that evaluates the new value to WHNF.
-modifyMVarMasked_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es ()
-modifyMVarMasked_ var f = reallyUnsafeUnliftIO $ \unlift -> do
-  M.modifyMVarMasked_ var $ \a0 -> do
-    a <- unlift $ f a0
-    a `seq` pure a
+-- | Lifted 'M.modifyMVar'Masked_'.
+modifyMVar'Masked_ :: Concurrent :> es => MVar' a -> (a -> Eff es a) -> Eff es ()
+modifyMVar'Masked_ var f = reallyUnsafeUnliftIO $ \unlift -> do
+  M.modifyMVar'Masked_ var $ unlift . f
+{-# INLINE modifyMVar'Masked_ #-}
 
--- | Lifted 'M.modifyMVarMasked' that evaluates the new value to WHNF.
-modifyMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b
-modifyMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do
-  M.modifyMVarMasked var $ \a0 -> do
-    a <- unlift $ f a0
-    a `seq` pure a
+-- | Lifted 'M.modifyMVar'Masked'.
+modifyMVar'Masked :: Concurrent :> es => MVar' a -> (a -> Eff es (a, b)) -> Eff es b
+modifyMVar'Masked var f = reallyUnsafeUnliftIO $ \unlift -> do
+  M.modifyMVar'Masked var $ unlift . f
+{-# INLINE modifyMVar'Masked #-}
 
--- | Lifted 'M.mkWeakMVar'.
-mkWeakMVar :: Concurrent :> es => MVar a -> Eff es () -> Eff es (Weak (MVar a))
-mkWeakMVar var f = unsafeEff $ \es -> do
+-- | Lifted 'M.mkWeakMVar''.
+--
+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes
+-- to thread local data will not be visible outside of it.
+mkWeakMVar'
+  :: (HasCallStack, Concurrent :> es)
+  => MVar' a -> Eff es ()
+  -> Eff es (Weak (MVar' a))
+mkWeakMVar' var f = unsafeEff $ \es -> do
   -- The finalizer can run at any point and in any thread.
-  M.mkWeakMVar var . unEff f =<< cloneEnv es
+  M.mkWeakMVar' var . unEff f =<< cloneEnv es
diff --git a/src/Effectful/Concurrent/MVar/Strict/Compat.hs b/src/Effectful/Concurrent/MVar/Strict/Compat.hs
new file mode 100644
--- /dev/null
+++ b/src/Effectful/Concurrent/MVar/Strict/Compat.hs
@@ -0,0 +1,138 @@
+-- | Lifted "Control.Concurrent.MVar" with operations that force values put
+-- inside an 'MVar' to WHNF.
+module Effectful.Concurrent.MVar.Strict.Compat
+  {-# DEPRECATED "Use Effectful.Concurrent.MVar.Strict" #-}
+  ( -- * Effect
+    Concurrent
+
+    -- ** Handlers
+  , runConcurrent
+
+    -- * MVar
+  , MVar
+  , newEmptyMVar
+  , newMVar
+  , takeMVar
+  , putMVar
+  , readMVar
+  , swapMVar
+  , tryTakeMVar
+  , tryPutMVar
+  , isEmptyMVar
+  , withMVar
+  , withMVarMasked
+  , modifyMVar
+  , modifyMVar_
+  , modifyMVarMasked
+  , modifyMVarMasked_
+  , tryReadMVar
+  , mkWeakMVar
+  ) where
+
+import System.Mem.Weak (Weak)
+import Control.Exception (evaluate)
+import Control.Concurrent.MVar (MVar)
+import Control.Concurrent.MVar qualified as M
+
+import Effectful
+import Effectful.Concurrent.Effect
+import Effectful.Dispatch.Static
+import Effectful.Dispatch.Static.Primitive
+import Effectful.Dispatch.Static.Unsafe
+
+-- | Lifted 'M.newEmptyMVar'.
+newEmptyMVar :: Concurrent :> es => Eff es (MVar a)
+newEmptyMVar = unsafeEff_ M.newEmptyMVar
+
+-- | Lifted 'M.newMVar' that evaluates the value to WHNF.
+newMVar :: Concurrent :> es => a -> Eff es (MVar a)
+newMVar a = unsafeEff_ $ M.newMVar =<< evaluate a
+
+-- | Lifted 'M.takeMVar'.
+takeMVar :: Concurrent :> es => MVar a -> Eff es a
+takeMVar = unsafeEff_ . M.takeMVar
+
+-- | Lifted 'M.putMVar'.
+putMVar :: Concurrent :> es => MVar a -> a -> Eff es ()
+putMVar var a = unsafeEff_ $ M.putMVar var =<< evaluate a
+
+-- | Lifted 'M.readMVar'.
+readMVar :: Concurrent :> es => MVar a -> Eff es a
+readMVar = unsafeEff_ . M.readMVar
+
+-- | Lifted 'M.swapMVar' that evaluates the new value to WHNF.
+swapMVar :: Concurrent :> es => MVar a -> a -> Eff es a
+swapMVar var a = unsafeEff_ $ M.swapMVar var =<< evaluate a
+
+-- | Lifted 'M.tryTakeMVar'.
+tryTakeMVar :: Concurrent :> es => MVar a -> Eff es (Maybe a)
+tryTakeMVar = unsafeEff_ . M.tryTakeMVar
+
+-- | Lifted 'M.tryPutMVar' that evaluates the new value to WHNF.
+tryPutMVar :: Concurrent :> es => MVar a -> a -> Eff es Bool
+tryPutMVar var a = unsafeEff_ $ M.tryPutMVar var =<< evaluate a
+
+-- | Lifted 'M.isEmptyMVar'.
+isEmptyMVar :: Concurrent :> es => MVar a -> Eff es Bool
+isEmptyMVar = unsafeEff_ . M.isEmptyMVar
+
+-- | Lifted 'M.tryReadMVar'.
+tryReadMVar :: Concurrent :> es => MVar a -> Eff es (Maybe a)
+tryReadMVar = unsafeEff_ . M.tryReadMVar
+
+-- | Lifted 'M.withMVar'.
+withMVar :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b
+withMVar var f = reallyUnsafeUnliftIO $ \unlift -> do
+  M.withMVar var $ unlift . f
+{-# INLINE withMVar #-}
+
+-- | Lifted 'M.withMVarMasked'.
+withMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es b) -> Eff es b
+withMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do
+  M.withMVarMasked var $ unlift . f
+{-# INLINE withMVarMasked #-}
+
+-- | Lifted 'M.modifyMVar_' that evaluates the new value to WHNF.
+modifyMVar_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es ()
+modifyMVar_ var f = reallyUnsafeUnliftIO $ \unlift -> do
+  M.modifyMVar_ var $ \a0 -> do
+    a <- unlift $ f a0
+    evaluate a
+{-# INLINE modifyMVar_ #-}
+
+-- | Lifted 'M.modifyMVar' that evaluates the new value to WHNF.
+modifyMVar :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b
+modifyMVar var f = reallyUnsafeUnliftIO $ \unlift -> do
+  M.modifyMVar var $ \a0 -> do
+    (a, b) <- unlift $ f a0
+    (, b) <$> evaluate a
+{-# INLINE modifyMVar #-}
+
+-- | Lifted 'M.modifyMVarMasked_' that evaluates the new value to WHNF.
+modifyMVarMasked_ :: Concurrent :> es => MVar a -> (a -> Eff es a) -> Eff es ()
+modifyMVarMasked_ var f = reallyUnsafeUnliftIO $ \unlift -> do
+  M.modifyMVarMasked_ var $ \a0 -> do
+    a <- unlift $ f a0
+    evaluate a
+{-# INLINE modifyMVarMasked_ #-}
+
+-- | Lifted 'M.modifyMVarMasked' that evaluates the new value to WHNF.
+modifyMVarMasked :: Concurrent :> es => MVar a -> (a -> Eff es (a, b)) -> Eff es b
+modifyMVarMasked var f = reallyUnsafeUnliftIO $ \unlift -> do
+  M.modifyMVarMasked var $ \a0 -> do
+    (a, b) <- unlift $ f a0
+    (, b) <$> evaluate a
+{-# INLINE modifyMVarMasked #-}
+
+-- | Lifted 'M.mkWeakMVar'.
+--
+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes
+-- to thread local data will not be visible outside of it.
+mkWeakMVar
+  :: (HasCallStack, Concurrent :> es)
+  => MVar a
+  -> Eff es ()
+  -> Eff es (Weak (MVar a))
+mkWeakMVar var f = unsafeEff $ \es -> do
+  -- The finalizer can run at any point and in any thread.
+  M.mkWeakMVar var . unEff f =<< cloneEnv es
diff --git a/src/Effectful/Concurrent/QSem.hs b/src/Effectful/Concurrent/QSem.hs
--- a/src/Effectful/Concurrent/QSem.hs
+++ b/src/Effectful/Concurrent/QSem.hs
@@ -14,7 +14,7 @@
   ) where
 
 import Control.Concurrent.QSem (QSem)
-import qualified Control.Concurrent.QSem as Q
+import Control.Concurrent.QSem qualified as Q
 
 import Effectful
 import Effectful.Concurrent.Effect
diff --git a/src/Effectful/Concurrent/QSemN.hs b/src/Effectful/Concurrent/QSemN.hs
--- a/src/Effectful/Concurrent/QSemN.hs
+++ b/src/Effectful/Concurrent/QSemN.hs
@@ -14,7 +14,7 @@
   ) where
 
 import Control.Concurrent.QSemN (QSemN)
-import qualified Control.Concurrent.QSemN as Q
+import Control.Concurrent.QSemN qualified as Q
 
 import Effectful
 import Effectful.Concurrent.Effect
diff --git a/src/Effectful/Concurrent/STM.hs b/src/Effectful/Concurrent/STM.hs
--- a/src/Effectful/Concurrent/STM.hs
+++ b/src/Effectful/Concurrent/STM.hs
@@ -37,6 +37,7 @@
   , STM.takeTMVar
   , STM.putTMVar
   , STM.readTMVar
+  , STM.writeTMVar
   , STM.tryReadTMVar
   , STM.swapTMVar
   , STM.tryTakeTMVar
@@ -90,7 +91,7 @@
   ) where
 
 import Control.Concurrent.STM (STM, TVar, TMVar, TChan, TQueue, TBQueue)
-import qualified Control.Concurrent.STM as STM
+import Control.Concurrent.STM qualified as STM
 import System.Mem.Weak (Weak)
 import GHC.Natural (Natural)
 
@@ -116,7 +117,14 @@
 registerDelay = unsafeEff_ . STM.registerDelay
 
 -- | Lifted 'STM.mkWeakTVar'.
-mkWeakTVar :: Concurrent :> es => TVar a -> Eff es () -> Eff es (Weak (TVar a))
+--
+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes
+-- to thread local data will not be visible outside of it.
+mkWeakTVar
+  :: (HasCallStack, Concurrent :> es)
+  => TVar a
+  -> Eff es ()
+  -> Eff es (Weak (TVar a))
 mkWeakTVar var f = unsafeEff $ \es -> do
   -- The finalizer can run at any point and in any thread.
   STM.mkWeakTVar var . unEff f =<< cloneEnv es
@@ -130,7 +138,14 @@
 newEmptyTMVarIO = unsafeEff_ STM.newEmptyTMVarIO
 
 -- | Lifted 'STM.mkWeakTMVar'.
-mkWeakTMVar :: Concurrent :> es => TMVar a -> Eff es () -> Eff es (Weak (TMVar a))
+--
+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes
+-- to thread local data will not be visible outside of it.
+mkWeakTMVar
+  :: (HasCallStack, Concurrent :> es)
+  => TMVar a
+  -> Eff es ()
+  -> Eff es (Weak (TMVar a))
 mkWeakTMVar var f = unsafeEff $ \es -> do
   -- The finalizer can run at any point and in any thread.
   STM.mkWeakTMVar var . unEff f =<< cloneEnv es
diff --git a/src/Effectful/Console/ByteString.hs b/src/Effectful/Console/ByteString.hs
--- a/src/Effectful/Console/ByteString.hs
+++ b/src/Effectful/Console/ByteString.hs
@@ -4,9 +4,9 @@
 -- to avoid name clashes with the functions provided by "Prelude", e.g.:
 --
 -- > import Data.ByteString (ByteString)
--- > import qualified Data.ByteString.Char8 as BS8
+-- > import Data.ByteString.Char8 qualified as BS8
 -- > import Effectful.Console.ByteString (Console)
--- > import qualified Effectful.Console.ByteString as Console
+-- > import Effectful.Console.ByteString qualified as Console
 --
 module Effectful.Console.ByteString
   ( -- * Effect
@@ -32,7 +32,7 @@
   )
 
 import Data.ByteString (ByteString)
-import qualified Data.ByteString.Char8 as BS8
+import Data.ByteString.Char8 qualified as BS8
 
 import Effectful
 import Effectful.Console.Effect
diff --git a/src/Effectful/Console/ByteString/Lazy.hs b/src/Effectful/Console/ByteString/Lazy.hs
--- a/src/Effectful/Console/ByteString/Lazy.hs
+++ b/src/Effectful/Console/ByteString/Lazy.hs
@@ -4,9 +4,9 @@
 -- to avoid name clashes with the functions provided by "Prelude", e.g.:
 --
 -- > import Data.ByteString.Lazy.Char8 (ByteString)
--- > import qualified Data.ByteString.Lazy.Char8 as LBS8
+-- > import Data.ByteString.Lazy.Char8 qualified as LBS8
 -- > import Effectful.Console.ByteString.Lazy (Console)
--- > import qualified Effectful.Console.ByteString.Lazy as Console
+-- > import Effectful.Console.ByteString.Lazy qualified as Console
 --
 module Effectful.Console.ByteString.Lazy
   ( -- * Effect
@@ -30,7 +30,7 @@
   )
 
 import Data.ByteString.Lazy (ByteString)
-import qualified Data.ByteString.Lazy.Char8 as LBS8
+import Data.ByteString.Lazy.Char8 qualified as LBS8
 
 import Effectful
 import Effectful.Console.Effect
diff --git a/src/Effectful/Console/Effect.hs b/src/Effectful/Console/Effect.hs
--- a/src/Effectful/Console/Effect.hs
+++ b/src/Effectful/Console/Effect.hs
@@ -17,5 +17,5 @@
 data instance StaticRep Console = Console
 
 -- | Run the 'Console' effect.
-runConsole :: IOE :> es => Eff (Console : es) a -> Eff es a
+runConsole :: (HasCallStack, IOE :> es) => Eff (Console : es) a -> Eff es a
 runConsole = evalStaticRep Console
diff --git a/src/Effectful/Environment.hs b/src/Effectful/Environment.hs
--- a/src/Effectful/Environment.hs
+++ b/src/Effectful/Environment.hs
@@ -20,7 +20,7 @@
   , withProgName
   ) where
 
-import qualified System.Environment as E
+import System.Environment qualified as E
 
 import Effectful
 import Effectful.Dispatch.Static
@@ -32,7 +32,7 @@
 data instance StaticRep Environment = Environment
 
 -- | Run the 'Environment' effect.
-runEnvironment :: IOE :> es => Eff (Environment : es) a -> Eff es a
+runEnvironment :: (HasCallStack, IOE :> es) => Eff (Environment : es) a -> Eff es a
 runEnvironment = evalStaticRep Environment
 
 -- | Lifted 'E.getArgs'.
diff --git a/src/Effectful/FileSystem.hs b/src/Effectful/FileSystem.hs
--- a/src/Effectful/FileSystem.hs
+++ b/src/Effectful/FileSystem.hs
@@ -92,7 +92,7 @@
   ) where
 
 import Data.Time (UTCTime)
-import qualified System.Directory as D
+import System.Directory qualified as D
 
 import Effectful
 import Effectful.Dispatch.Static
diff --git a/src/Effectful/FileSystem/Effect.hs b/src/Effectful/FileSystem/Effect.hs
--- a/src/Effectful/FileSystem/Effect.hs
+++ b/src/Effectful/FileSystem/Effect.hs
@@ -16,5 +16,5 @@
 data instance StaticRep FileSystem = FileSystem
 
 -- | Run the 'FileSystem' effect.
-runFileSystem :: IOE :> es => Eff (FileSystem : es) a -> Eff es a
+runFileSystem :: (HasCallStack, IOE :> es) => Eff (FileSystem : es) a -> Eff es a
 runFileSystem = evalStaticRep FileSystem
diff --git a/src/Effectful/FileSystem/IO.hs b/src/Effectful/FileSystem/IO.hs
--- a/src/Effectful/FileSystem/IO.hs
+++ b/src/Effectful/FileSystem/IO.hs
@@ -39,7 +39,7 @@
   ) where
 
 import System.IO (Handle, IOMode (..))
-import qualified System.IO as IO
+import System.IO qualified as IO
 
 import Effectful
 import Effectful.Dispatch.Static
diff --git a/src/Effectful/FileSystem/IO/ByteString.hs b/src/Effectful/FileSystem/IO/ByteString.hs
--- a/src/Effectful/FileSystem/IO/ByteString.hs
+++ b/src/Effectful/FileSystem/IO/ByteString.hs
@@ -5,8 +5,8 @@
 -- to avoid name clashes with the functions provided by "Prelude", e.g.:
 --
 -- > import Data.ByteString (ByteString)
--- > import qualified Data.ByteString as BS
--- > import qualified Effectful.FileSystem.IO.ByteString as EBS
+-- > import Data.ByteString qualified as BS
+-- > import Effectful.FileSystem.IO.ByteString qualified as EBS
 --
 module Effectful.FileSystem.IO.ByteString
 #if MIN_VERSION_bytestring(0,11,2)
@@ -35,20 +35,18 @@
   , hPutStrLn
   ) where
 
-import Prelude hiding
-  ( appendFile
-  , readFile
-  , writeFile
-  )
-
 import Data.ByteString (ByteString)
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Char8 as BS8
+import Data.ByteString.Char8 qualified as BS8
+import Prelude hiding (appendFile, readFile, writeFile)
 import System.IO (Handle)
 
 import Effectful
 import Effectful.Dispatch.Static
 import Effectful.FileSystem
+
+#if MIN_VERSION_bytestring(0,11,2)
+import Data.ByteString qualified as BS
+#endif
 
 ----------------------------------------
 -- Introducing and eliminating ByteStrings
diff --git a/src/Effectful/FileSystem/IO/ByteString/Builder.hs b/src/Effectful/FileSystem/IO/ByteString/Builder.hs
--- a/src/Effectful/FileSystem/IO/ByteString/Builder.hs
+++ b/src/Effectful/FileSystem/IO/ByteString/Builder.hs
@@ -5,8 +5,8 @@
 -- to avoid name clashes with the functions provided by "Prelude", e.g.:
 --
 -- > import Data.ByteString.Builder (Builder)
--- > import qualified Data.ByteString.Builder as BSB
--- > import qualified Effectful.FileSystem.IO.ByteString.Builder as EBSB
+-- > import Data.ByteString.Builder qualified as BSB
+-- > import Effectful.FileSystem.IO.ByteString.Builder qualified as EBSB
 --
 module Effectful.FileSystem.IO.ByteString.Builder
   ( -- * Executing Builders
@@ -16,10 +16,9 @@
 #endif
   ) where
 
-import Prelude hiding (writeFile)
-
 import Data.ByteString.Builder (Builder)
-import qualified Data.ByteString.Builder as BSB
+import Data.ByteString.Builder qualified as BSB
+import Prelude hiding (writeFile)
 import System.IO (Handle)
 
 import Effectful
diff --git a/src/Effectful/FileSystem/IO/ByteString/Lazy.hs b/src/Effectful/FileSystem/IO/ByteString/Lazy.hs
--- a/src/Effectful/FileSystem/IO/ByteString/Lazy.hs
+++ b/src/Effectful/FileSystem/IO/ByteString/Lazy.hs
@@ -3,8 +3,8 @@
 -- to avoid name clashes with the functions provided by "Prelude", e.g.:
 --
 -- > import Data.ByteString.Lazy (ByteString)
--- > import qualified Data.ByteString.Lazy.Char8 as LBS8
--- > import qualified Effectful.FileSystem.IO.ByteString.Lazy as ELBS
+-- > import Data.ByteString.Lazy.Char8 qualified as LBS8
+-- > import Effectful.FileSystem.IO.ByteString.Lazy qualified as ELBS
 --
 module Effectful.FileSystem.IO.ByteString.Lazy
   ( -- * Files
@@ -29,7 +29,7 @@
   )
 
 import Data.ByteString.Lazy (ByteString)
-import qualified Data.ByteString.Lazy.Char8 as LBS8
+import Data.ByteString.Lazy.Char8 qualified as LBS8
 import System.IO (Handle)
 
 import Effectful
diff --git a/src/Effectful/FileSystem/IO/File.hs b/src/Effectful/FileSystem/IO/File.hs
--- a/src/Effectful/FileSystem/IO/File.hs
+++ b/src/Effectful/FileSystem/IO/File.hs
@@ -15,7 +15,7 @@
 
 import Data.ByteString (ByteString)
 import System.IO (Handle, IOMode (..))
-import qualified UnliftIO.IO.File as U
+import UnliftIO.IO.File qualified as U
 
 import Effectful
 import Effectful.Dispatch.Static
diff --git a/src/Effectful/Prim/IORef.hs b/src/Effectful/Prim/IORef.hs
new file mode 100644
--- /dev/null
+++ b/src/Effectful/Prim/IORef.hs
@@ -0,0 +1,79 @@
+-- | Lifted "Data.IORef".
+--
+-- /Note:/ it requires 'Prim' because @MutVar@ from the @primitive@ library is a
+-- generalization of 'IORef'.
+--
+-- @since 2.4.0.0
+module Effectful.Prim.IORef
+  ( -- * Effect
+    Prim
+
+    -- ** Handlers
+  , runPrim
+
+    -- * IORef
+  , IORef
+  , newIORef
+  , readIORef
+  , writeIORef
+  , modifyIORef
+  , modifyIORef'
+  , atomicModifyIORef
+  , atomicModifyIORef'
+  , atomicWriteIORef
+  , mkWeakIORef
+  ) where
+
+import Data.IORef (IORef)
+import Data.IORef qualified as Ref
+import System.Mem.Weak (Weak)
+
+import Effectful
+import Effectful.Dispatch.Static
+import Effectful.Dispatch.Static.Primitive
+import Effectful.Prim
+
+-- | Lifted 'Ref.newIORef'.
+newIORef :: Prim :> es => a -> Eff es (IORef a)
+newIORef = unsafeEff_ . Ref.newIORef
+
+-- | Lifted 'Ref.readIORef'.
+readIORef :: Prim :> es => IORef a -> Eff es a
+readIORef = unsafeEff_ . Ref.readIORef
+
+-- | Lifted 'Ref.writeIORef'.
+writeIORef :: Prim :> es => IORef a -> a -> Eff es ()
+writeIORef var = unsafeEff_ . Ref.writeIORef var
+
+-- | Lifted 'Ref.modifyIORef'.
+modifyIORef :: Prim :> es => IORef a -> (a -> a) -> Eff es ()
+modifyIORef var = unsafeEff_ . Ref.modifyIORef var
+
+-- | Lifted 'Ref.modifyIORef''.
+modifyIORef' :: Prim :> es => IORef a -> (a -> a) -> Eff es ()
+modifyIORef' var = unsafeEff_ . Ref.modifyIORef' var
+
+-- | Lifted 'Ref.atomicModifyIORef'.
+atomicModifyIORef :: Prim :> es => IORef a -> (a -> (a, b)) -> Eff es b
+atomicModifyIORef var = unsafeEff_ . Ref.atomicModifyIORef var
+
+-- | Lifted 'Ref.atomicModifyIORef''.
+atomicModifyIORef' :: Prim :> es => IORef a -> (a -> (a, b)) -> Eff es b
+atomicModifyIORef' var = unsafeEff_ . Ref.atomicModifyIORef' var
+
+-- | Lifted 'Ref.atomicWriteIORef''.
+atomicWriteIORef :: Prim :> es => IORef a -> a -> Eff es ()
+atomicWriteIORef var = unsafeEff_ . Ref.atomicWriteIORef var
+
+-- | Lifted 'Ref.mkWeakIORef'.
+--
+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes
+-- to thread local data will not be visible outside of it.
+mkWeakIORef
+  :: (HasCallStack, Prim :> es)
+  => IORef a
+  -> Eff es ()
+  -> Eff es (Weak (IORef a))
+mkWeakIORef var f = unsafeEff $ \es -> do
+  -- The finalizer can run at any point and in any thread.
+  Ref.mkWeakIORef var . unEff f =<< cloneEnv es
diff --git a/src/Effectful/Prim/IORef/Strict.hs b/src/Effectful/Prim/IORef/Strict.hs
new file mode 100644
--- /dev/null
+++ b/src/Effectful/Prim/IORef/Strict.hs
@@ -0,0 +1,66 @@
+-- | Lifted "Data.IORef.Strict".
+--
+-- @since 2.4.0.0
+module Effectful.Prim.IORef.Strict
+  ( -- * Effect
+    Prim
+
+    -- ** Handlers
+  , runPrim
+
+    -- * IORef
+  , IORef'
+  , newIORef'
+  , readIORef'
+  , writeIORef'
+  , modifyIORef'
+  , atomicModifyIORef'
+  , atomicWriteIORef'
+  , mkWeakIORef'
+  ) where
+
+import Data.IORef.Strict (IORef')
+import Data.IORef.Strict qualified as Ref
+import System.Mem.Weak (Weak)
+
+import Effectful
+import Effectful.Dispatch.Static
+import Effectful.Dispatch.Static.Primitive
+import Effectful.Prim
+
+-- | Lifted 'Ref.newIORef''.
+newIORef' :: Prim :> es => a -> Eff es (IORef' a)
+newIORef' = unsafeEff_ . Ref.newIORef'
+
+-- | Lifted 'Ref.readIORef''.
+readIORef' :: Prim :> es => IORef' a -> Eff es a
+readIORef' = unsafeEff_ . Ref.readIORef'
+
+-- | Lifted 'Ref.writeIORef''.
+writeIORef' :: Prim :> es => IORef' a -> a -> Eff es ()
+writeIORef' var = unsafeEff_ . Ref.writeIORef' var
+
+-- | Lifted 'Ref.modifyIORef''.
+modifyIORef' :: Prim :> es => IORef' a -> (a -> a) -> Eff es ()
+modifyIORef' var = unsafeEff_ . Ref.modifyIORef' var
+
+-- | Lifted 'Ref.atomicModifyIORef''.
+atomicModifyIORef' :: Prim :> es => IORef' a -> (a -> (a, b)) -> Eff es b
+atomicModifyIORef' var = unsafeEff_ . Ref.atomicModifyIORef' var
+
+-- | Lifted 'Ref.atomicWriteIORef''.
+atomicWriteIORef' :: Prim :> es => IORef' a -> a -> Eff es ()
+atomicWriteIORef' var = unsafeEff_ . Ref.atomicWriteIORef' var
+
+-- | Lifted 'Ref.mkWeakIORef''.
+--
+-- /Note:/ the finalizer will run a cloned environment, so any changes it makes
+-- to thread local data will not be visible outside of it.
+mkWeakIORef'
+  :: (HasCallStack, Prim :> es)
+  => IORef' a
+  -> Eff es ()
+  -> Eff es (Weak (IORef' a))
+mkWeakIORef' var f = unsafeEff $ \es -> do
+  -- The finalizer can run at any point and in any thread.
+  Ref.mkWeakIORef' var . unEff f =<< cloneEnv es
diff --git a/src/Effectful/Process.hs b/src/Effectful/Process.hs
--- a/src/Effectful/Process.hs
+++ b/src/Effectful/Process.hs
@@ -51,7 +51,7 @@
 import System.Exit (ExitCode)
 import System.IO (Handle)
 import System.Posix.Internals (FD)
-import qualified System.Process as P
+import System.Process qualified as P
 
 import Effectful
 import Effectful.Dispatch.Static
@@ -62,7 +62,7 @@
 type instance DispatchOf Process = Static WithSideEffects
 data instance StaticRep Process = Process
 
-runProcess :: IOE :> es => Eff (Process : es) a -> Eff es a
+runProcess :: (HasCallStack, IOE :> es) => Eff (Process : es) a -> Eff es a
 runProcess = evalStaticRep Process
 
 ----------------------------------------
diff --git a/src/Effectful/Temporary.hs b/src/Effectful/Temporary.hs
--- a/src/Effectful/Temporary.hs
+++ b/src/Effectful/Temporary.hs
@@ -13,7 +13,7 @@
   ) where
 
 import System.IO
-import qualified UnliftIO.Temporary as T
+import UnliftIO.Temporary qualified as T
 
 import Effectful
 import Effectful.Dispatch.Static
@@ -25,7 +25,7 @@
 data instance StaticRep Temporary = Temporary
 
 -- | Run the 'Temporary' effect.
-runTemporary :: IOE :> es => Eff (Temporary : es) a -> Eff es a
+runTemporary :: (HasCallStack, IOE :> es) => Eff (Temporary : es) a -> Eff es a
 runTemporary = evalStaticRep Temporary
 
 -- | Lifted 'T.withSystemTempFile'.
diff --git a/src/Effectful/Timeout.hs b/src/Effectful/Timeout.hs
--- a/src/Effectful/Timeout.hs
+++ b/src/Effectful/Timeout.hs
@@ -9,7 +9,7 @@
   , timeout
   ) where
 
-import qualified System.Timeout as T
+import System.Timeout qualified as T
 
 import Effectful
 import Effectful.Dispatch.Static
@@ -21,7 +21,7 @@
 data instance StaticRep Timeout = Timeout
 
 -- | Run the 'Timeout' effect.
-runTimeout :: IOE :> es => Eff (Timeout : es) a -> Eff es a
+runTimeout :: (HasCallStack, IOE :> es) => Eff (Timeout : es) a -> Eff es a
 runTimeout = evalStaticRep Timeout
 
 -- | Lifted 'T.timeout'.
diff --git a/tests/AsyncTests.hs b/tests/AsyncTests.hs
--- a/tests/AsyncTests.hs
+++ b/tests/AsyncTests.hs
@@ -2,16 +2,16 @@
 
 import Control.Concurrent (threadDelay)
 import Control.Monad
+import Data.Set qualified as S
 import Test.Tasty
 import Test.Tasty.HUnit
-import qualified Data.Set as S
 
 import Effectful
 import Effectful.Concurrent.Async
 import Effectful.Error.Static
 import Effectful.State.Dynamic
 
-import qualified Utils as U
+import Utils qualified as U
 
 asyncTests :: TestTree
 asyncTests = testGroup "Async"
diff --git a/tests/ConcurrencyTests.hs b/tests/ConcurrencyTests.hs
--- a/tests/ConcurrencyTests.hs
+++ b/tests/ConcurrencyTests.hs
@@ -2,15 +2,17 @@
 
 import Control.Concurrent (threadDelay)
 import Control.Monad
+import Data.Set qualified as S
 import Test.Tasty
 import Test.Tasty.HUnit
 import UnliftIO
-import qualified Data.Set as S
 
 import Effectful
+import Effectful.Concurrent.Async qualified as E
+import Effectful.Dispatch.Dynamic
 import Effectful.Error.Static
 import Effectful.State.Dynamic
-import qualified Utils as U
+import Utils qualified as U
 
 concurrencyTests :: TestTree
 concurrencyTests = testGroup "Concurrency"
@@ -20,6 +22,8 @@
   , testCase "unlifting several times" test_unliftMany
   , testCase "async with unmask" test_asyncWithUnmask
   , testCase "pooled workers" test_pooledWorkers
+  , testCase "using local unlift correctly works" test_correctLocalUnlift
+  , testCase "using local unlift incorrectly doesn't work" test_wrongLocalUnlift
   ]
 
 test_localState :: Assertion
@@ -119,3 +123,34 @@
   where
     n = 10
     threads = 4
+
+test_correctLocalUnlift :: Assertion
+test_correctLocalUnlift = runEff . E.runConcurrent $ do
+  x <- runFork . send . RunAsyncCorrect $ pure ()
+  E.wait x
+
+test_wrongLocalUnlift :: Assertion
+test_wrongLocalUnlift = runEff . E.runConcurrent $ do
+  U.assertThrowsErrorCall "invalid LocalEnv use" $ do
+    x <- runFork . send . RunAsyncWrong $ pure ()
+    E.wait x
+
+data Fork :: Effect where
+  RunAsyncCorrect :: m a -> Fork m (E.Async a)
+  RunAsyncWrong :: m a -> Fork m (E.Async a)
+type instance DispatchOf Fork = Dynamic
+
+runFork :: (IOE :> es, E.Concurrent :> es) => Eff (Fork : es) a -> Eff es a
+runFork = interpret $ \env -> \case
+  RunAsyncCorrect action -> do
+    -- LocalEnv is correctly used in the thread in belongs to, so creation of
+    -- the unlifting function should succeed.
+    localUnlift env strategy $ \unlift -> do
+      E.async $ unlift action
+  RunAsyncWrong action -> E.async $ do
+    -- LocalEnv is incorrectly passed to a different thread, so creation of the
+    -- unlifting function should fail.
+    localUnlift env strategy $ \unlift -> do
+      unlift action
+  where
+    strategy = ConcUnlift Ephemeral $ Limited 1
diff --git a/tests/EnvTests.hs b/tests/EnvTests.hs
--- a/tests/EnvTests.hs
+++ b/tests/EnvTests.hs
@@ -12,7 +12,7 @@
 import Effectful.Reader.Static
 import Effectful.Provider
 import Effectful.State.Static.Local
-import qualified Utils as U
+import Utils qualified as U
 
 envTests :: TestTree
 envTests = testGroup "Env"
@@ -22,6 +22,7 @@
   , testCase "unsafeCoerce doesn't work" test_noUnsafeCoerce
   , testCase "interpose works" test_interpose
   , testCase "interpose/provider works" test_interposeProvider
+  , testCase "borrow/lend works" test_borrowLend
   ]
 
 test_tailEnv :: Assertion
@@ -103,8 +104,6 @@
 test_noUnsafeCoerce = do
   r1 <- try @ErrorCall . evaluate $ unsafeCoerce1 @Int 'a'
   assertBool "unsafeCoerce1" (isLeft r1)
-  r2 <- try @ErrorCall . evaluate $ unsafeCoerce2 @Int 'a'
-  assertBool "unsafeCoerce2" (isLeft r2)
 
 unsafeCoerce1 :: forall b a. a -> b
 unsafeCoerce1 a = runPureEff $ do
@@ -121,26 +120,6 @@
   Box b <- runReader (Box a) $ raise oops
   pure b
 
-unsafeCoerce2 :: forall b a. a -> b
-unsafeCoerce2 a = runPureEff $ do
-  backupEs <- unsafeEff cloneEnv
-  -- 'oops' gains access to the effect stack with Reader (Box b) via the
-  -- unlifting function that escaped its scope. The problem here is that this
-  -- effect is no longer in scope.
-  oops <- runReader @(Box b) (Box undefined) $ do
-    raiseWith SeqUnlift $ \unlift -> do
-      pure . unlift $ ask @(Box b)
-  -- If restoreEnv messes up versioning (i.e. restores the version counter from
-  -- before the above Reader was used), below code will succeed because the
-  -- Reader (Box a) will have the same version as Reader (Box b).
-  unsafeEff $ \es -> restoreEnv es backupEs
-  -- Put Reader (Box a) where the Reader (Box b) was before and attempt to
-  -- retrieve 'a' coerced to 'b'. It should fail because 'getLocation' in
-  -- 'Effectful.Internal.Env' checks that version of the reference is the same
-  -- as version of the effect.
-  Box b <- runReader (Box a) $ raise oops
-  pure b
-
 data Box a = Box a
 
 ----------------------------------------
@@ -191,11 +170,11 @@
 type instance DispatchOf A = Dynamic
 
 runA :: Int -> Eff (A : es) a -> Eff es a
-runA n = interpret $ \_ -> \case
+runA n = interpret_ $ \case
   A -> pure n
 
 doubleA :: A :> es => Eff es a -> Eff es a
-doubleA = interpose $ \_ -> \case
+doubleA = interpose_ $ \case
   A -> (+) <$> send A <*> send A
 
 data B :: Effect where
@@ -203,9 +182,41 @@
 type instance DispatchOf B = Dynamic
 
 runB :: A :> es => Eff (B : es) a -> Eff es a
-runB = interpret $ \_ -> \case
+runB = interpret_ $ \case
   B -> send A
 
 doubleB :: B :> es => Eff es a -> Eff es a
-doubleB = interpose $ \_ -> \case
+doubleB = interpose_ $ \case
   B -> (+) <$> send B <*> send B
+
+----------------------------------------
+
+test_borrowLend :: Assertion
+test_borrowLend = runEff $ do
+  runX 1 2 . evalState @[Int] [3] . runReader () . runReader @[Int] [4] $ do
+    U.assertEqual "expected result" [1,2,3,4,1,2,3,4] =<< send X
+
+data X :: Effect where
+  X :: (State [Int] :> es, Reader [Int] :> es, Reader () :> es) => X (Eff es) [Int]
+type instance DispatchOf X = Dynamic
+
+runX :: Int -> Int -> Eff (X : es) a -> Eff es a
+runX s0 r0 = reinterpret (evalState s0 . evalState () . runReader r0) $ \env -> \case
+  X -> localSeqUnlift env $ \unlift -> do
+    as <- localSeqLend @[State Int, Reader Int] env $ \withHandlerEffs -> do
+      unlift . withHandlerEffs $ do
+        () <- ask
+        s <- get @Int
+        r <- ask @Int
+        ss <- get @[Int]
+        rs <- ask @[Int]
+        pure $ [s, r] ++ ss ++ rs
+    bs <- localSeqBorrow @[Reader [Int], State [Int], Reader ()] env $ \withEffs -> do
+      withEffs $ do
+        () <- ask
+        s <- get @Int
+        r <- ask @Int
+        ss <- get @[Int]
+        rs <- ask @[Int]
+        pure $ [s, r] ++ ss ++ rs
+    pure $ as ++ bs
diff --git a/tests/EnvironmentTests.hs b/tests/EnvironmentTests.hs
--- a/tests/EnvironmentTests.hs
+++ b/tests/EnvironmentTests.hs
@@ -6,7 +6,7 @@
 
 import Effectful
 import Effectful.Environment
-import qualified Utils as U
+import Utils qualified as U
 
 environmentTests :: TestTree
 environmentTests = testCaseSteps "Environment" $ \step -> do
diff --git a/tests/ErrorTests.hs b/tests/ErrorTests.hs
--- a/tests/ErrorTests.hs
+++ b/tests/ErrorTests.hs
@@ -5,11 +5,13 @@
 
 import Effectful
 import Effectful.Dispatch.Dynamic
+import Effectful.Error.Dynamic qualified as D
 import Effectful.Error.Static
 
 errorTests :: TestTree
 errorTests = testGroup "Error"
   [ testCase "different handlers are independent" test_independentHandlers
+  , testCase "call stack of dynamic throwError doesn't show internal details" test_dynamicThrowErrorCallStack
   ]
 
 test_independentHandlers :: Assertion
@@ -21,6 +23,13 @@
       "outerThrow" == fst (last $ getCallStack cs)
     Right _ -> assertFailure "error caught by the wrong (inner) handler"
 
+test_dynamicThrowErrorCallStack :: Assertion
+test_dynamicThrowErrorCallStack = do
+  Left (cs, ()) <- runEff . D.runError @() $ D.throwError ()
+  case getCallStack cs of
+    [("throwError", _)] -> pure ()
+    _ -> assertFailure $ "invalid call stack: " ++ prettyCallStack cs
+
 ----------------------------------------
 -- Helpers
 
@@ -33,5 +42,5 @@
 outerThrow = send OuterThrow
 
 runOuterThrow :: Error String :> es => Eff (OuterThrow : es) a -> Eff es a
-runOuterThrow = interpret $ \_ -> \case
+runOuterThrow = interpret_ $ \case
   OuterThrow -> throwError "outer"
diff --git a/tests/LabeledTests.hs b/tests/LabeledTests.hs
new file mode 100644
--- /dev/null
+++ b/tests/LabeledTests.hs
@@ -0,0 +1,61 @@
+module LabeledTests (labeledTests) where
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+import Effectful
+import Effectful.Dispatch.Dynamic
+import Effectful.Labeled
+import Effectful.Reader.Static
+import Utils qualified as U
+
+labeledTests :: TestTree
+labeledTests = testGroup "Labeled"
+  [ testCase "labeled behaves correctly" $ test_labeledBehavior
+  , testCase "(labeled . send) and (send . Labeled) behave the same" $ test_labeledSend
+  ]
+
+test_labeledBehavior :: Assertion
+test_labeledBehavior = do
+  v <- runEff
+    . runLabeled @"a" (runReader "a")
+    . runLabeled @"b" (runReader "b")
+    . runReader "c"
+    $ action
+  assertEqual "expected result" "abc" v
+  where
+    action
+      :: ( Labeled "a" (Reader String) :> es
+         , Labeled "b" (Reader String) :> es
+         , Reader String :> es
+         )
+      => Eff es String
+    action = do
+      a <- labeled @"b" @(Reader String) $ do
+        labeled @"a" @(Reader String) $ do
+          ask
+      b <- labeled @"b" @(Reader String) $ do
+        ask
+      c <- ask
+      pure $ a ++ b ++ c
+
+test_labeledSend :: Assertion
+test_labeledSend = runEff $ do
+  runX 1 . runLabeled @"x" (runX 2) $ do
+    v0 <- send X2
+    U.assertEqual "expected result" 1 v0
+    v1 <- (labeled @"x" @X . send) X2
+    U.assertEqual "expected result" 2 v1
+    v2 <- (send . Labeled @"x") X2
+    U.assertEqual "expected result" 2 v2
+
+data X :: Effect where
+  X1 :: X m Int
+  X2 :: (X :> es, Labeled "x" X :> es) => X (Eff es) Int
+
+type instance DispatchOf X = Dynamic
+
+runX :: Int -> Eff (X : es) a -> Eff es a
+runX x = interpret $ \env -> \case
+  X1 -> pure x
+  X2 -> localSeqUnlift env $ \unlift -> unlift $ send X1
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -7,6 +7,7 @@
 import EnvTests
 import EnvironmentTests
 import ErrorTests
+import LabeledTests
 import NonDetTests
 import PrimTests
 import ReaderTests
@@ -21,6 +22,7 @@
   , envTests
   , environmentTests
   , errorTests
+  , labeledTests
   , nonDetTests
   , primTests
   , readerTests
diff --git a/tests/NonDetTests.hs b/tests/NonDetTests.hs
--- a/tests/NonDetTests.hs
+++ b/tests/NonDetTests.hs
@@ -9,7 +9,7 @@
 import Effectful.Reader.Static
 import Effectful.State.Dynamic
 
-import qualified Utils as U
+import Utils qualified as U
 
 nonDetTests :: TestTree
 nonDetTests = testGroup "NonDet"
@@ -30,11 +30,11 @@
 
     expectedLocalState :: OnEmptyPolicy -> Int
     expectedLocalState = \case
-      OnEmptyKeep     -> 3
-      OnEmptyRollback -> 2
+      OnEmptyKeep     -> 7
+      OnEmptyRollback -> 0
 
     expectedSharedState :: OnEmptyPolicy -> Int
-    expectedSharedState _ = 3
+    expectedSharedState _ = 7
 
 test_empty
   :: Eff [NonDet, IOE] Bool
@@ -53,12 +53,15 @@
 test_state evalState expectedState step = runEff $ do
   evalState 0 . runNonDetBoth test $ \policy result -> do
     liftIO . step $ show policy
-    U.assertEqual "result" (Just ()) (dropLeft result)
+    U.assertEqual "result" Nothing (dropLeft result)
     s <- state @Int $ \s -> (s, 0)
     U.assertEqual "state" (expectedState policy) s
   where
     test :: (NonDet :> es, State Int :> es) => Eff es ()
-    test = (modify @Int (+1) >> empty) <|> modify @Int (+2)
+    test = do
+      modify @Int (+1)
+      _<- (modify @Int (+2) >> empty) <|> (modify @Int (+4) >> empty)
+      modify @Int (+8)
 
 test_independentHandlers :: (String -> IO ()) -> Assertion
 test_independentHandlers step = runEff $ do
@@ -84,7 +87,7 @@
 outerEmpty = send OuterEmpty
 
 runOuterEmpty :: NonDet :> es => Eff (OuterEmpty : es) a -> Eff es a
-runOuterEmpty = interpret $ \_ -> \case
+runOuterEmpty = interpret_ $ \case
   OuterEmpty -> emptyEff
 
 ----
diff --git a/tests/PrimTests.hs b/tests/PrimTests.hs
--- a/tests/PrimTests.hs
+++ b/tests/PrimTests.hs
@@ -3,7 +3,7 @@
 import Data.Primitive.MutVar
 import Test.Tasty
 import Test.Tasty.HUnit
-import qualified Utils as U
+import Utils qualified as U
 
 import Effectful
 import Effectful.Prim
diff --git a/tests/ReaderTests.hs b/tests/ReaderTests.hs
--- a/tests/ReaderTests.hs
+++ b/tests/ReaderTests.hs
@@ -5,13 +5,14 @@
 
 import Effectful
 import Effectful.Dispatch.Dynamic
-import Effectful.Reader.Dynamic
-import qualified Utils as U
+import Effectful.Reader.Dynamic qualified as D
+import Effectful.Reader.Static qualified as S
+import Utils qualified as U
 
 readerTests :: TestTree
 readerTests = testGroup "Reader"
-  [ testCase "local works in handlers (dynamic/static)" $ test_localInHandler runReader
-  , testCase "local works in handlers (dynamic/pure)" $ test_localInHandler runPureReader
+  [ testCase "local works in handlers (static)" test_localInHandlerStatic
+  , testCase "local works in handlers (dynamic)" test_localInHandlerDynamic
   ]
 
 data SomeEff :: Effect where
@@ -19,21 +20,16 @@
 
 type instance DispatchOf SomeEff = Dynamic
 
-test_localInHandler
-  :: (forall r es a. r -> Eff (Reader r : es) a -> Eff es a)
-  -> Assertion
-test_localInHandler runR = runEff . runR "global" . interpret f $ do
-  local (const "local") $ send SomeAction
+test_localInHandlerStatic :: Assertion
+test_localInHandlerStatic = runEff . S.runReader "global" . interpret f $ do
+  S.local (const "local") $ send SomeAction
   where
-    f :: (IOE :> es, Reader String :> es) => EffectHandler SomeEff es
-    f _ SomeAction = U.assertEqual "expected result" "local" =<< ask
+    f :: (IOE :> es, S.Reader String :> es) => EffectHandler SomeEff es
+    f _ SomeAction = U.assertEqual "expected result" "local" =<< S.ask
 
--- | Purely dynamic Reader for testing purposes.
-runPureReader :: r -> Eff (Reader r : es) a -> Eff es a
-runPureReader r0 = interpret (handler r0)
+test_localInHandlerDynamic :: Assertion
+test_localInHandlerDynamic = runEff . D.runReader "global" . interpret f $ do
+  D.local (const "local") $ send SomeAction
   where
-    handler :: r -> EffectHandler (Reader r) handlerEs
-    handler r env = \case
-      Ask       -> pure r
-      Local f m -> localSeqUnlift env $ \unlift -> do
-        unlift $ interpose (handler $ f r) m
+    f :: (IOE :> es, D.Reader String :> es) => EffectHandler SomeEff es
+    f _ SomeAction = U.assertEqual "expected result" "local" =<< D.ask
diff --git a/tests/StateTests.hs b/tests/StateTests.hs
--- a/tests/StateTests.hs
+++ b/tests/StateTests.hs
@@ -1,15 +1,20 @@
 module StateTests (stateTests) where
 
+import Control.Exception.Lifted qualified as LE
+import Control.Exception.Safe qualified as Safe
+import Control.Monad
+import Control.Monad.Catch qualified as C
+import Data.IORef.Strict
 import Test.Tasty
 import Test.Tasty.HUnit
-import qualified Control.Monad.Catch as E
-import qualified Control.Exception.Lifted as LE
-import qualified UnliftIO.Exception as UE
+import UnliftIO.Exception qualified as UE
 
 import Effectful
 import Effectful.Dispatch.Dynamic
+import Effectful.Dispatch.Static
+import Effectful.Internal.Env
 import Effectful.State.Static.Local
-import qualified Utils as U
+import Utils qualified as U
 
 stateTests :: TestTree
 stateTests = testGroup "State"
@@ -34,9 +39,19 @@
 
 test_stateM :: Assertion
 test_stateM = runEff $ do
-  (a, b) <- runState "hi" . stateM $ \s -> pure (s, s ++ "!!!")
+  (a, b) <- runState "hi" $ do
+    stateM $ \s -> do
+      effs0 <- getEffectReps
+      -- Trigger reallocation of the internal array in Storage.
+      _ <- evalState () $ pure ()
+      effs1 <- getEffectReps
+      when (effs0 == effs1) $ do
+        U.assertFailure "Internal array was not reallocated"
+      pure (s, s ++ "!!!")
   U.assertEqual "correct a" "hi"    a
   U.assertEqual "correct b" "hi!!!" b
+  where
+    getEffectReps = unsafeEff $ \es -> sdEffects . stData <$> readIORef' (envStorage es)
 
 test_deepStack :: Assertion
 test_deepStack = runEff $ do
@@ -52,12 +67,14 @@
 
 test_exceptions :: Assertion
 test_exceptions = runEff $ do
-  testTry   "exceptions"  E.try
-  testCatch "exceptions"  E.catch
-  testTry   "lifted-base" LE.try
-  testCatch "lifted-base" LE.catch
-  testTry   "unliftio"    UE.try
-  testCatch "unliftio"    UE.catch
+  testTry   "exceptions"      C.try
+  testCatch "exceptions"      C.catch
+  testTry   "safe-exceptions" Safe.try
+  testCatch "safe-exceptions" Safe.catch
+  testTry   "lifted-base"     LE.try
+  testCatch "lifted-base"     LE.catch
+  testTry   "unliftio"        UE.try
+  testCatch "unliftio"        UE.catch
   where
     testTry
       :: String
@@ -82,7 +99,7 @@
     action :: State Int :> es => Eff es ()
     action = do
       modify @Int (+1)
-      _ <- E.throwM U.Ex
+      _ <- C.throwM U.Ex
       modify @Int (+2)
 
 test_localEffects :: Assertion
@@ -117,7 +134,7 @@
 runHasInt :: Int -> Eff (HasInt : es) a -> Eff es a
 runHasInt n =
   -- reinterpret with redundant local effects
-  reinterpret (evalState () . evalState n . evalState True) $ \_ -> \case
+  reinterpret_ (evalState () . evalState n . evalState True) $ \case
     GetInt   -> get
     PutInt i -> put i
 
diff --git a/tests/TimeoutTests.hs b/tests/TimeoutTests.hs
--- a/tests/TimeoutTests.hs
+++ b/tests/TimeoutTests.hs
@@ -6,7 +6,7 @@
 
 import Effectful
 import Effectful.Timeout
-import qualified Utils as U
+import Utils qualified as U
 
 timeoutTests :: TestTree
 timeoutTests = testGroup "Timeout"
diff --git a/tests/UnliftTests.hs b/tests/UnliftTests.hs
--- a/tests/UnliftTests.hs
+++ b/tests/UnliftTests.hs
@@ -1,12 +1,14 @@
 module UnliftTests (unliftTests) where
 
-import Control.Exception
 import Test.Tasty
 import Test.Tasty.HUnit
-import qualified UnliftIO.Async as A
+import UnliftIO.Async qualified as A
 
 import Effectful
-import qualified Utils as U
+import Effectful.Concurrent.Async qualified as E
+import Effectful.Dispatch.Dynamic
+import Effectful.State.Static.Local
+import Utils qualified as U
 
 unliftTests :: TestTree
 unliftTests = testGroup "Unlift"
@@ -22,6 +24,7 @@
     , testCase "Uses in same thread" test_persistentSameThread
     , testCase "Uses in multiple threads" test_persistentMultipleThreads
     ]
+  , testCase "Unlifting functions work correctly" test_unliftingFunctions
   ]
 
 test_threadStrategy :: Assertion
@@ -34,32 +37,32 @@
 
 test_seqUnliftInNewThread :: Assertion
 test_seqUnliftInNewThread = runEff $ do
-  assertThrowsUnliftError "InvalidUseOfSeqUnlift error" $ do
+  U.assertThrowsErrorCall "InvalidUseOfSeqUnlift error" $ do
     withEffToIO SeqUnlift $ \runInIO -> do
       inThread $ runInIO $ return ()
 
 test_ephemeralInvalid :: Assertion
 test_ephemeralInvalid = runEff $ do
-  assertThrowsUnliftError "InvalidNumberOfUses error" $ do
+  U.assertThrowsErrorCall "InvalidNumberOfUses error" $ do
     withEffToIO (ConcUnlift Ephemeral $ Limited 0) $ \_ -> return ()
 
 test_ephemeralSameThread :: Assertion
 test_ephemeralSameThread = runEff $ do
-  assertThrowsUnliftError "ExceededNumberOfUses error" $ do
+  U.assertThrowsErrorCall "ExceededNumberOfUses error" $ do
     withEffToIO (ConcUnlift Ephemeral $ Limited 1) $ \runInIO -> inThread $ do
       runInIO $ return ()
       runInIO $ return ()
 
 test_ephemeralMultipleThreads :: Assertion
 test_ephemeralMultipleThreads = runEff $ do
-  assertThrowsUnliftError "ExceededNumberOfUses error" $ do
+  U.assertThrowsErrorCall "ExceededNumberOfUses error" $ do
     withEffToIO (ConcUnlift Ephemeral $ Limited 1) $ \runInIO -> do
       inThread $ runInIO $ return ()
       inThread $ runInIO $ return ()
 
 test_persistentInvalid :: Assertion
 test_persistentInvalid = runEff $ do
-  assertThrowsUnliftError "InvalidNumberOfThreads error" $ do
+  U.assertThrowsErrorCall "InvalidNumberOfThreads error" $ do
     withEffToIO (ConcUnlift Persistent $ Limited 0) $ \_ -> return ()
 
 test_persistentSameThread :: Assertion
@@ -70,18 +73,85 @@
 
 test_persistentMultipleThreads :: Assertion
 test_persistentMultipleThreads = runEff $ do
-  assertThrowsUnliftError "ExceededNumberOfThreads error" $ do
+  U.assertThrowsErrorCall "ExceededNumberOfThreads error" $ do
     withEffToIO (ConcUnlift Persistent $ Limited 1) $ \runInIO -> do
       inThread $ runInIO $ return ()
       inThread $ runInIO $ return ()
 
+test_unliftingFunctions :: Assertion
+test_unliftingFunctions = runEff . E.runConcurrent $ do
+  testFork "runFork1" runFork1
+  testFork "runFork2" runFork2
+  testFork "runFork3" runFork3
+  testFork "runFork4" runFork4
+  testFork "runFork5" runFork5
+  where
+    testFork description runFork = do
+      a <- runFork . send $ ForkWithUnmask $ \unmask -> do
+        evalState @Int 0 $ raiseWith SeqUnlift $ \unlift -> do
+          unlift $ modify @Int (+1)
+          unmask . unlift $ modify @Int (+2)
+          unlift $ modify @Int (+4)
+          unmask . unlift $ modify @Int (+8)
+          unlift $ U.assertEqual (description ++ ": correct state") 15 =<< get @Int
+      E.waitCatch a >>= \case
+        Right () -> pure ()
+        Left err -> U.assertFailure $ description ++ ": " ++ show err
+
+data Fork :: Effect where
+  ForkWithUnmask :: ((forall a. m a -> m a) -> m r) -> Fork m (A.Async r)
+type instance DispatchOf Fork = Dynamic
+
+-- | Uses 'localUnliftIO' and 'withLiftMapIO'.
+runFork1 :: IOE :> es => Eff (Fork : es) a -> Eff es a
+runFork1 = interpret $ \env -> \case
+  ForkWithUnmask m -> do
+    withLiftMapIO env $ \liftMap -> do
+      localUnliftIO env strategy $ \unlift -> do
+        A.asyncWithUnmask $ \unmask -> unlift $ m $ liftMap unmask
+  where
+    strategy = ConcUnlift Ephemeral $ Limited 1
+
+-- | Uses 'localUnlift' and 'withLiftMap'.
+runFork2 :: (IOE :> es, E.Concurrent :> es) => Eff (Fork : es) a -> Eff es a
+runFork2 = interpret $ \env -> \case
+  ForkWithUnmask m -> do
+    withLiftMap env $ \liftMap -> do
+      localUnlift env strategy $ \unlift -> do
+        E.asyncWithUnmask $ \unmask -> unlift $ m $ liftMap unmask
+  where
+    strategy = ConcUnlift Ephemeral $ Limited 1
+
+-- | Uses 'localLiftUnliftIO'.
+runFork3 :: IOE :> es => Eff (Fork : es) a -> Eff es a
+runFork3 = interpret $ \env -> \case
+  ForkWithUnmask m -> do
+    localLiftUnliftIO env strategy $ \lift unlift -> do
+      A.asyncWithUnmask $ \unmask -> unlift $ m $ lift . unmask . unlift
+  where
+    strategy = ConcUnlift Persistent $ Limited 1
+
+-- | Uses 'localLiftUnlift'.
+runFork4 :: (IOE :> es, E.Concurrent :> es) => Eff (Fork : es) a -> Eff es a
+runFork4 = interpret $ \env -> \case
+  ForkWithUnmask m -> do
+    localLiftUnlift env strategy $ \lift unlift -> do
+      E.asyncWithUnmask $ \unmask -> unlift $ m $ lift . unmask . unlift
+  where
+    strategy = ConcUnlift Persistent $ Limited 1
+
+-- | Uses 'localLift' and 'localUnlift'.
+runFork5 :: (IOE :> es, E.Concurrent :> es) => Eff (Fork : es) a -> Eff es a
+runFork5 = interpret $ \env -> \case
+  ForkWithUnmask m -> do
+    localLift env strategy $ \lift -> do
+      localUnlift env strategy $ \unlift -> do
+        E.asyncWithUnmask $ \unmask -> unlift $ m $ lift . unmask . unlift
+  where
+    strategy = ConcUnlift Persistent $ Limited 1
+
 ----------------------------------------
 -- Helpers
-
-assertThrowsUnliftError
-  :: IOE :> es
-  => String -> Eff es a -> Eff es ()
-assertThrowsUnliftError err = U.assertThrows err (\ErrorCall{} -> True)
 
 inThread :: IO a -> IO a
 inThread k = A.async k >>= A.wait
diff --git a/tests/Utils.hs b/tests/Utils.hs
--- a/tests/Utils.hs
+++ b/tests/Utils.hs
@@ -3,14 +3,15 @@
   , assertEqual
   , assertFailure
   , assertThrows
+  , assertThrowsErrorCall
   , Ex(..)
   ) where
 
-import Control.Monad.Catch
 import GHC.Stack
-import qualified Test.Tasty.HUnit as T
+import Test.Tasty.HUnit qualified as T
 
 import Effectful
+import Effectful.Exception
 
 assertBool :: (HasCallStack, IOE :> es) => String -> Bool -> Eff es ()
 assertBool msg p = liftIO $ T.assertBool msg p
@@ -37,5 +38,11 @@
   (k >> liftIO (T.assertFailure msg))
   pure
 
-data Ex = Ex deriving (Eq, Show)
+assertThrowsErrorCall
+  :: IOE :> es
+  => String -> Eff es a -> Eff es ()
+assertThrowsErrorCall err = assertThrows err (\ErrorCall{} -> True)
+
+data Ex = Ex
+  deriving stock (Eq, Show)
 instance Exception Ex
