diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,80 @@
+# 0.11.0.0
+
+* Add `Bluefin.Internal.Capability.ThrowCatch`
+
+* Add `ThrowCatch`, `throwCatchThrow`, `throwCatchTry`
+
+* Remove `withScopedException_`
+
+* Add `Bluefin.Internal.DslBuilderEff.runDslBuilderEffMappedArgs`
+
+* Remove `Bluefin.Internal.DslBuilderEffects`
+
+# 0.10.1.0
+
+* Add `runPureEffAsyncSafe` and `runPureEffAsyncSafeRestarting`
+
+* Use `INLINE [0]` on `runDslBuilderEff` for improved performance
+
+# 0.10.0.0
+
+* Make `Ask`, `AskCapability`, `Await`, `JumpTo`, `Modify`, `Request`,
+  `ReturnEarly`, `Tell`, `Throw`, and `Yield` the canonical capability
+  types; `Reader`, `HandleReader`, `Consume`, `Jump`, `State`, `Coroutine`,
+  `EarlyReturn`, `Writer`, `Exception`, and `Stream` are now type synonyms.
+
+* Move `Bluefin.Internal.Pipes` to `bluefin-examples`
+
+* Remove unused failing stubs `connect` and `head'` from
+  `Bluefin.Internal`
+
+* Breaking change: separate `Prim`'s primitive state and capability
+  scope type parameters
+
+# 0.9.2.0
+
+* Bug fix: release `Reader` Vault keys when their handler scope exits.
+
+* Make `Vault`'s `Key` type role representational.  This is
+  technically a PVP violation but since it's fixing a type safety bug
+  we're not going to release a major version for it.
+
+# 0.9.1.0
+
+* Add `yieldToPureList`
+
+# 0.9.0.0
+
+* Add quantified constraint `forall e es. (e <: es) => OneWayCoercible
+  (h e) (h es)` as a superclass of `Handle h`
+
+* Improve performance of `mapHandle` by using `unsafeOneWayCoerce`.
+  This can violate type safety if a `OneWayCoercibleInstance` is
+  invalid, so do not define or use invalid `OneWayCoercible`
+  instances!
+
+# 0.8.2.0
+
+* Improve performance of `Reader` and `DslBuilderEff`
+
+# 0.8.1.0
+
+* Add `trans3D`, `oneWayCoercibleNewtypeHandle`
+
+# 0.8.0.0
+
+* Restrict `Reader` type tag to `Effects`
+
+* Move most of `Bluefin.Internal.Examples`, `zipCoroutines` and
+  `mapStream` to `Bluefin.Examples`
+
+# 0.7.0.0
+
+* Fix `Reader` bug that caused incorrect scoping in
+  `awaitYield`/`connectRequests`/`streamConsume`/`connectCoroutines`
+
+  <https://github.com/tomjaguarpaw/bluefin/issues/98>
+
 # 0.6.0.0
 
 * Changed type of `runEff` to match `runEff_`
diff --git a/bluefin-internal.cabal b/bluefin-internal.cabal
--- a/bluefin-internal.cabal
+++ b/bluefin-internal.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               bluefin-internal
-version:            0.6.0.0
+version:            0.11.0.0
 license:            MIT
 license-file:       LICENSE
 author:             Tom Ellis
@@ -77,27 +77,28 @@
     hs-source-dirs: src
     build-depends:
       async >= 2.2 && < 2.3,
-      base >= 4.14 && < 4.23,
+      base >= 4.15 && < 4.23,
       unliftio-core < 0.3,
       primitive >= 0.8 && < 0.10,
       transformers < 0.7,
       transformers-base < 0.5,
-      monad-control < 1.1
+      monad-control < 1.1,
+      vault >= 0.3 && < 0.4
     exposed-modules:
       Bluefin.Internal,
       Bluefin.Internal.CloneableHandle,
       Bluefin.Internal.DslBuilder,
       Bluefin.Internal.DslBuilderEff,
-      Bluefin.Internal.DslBuilderEffects,
       Bluefin.Internal.Examples,
       Bluefin.Internal.Exception,
+      Bluefin.Internal.Capability.ThrowCatch,
       Bluefin.Internal.Exception.Scoped,
       Bluefin.Internal.GadtEffect,
       Bluefin.Internal.Key,
       Bluefin.Internal.OneWayCoercible,
-      Bluefin.Internal.Pipes,
       Bluefin.Internal.Prim,
-      Bluefin.Internal.System.IO
+      Bluefin.Internal.System.IO,
+      Bluefin.Internal.Vault
 
 test-suite bluefin-test
     import:           defaults
@@ -106,7 +107,10 @@
     hs-source-dirs:   test
     main-is:          Main.hs
     other-modules:    Test.GeneralBracket,
-                      Test.SpecH
+                      Test.RunPureEff,
+                      Test.SpecH,
+                      Test.ThrowCatch
     build-depends:
         base,
+        async,
         bluefin-internal
diff --git a/src/Bluefin/Internal.hs b/src/Bluefin/Internal.hs
--- a/src/Bluefin/Internal.hs
+++ b/src/Bluefin/Internal.hs
@@ -16,13 +16,20 @@
 import Bluefin.Internal.OneWayCoercible
   ( OneWayCoercible (oneWayCoercibleImpl),
     OneWayCoercibleD,
+    OneWayCoercion,
     gOneWayCoercible,
-    oneWayCoerce,
     oneWayCoercible,
+    oneWayCoercion,
+    trans3D,
+    unsafeCoercionOfOneWayCoercion,
+    unsafeOneWayCoerce,
     unsafeOneWayCoercible,
   )
+import Bluefin.Internal.Vault (Vault)
+import Bluefin.Internal.Vault qualified as Vault
+import Control.Concurrent (forkIO, forkIOWithUnmask, killThread, myThreadId, throwTo)
 import Control.Concurrent.Async qualified as Async
-import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
+import Control.Concurrent.MVar (newEmptyMVar, putMVar, readMVar, takeMVar)
 import Control.Exception qualified
 import Control.Monad (forever)
 import Control.Monad.Base (MonadBase (liftBase))
@@ -30,16 +37,20 @@
 import Control.Monad.IO.Class (MonadIO, liftIO)
 import Control.Monad.IO.Unlift (MonadUnliftIO, withRunInIO)
 import Control.Monad.Trans.Control (MonadBaseControl, StM, liftBaseWith, restoreM)
+import Control.Monad.Trans.Reader (ReaderT)
 import Control.Monad.Trans.Reader qualified as Reader
-import Data.Coerce (coerce)
+import Data.Coerce (Coercible, coerce)
 import Data.Foldable (for_)
-import Data.IORef (IORef, newIORef, readIORef, writeIORef)
+import Data.Function (fix)
+import Data.IORef (IORef, modifyIORef', newIORef, readIORef, writeIORef)
 import Data.Kind (Type)
 import Data.Proxy (Proxy (Proxy))
 import Data.Type.Coercion (Coercion (Coercion))
-import GHC.Exts (Any, Proxy#, proxy#)
+import GHC.Exts (Any, Proxy#, keepAlive#, proxy#)
 import GHC.Generics (Generic, M1, Rec1, (:*:))
+import GHC.IO (IO (..))
 import System.IO.Unsafe (unsafePerformIO)
+import System.Mem.Weak (addFinalizer)
 import Unsafe.Coerce (unsafeCoerce)
 import Prelude hiding (drop, head, read, return)
 
@@ -55,14 +66,16 @@
 
 type (:&) = Union
 
-newtype Eff (es :: Effects) a = UnsafeMkEff {unsafeUnEff :: IO a}
+type Env = IORef Vault
+
+newtype Eff (es :: Effects) a = UnsafeMkEff {unsafeUnEff :: Env -> IO a}
   deriving stock (Functor)
-  deriving newtype (Applicative, Monad, MonadFix)
+  deriving (Applicative, Monad, MonadFix) via ReaderT Env IO
 
 type role Eff nominal representational
 
 instance (e <: es) => OneWayCoercible (Eff e) (Eff es) where
-  oneWayCoercibleImpl = oneWayCoercible
+  oneWayCoercibleImpl = unsafeOneWayCoercible
 
 instance (e <: es) => OneWayCoercible (Eff e r) (Eff es r) where
   oneWayCoercibleImpl = oneWayCoercible
@@ -89,7 +102,7 @@
   ((forall r. (forall e1. IOE e1 -> Eff (e1 :& es) r) -> IO r) -> IO a) ->
   IOE e2 ->
   Eff es a
-withEffToIO k io = effIO io (k (\f -> unsafeUnEff (f io)))
+withEffToIO k io = UnsafeMkEff (\env -> k (\f -> unsafeUnEff (f io) env))
 
 withEffToIO' ::
   (e2 <: es) =>
@@ -142,15 +155,24 @@
   Eff es a
 race x y io = do
   r <- withEffToIO' io $ \toIO ->
-    Async.race (toIO x) (toIO y)
+    Async.race (toIO (withClonedEnv . x)) (toIO (withClonedEnv . y))
 
   pure $ case r of
     Left a -> a
     Right a -> a
 
+withClonedEnv :: Eff es r -> Eff es r
+withClonedEnv m = UnsafeMkEff $ \vault -> do
+  vault' <- cloneIORef vault
+  case m of UnsafeMkEff m' -> m' vault'
+  where
+    cloneIORef ref = do
+      orig <- readIORef ref
+      newIORef orig
+
 -- | Connect two coroutines.  Their execution is interleaved by
--- exchanging @a@s and @b@s. When the first yields its first @a@ it
--- starts the second (which is awaiting an @a@).
+-- exchanging @a@s and @b@s. When the former yields its first @a@ it
+-- starts the latter (which is awaiting an @a@).
 connectCoroutines ::
   forall es a b r.
   (forall e. Coroutine a b e -> Eff (e :& es) r) ->
@@ -200,24 +222,6 @@
   Eff es r
 streamConsume s c = consumeStream c s
 
-zipCoroutines ::
-  (e1 <: es) =>
-  Coroutine (a1, a2) b e1 ->
-  (forall e. Coroutine a1 b e -> Eff (e :& es) r) ->
-  (forall e. Coroutine a2 b e -> Eff (e :& es) r) ->
-  -- | ͘
-  Eff es r
-zipCoroutines c m1 m2 = do
-  connectCoroutines m1 $ \a1 c1 -> do
-    connectCoroutines (useImplUnder . m2) $ \a2 c2 -> do
-      evalState (a1, a2) $ \ass -> do
-        forever $ do
-          as <- get ass
-          b' <- yieldCoroutine c as
-          a1' <- yieldCoroutine c1 b'
-          a2' <- yieldCoroutine c2 b'
-          put ass (a1', a2')
-
 instance (e <: es) => MonadBase IO (EffReader (IOE e) es) where
   liftBase = liftIO
 
@@ -226,7 +230,7 @@
   liftBaseWith = withRunInIO
   restoreM = pure
 
-instance (e <: es) => MonadFail (EffReader (Exception String e) es) where
+instance (e <: es) => MonadFail (EffReader (Throw String e) es) where
   fail = MkEffReader . flip throw
 
 hoistReader ::
@@ -267,8 +271,8 @@
 -- `Either String` and then applying `either (throw f) pure`.
 withMonadFail ::
   (e <: es) =>
-  -- | @Exception@ to @throw@ on @fail@
-  Exception String e ->
+  -- | @Throw@ to @throw@ on @fail@
+  Throw String e ->
   -- | 'MonadFail' operation
   (forall m. (MonadFail m) => m r) ->
   -- | @MonadFail@ operation run in @Eff@
@@ -277,13 +281,95 @@
 
 -- | Run an 'Eff' that doesn't contain any unhandled effects.
 runPureEff :: (forall es. Eff es a) -> a
-runPureEff e = unsafePerformIO (runEff (\_ -> e))
+runPureEff = runPureEffPoisonable
 
+-- | Run an 'Eff' that doesn't contain any unhandled effects.  If the
+-- evaluation of the result of runPureEffPoisonable is interrupted by
+-- an asynchronous exception the thunk can become poisoned and unable
+-- to be resumed (subsequent evaluation throws the async exception
+-- again).
+--
+-- See https://github.com/tomjaguarpaw/bluefin/issues/30
+runPureEffPoisonable :: (forall es. Eff es a) -> a
+runPureEffPoisonable e = unsafePerformIO (runEff (\_ -> e))
+
+-- | Run an 'Eff' that doesn't contain any unhandled effects. The
+-- computation runs in a dedicated worker thread, so an asynchronous
+-- exception received by a thread demanding the result does not
+-- interrupt the computation itself. (An exception delivered to the
+-- worker thread would be rethrown and could poison the thunk, but
+-- that cannot happen unless the worker thread's ID is looked up by
+-- some out-of-band means -- don't do that!).
+--
+-- The worker thread continues to work even if the thread forcing it
+-- is killed, which may be surprising. If no references to the thunk
+-- for the result of runPureEffAsyncSafe remain, the worker thread is
+-- killed.
+--
+-- A proper fix to this issue probably belongs in GHC.
+runPureEffAsyncSafe :: (forall es. Eff es a) -> a
+runPureEffAsyncSafe e = unsafePerformIO $ do
+  result <- newEmptyMVar
+  owner <- newIORef ()
+  _ <- Control.Exception.mask_ $ forkIOWithUnmask $ \unmask -> do
+    tid <- myThreadId
+    addFinalizer owner (killThread tid)
+    r <- Control.Exception.try @Control.Exception.SomeException . unmask $ do
+      runEff (\_ -> e)
+    putMVar result r
+  r <- keepAlive owner (readMVar result)
+  either Control.Exception.throwIO pure r
+
+keepAlive :: a -> IO b -> IO b
+keepAlive a (IO action) = IO $ \s -> keepAlive# a s action
+
+-- | Like 'runPureEffAsyncSafe', but starts a fresh worker after an
+-- asynchronous exception interrupts a demand for the result.  This
+-- means that if the thread forcing the thunk is killed the work done
+-- so far is discarded, and restarted from scratch in the next
+-- evaluation.
+--
+-- This should not be used. It exists only as an example of a (worse)
+-- alternative approach.  Use 'runPureEffAsyncSafe' instead.
+runPureEffAsyncSafeRestarting :: (forall es. Eff es a) -> a
+runPureEffAsyncSafeRestarting effBody = unsafePerformIO $
+  Control.Exception.mask $ \restore -> do
+    tidVar <- newEmptyMVar
+    done <- newEmptyMVar
+
+    let body = do
+          tid <- forkIO $ do
+            r <-
+              Control.Exception.try @Control.Exception.SomeException $
+                restore $
+                  runEff (\_ -> effBody)
+            putMVar done r
+          putMVar tidVar tid
+          takeMVar done
+
+    r <- fix $ \again -> do
+      attempted <-
+        restore $
+          Control.Exception.try @Control.Exception.SomeException body
+      case attempted of
+        Left ex -> do
+          tid <- takeMVar tidVar
+          killThread tid
+          _ <- takeMVar done
+          myself <- myThreadId
+          throwTo myself ex
+          again
+        Right result -> pure result
+
+    case r of
+      Left l -> Control.Exception.throwIO l
+      Right r' -> pure r'
+
 unsafeCoerceEff :: Eff t r -> Eff t' r
 unsafeCoerceEff = coerce
 
 weakenEff :: t `In` t' -> Eff t r -> Eff t' r
-weakenEff _ = unsafeCoerceEff
+weakenEff (In# (# #)) = unsafeCoerceEff
 
 insertFirst :: Eff b r -> Eff (c1 :& b) r
 insertFirst = weakenEff (drop (eq ZW))
@@ -346,41 +432,55 @@
 type role StateSource nominal
 
 -- | Capability to throw an exception of type @exn@
-newtype Exception exn (e :: Effects)
+newtype Throw exn (e :: Effects)
   = MkException (forall a. exn -> Eff e a)
-  deriving (Handle) via OneWayCoercibleHandle (Exception exn)
+  deriving (Handle) via OneWayCoercibleHandle (Throw exn)
 
-type role Exception representational nominal
+type role Throw representational nominal
 
-instance (e <: es) => OneWayCoercible (Exception ex e) (Exception ex es) where
+instance (e <: es) => OneWayCoercible (Throw ex e) (Throw ex es) where
   oneWayCoercibleImpl = oneWayCoercible
 
+-- | A scoped exception capability that can catch within its own scope.
+type ThrowCatch :: Type -> Effects -> Type
+newtype ThrowCatch ex e
+  = MkThrowCatch (ScopedException.Exception ex)
+  deriving (Handle) via OneWayCoercibleHandle (ThrowCatch ex)
+
+type role ThrowCatch nominal nominal
+
+instance
+  (e <: es) =>
+  OneWayCoercible (ThrowCatch ex e) (ThrowCatch ex es)
+  where
+  oneWayCoercibleImpl = unsafeOneWayCoercible
+
 -- | Capability to modify a reference to an @s@
-newtype State s (e :: Effects) = UnsafeMkState (IORef s)
-  deriving (Handle) via OneWayCoercibleHandle (State s)
+newtype Modify s (e :: Effects) = UnsafeMkState (IORef s)
+  deriving (Handle) via OneWayCoercibleHandle (Modify s)
 
-type role State representational nominal
+type role Modify representational nominal
 
-instance (e <: es) => OneWayCoercible (State s e) (State s es) where
+instance (e <: es) => OneWayCoercible (Modify s e) (Modify s es) where
   oneWayCoercibleImpl = oneWayCoercible
 
 -- | Capability to yield a value of type @a@ and then await a value of
 -- type @b@ in response.
-newtype Coroutine a b (e :: Effects) = MkCoroutine (a -> Eff e b)
-  deriving (Handle) via OneWayCoercibleHandle (Coroutine a b)
+newtype Request a b (e :: Effects) = MkCoroutine (a -> Eff e b)
+  deriving (Handle) via OneWayCoercibleHandle (Request a b)
 
-instance (e <: es) => OneWayCoercible (Coroutine a b e) (Coroutine a b es) where
+instance (e <: es) => OneWayCoercible (Request a b e) (Request a b es) where
   oneWayCoercibleImpl = oneWayCoercible
 
 -- | Capability to yield values of type @a@.  It is implemented as a
 -- 'Bluefin.Capability.Request' capability that can yield values of
 -- type @a@ and then await values of type @()@.
-type Stream a = Coroutine a ()
+type Yield a = Request a ()
 
-type Consume a = Coroutine () a
+type Await a = Request () a
 
 -- | Every Bluefin capability should have an instance of class @Handle@.
--- Built-in capabilities, such as 'Exception', 'State' and 'IOE', come with
+-- Built-in capabilities, such as 'Throw', 'Modify' and 'IOE', come with
 -- @Handle@ instances.
 --
 -- You should define a @Handle@ instance for each capability that you
@@ -392,8 +492,8 @@
 -- @
 -- data Application e = MkApplication
 --   { queryDatabase :: String -> Int -> Eff e [String],
---     applicationState :: State (Int, Bool) e,
---     logger :: Stream String e
+--     applicationState :: Modify (Int, Bool) e,
+--     logger :: Yield String e
 --   }
 --   deriving (Generic)
 --   deriving (Handle) via 'OneWayCoercibleHandle' Application
@@ -412,7 +512,10 @@
 --
 -- Please note the "handle" nomeclature is legacy and will probably
 -- change to "capability" in the future.  See "Bluefin.Capability".
-class Handle (h :: Effects -> Type) where
+class
+  (forall e es. (e <: es) => OneWayCoercible (h e) (h es)) =>
+  Handle (h :: Effects -> Type)
+  where
   handleImpl :: HandleD h
 
 -- | This was previously a method of class 'Handle' using which you
@@ -444,7 +547,7 @@
 --   oneWayCoercibleImpl = 'oneWayCoercibleTrustMe' $ \\h -> \<mapHandle definition\>
 -- @
 mapHandle :: forall h e es. (Handle h, e <: es) => h e -> h es
-mapHandle = case handleDictImpl @h of MkHandleDict -> oneWayCoerce
+mapHandle = unsafeOneWayCoerce
 
 withHandle ::
   forall h r.
@@ -452,7 +555,7 @@
   -- | ͘
   ((forall e es. (e <: es) => OneWayCoercible (h e) (h es)) => r) ->
   r
-withHandle r = case handleDictImpl @h of MkHandleDict -> r
+withHandle r = r
 
 type HandleDict :: (Effects -> Type) -> Type
 data HandleDict h where
@@ -514,13 +617,13 @@
 handleOneWayCoercible = MkHandleD (unsafeCoerce (MkHandleDict @h))
 
 instance (Handle h) => Handle (Rec1 h) where
-  handleImpl = withHandle @h handleOneWayCoercible
+  handleImpl = handleOneWayCoercible
 
 instance (Handle h) => Handle (M1 i t h) where
-  handleImpl = withHandle @h handleOneWayCoercible
+  handleImpl = handleOneWayCoercible
 
 instance (Handle h1, Handle h2) => Handle (h1 :*: h2) where
-  handleImpl = withHandle @h1 (withHandle @h2 handleOneWayCoercible)
+  handleImpl = handleOneWayCoercible
 
 -- | It is not always possible to derive an instance of
 -- 'OneWayCoercible'.  In such cases write a definition of
@@ -580,6 +683,30 @@
 
 -- }
 
+-- | For defining 'OneWayCoercible' instances for newtypes. Example:
+--
+-- @
+-- newtype Random g e = Random (Modify g e)
+--   deriving (Handle) via OneWayCoercibleHandle (Random g)
+--
+-- instance (e \<: es) => OneWayCoercible (Random g e) (Random g es) where
+--   oneWayCoercibleImpl = oneWayCoercibleNewtypeHandle @(Modify g)
+-- @
+oneWayCoercibleNewtypeHandle ::
+  forall h1 h2 e es.
+  (e :> es) =>
+  ( Coercible (h2 e) (h1 e),
+    OneWayCoercible (h1 e) (h1 es),
+    Coercible (h1 es) (h2 es)
+  ) =>
+  -- | ͘
+  OneWayCoercibleD (h2 e) (h2 es)
+oneWayCoercibleNewtypeHandle =
+  trans3D
+    (oneWayCoercible @(h2 e) @(h1 e))
+    (oneWayCoercibleImpl @(h1 e) @(h1 es))
+    (oneWayCoercible @(h1 es) @(h2 es))
+
 -- | A convenience type whose only purpose is to avoid writing @(# #)@
 -- as an argument to functions which are only functions because
 -- top-level definitions of unlifted kind are forbidden.
@@ -728,12 +855,20 @@
 -- @
 throw ::
   (e <: es) =>
-  Exception ex e ->
+  Throw ex e ->
   -- | Value to throw
   ex ->
   Eff es a
 throw h = case mapHandle h of MkException throw_ -> throw_
 
+throwCatchThrow ::
+  (e <: es) =>
+  ThrowCatch ex e ->
+  ex ->
+  Eff es a
+throwCatchThrow (MkThrowCatch ex) exn =
+  unsafeProvideIO $ \io -> effIO io (ScopedException.throw ex exn)
+
 has :: forall a b. (a <: b) => a `In` b
 -- This is safe because, as shown by instanceProof1/2/3, the only way
 -- to construct `a <: b` is if `a `In` b`.
@@ -758,14 +893,21 @@
 -- @
 try ::
   forall exn (es :: Effects) a.
-  (forall e. Exception exn e -> Eff (e :& es) a) ->
+  (forall e. Throw exn e -> Eff (e :& es) a) ->
   -- | @Left@ if the exception was thrown, @Right@ otherwise
   Eff es (Either exn a)
 try f =
+  throwCatchTry $ \ex ->
+    f (MkException (throwCatchThrow ex))
+
+throwCatchTry ::
+  (forall e. ThrowCatch ex e -> Eff (e :& es) a) ->
+  Eff es (Either ex a)
+throwCatchTry f =
   unsafeProvideIO $ \io -> do
     withEffToIO_ io $ \effToIO -> do
-      withScopedException_ $ \throw_ -> do
-        effToIO (f (MkException (effIO io . throw_)))
+      ScopedException.try $ \ex -> do
+        effToIO (f (MkThrowCatch ex))
 
 -- |
 -- @
@@ -778,7 +920,7 @@
   forall exn (es :: Effects) a.
   -- | If the exception is thrown, apply this handler
   (exn -> Eff es a) ->
-  (forall e. Exception exn e -> Eff (e :& es) a) ->
+  (forall e. Throw exn e -> Eff (e :& es) a) ->
   Eff es a
 handle h f =
   try f >>= \case
@@ -788,7 +930,7 @@
 -- | 'handle', but with the argument order swapped
 catch ::
   forall exn (es :: Effects) a.
-  (forall e. Exception exn e -> Eff (e :& es) a) ->
+  (forall e. Throw exn e -> Eff (e :& es) a) ->
   -- | If the exception is thrown, apply this handler
   (exn -> Eff es a) ->
   Eff es a
@@ -828,7 +970,7 @@
   forall ex es e1 e2 r.
   (e1 <: es, e2 <: es, Control.Exception.Exception ex) =>
   IOE e1 ->
-  Exception ex e2 ->
+  Throw ex e2 ->
   Eff es r ->
   -- | ͘
   Eff es r
@@ -895,7 +1037,7 @@
 withStateInIO ::
   (e1 <: es, e2 <: es) =>
   IOE e1 ->
-  State s e2 ->
+  Modify s e2 ->
   (IORef s -> IO r) ->
   Eff es r
 withStateInIO io (UnsafeMkState r) k = effIO io (k r)
@@ -909,7 +1051,7 @@
 -- @
 get ::
   (e <: es) =>
-  State s e ->
+  Modify s e ->
   -- | The current value of the state
   Eff es s
 get st = unsafeProvideIO $ \io -> withStateInIO io st readIORef
@@ -923,7 +1065,7 @@
 -- @
 put ::
   (e <: es) =>
-  State s e ->
+  Modify s e ->
   -- | The new value of the state.  The new value is forced before
   -- writing it to the state.
   s ->
@@ -938,7 +1080,7 @@
 -- @
 modify ::
   (e <: es) =>
-  State s e ->
+  Modify s e ->
   -- | Apply this function to the state.  The new value of the state
   -- is forced before writing it to the state.
   (s -> s) ->
@@ -947,20 +1089,16 @@
   s <- get state
   put state (f s)
 
-withScopedException_ :: ((forall a. e -> IO a) -> IO r) -> IO (Either e r)
-withScopedException_ f =
-  ScopedException.try (\ex -> f (ScopedException.throw ex))
-
 -- |
 -- @
 -- 'runPureEff' $ 'withStateSource' $ \\source -> do
 --   n <- 'newState' source 5
 --   total <- newState source 0
 --
---   'withJump' $ \\done -> forever $ do
---     n' <- 'Bluefin.State.get' n
---     'Bluefin.State.modify' total (+ n')
---     when (n' == 0) $ 'Bluefin.Jump.jumpTo' done
+--   'withJumpTo' $ \\done -> forever $ do
+--     n' <- 'Bluefin.Capability.Modify.get' n
+--     'Bluefin.Capability.Modify.modify' total (+ n')
+--     when (n' == 0) $ 'Bluefin.Capability.JumpTo.jumpTo' done
 --     modify n (subtract 1)
 --
 --   get total
@@ -978,10 +1116,10 @@
 --   n <- 'newState' source 5
 --   total <- newState source 0
 --
---   'Bluefin.Jump.withJump' $ \\done -> forever $ do
---     n' <- 'Bluefin.State.get' n
---     'Bluefin.State.modify' total (+ n')
---     when (n' == 0) $ 'Bluefin.Jump.jumpTo' done
+--   'Bluefin.Capability.JumpTo.withJumpTo' $ \\done -> forever $ do
+--     n' <- 'Bluefin.Capability.Modify.get' n
+--     'Bluefin.Capability.Modify.modify' total (+ n')
+--     when (n' == 0) $ 'Bluefin.Capability.JumpTo.jumpTo' done
 --     modify n (subtract 1)
 --
 --   get total
@@ -993,7 +1131,7 @@
   -- | The initial value for the state capability
   s ->
   -- | A new state capability
-  Eff es (State s e)
+  Eff es (Modify s e)
 newState StateSource s = unsafeProvideIO $ \io -> do
   fmap UnsafeMkState (effIO io (newIORef s))
 
@@ -1008,7 +1146,7 @@
   -- | Initial state
   s ->
   -- | Stateful computation
-  (forall e. State s e -> Eff (e :& es) a) ->
+  (forall e. Modify s e -> Eff (e :& es) a) ->
   -- | Result and final state
   Eff es (a, s)
 runState s f = do
@@ -1039,7 +1177,7 @@
 -- @
 yield ::
   (e1 <: es) =>
-  Stream a e1 ->
+  Yield a e1 ->
   -- | Yield this value from the stream
   a ->
   Eff es ()
@@ -1066,7 +1204,7 @@
 -- ([0, 0, 1, 10, 2, 20, 3, 30], ())
 -- @
 forEach ::
-  (forall e1. Coroutine a b e1 -> Eff (e1 :& es) r) ->
+  (forall e1. Request a b e1 -> Eff (e1 :& es) r) ->
   -- | Apply this effectful function for each element of the coroutine
   (a -> Eff es b) ->
   Eff es r
@@ -1100,7 +1238,7 @@
   (Foldable t, e1 <: es) =>
   -- | Yield all these values from the stream
   t a ->
-  Stream a e1 ->
+  Yield a e1 ->
   Eff es ()
 inFoldable t = for_ t . yield
 
@@ -1114,8 +1252,8 @@
 enumerate ::
   (e2 <: es) =>
   -- | ͘
-  (forall e1. Stream a e1 -> Eff (e1 :& es) r) ->
-  Stream (Int, a) e2 ->
+  (forall e1. Yield a e1 -> Eff (e1 :& es) r) ->
+  Yield (Int, a) e2 ->
   Eff es r
 enumerate s = enumerateFrom 0 s
 
@@ -1130,8 +1268,8 @@
   (e2 <: es) =>
   -- | Initial value
   Int ->
-  (forall e1. Stream a e1 -> Eff (e1 :& es) r) ->
-  Stream (Int, a) e2 ->
+  (forall e1. Yield a e1 -> Eff (e1 :& es) r) ->
+  Yield (Int, a) e2 ->
   Eff es r
 enumerateFrom n ss st =
   evalState n $ \i -> forEach (useImplUnder . ss) $ \s -> do
@@ -1153,10 +1291,10 @@
   Eff es r
 consumeEach k e = forEach k (\() -> e)
 
-await :: (e <: es) => Consume a e -> Eff es a
+await :: (e <: es) => Await a e -> Eff es a
 await r = yieldCoroutine r ()
 
-type EarlyReturn = Exception
+type ReturnEarly = Throw
 
 -- | Run an 'Eff' action with the ability to return early to this
 -- point.  In the language of exceptions, 'withEarlyReturn' installs
@@ -1187,7 +1325,7 @@
 -- @
 returnEarly ::
   (e <: es) =>
-  EarlyReturn r e ->
+  ReturnEarly r e ->
   -- | Return early to the handler, with this value.
   r ->
   Eff es a
@@ -1204,7 +1342,7 @@
   -- | Initial state
   s ->
   -- | Stateful computation
-  (forall e. State s e -> Eff (e :& es) a) ->
+  (forall e. Modify s e -> Eff (e :& es) a) ->
   -- | Result
   Eff es a
 evalState s f = fmap fst (runState s f)
@@ -1220,7 +1358,7 @@
   -- | Initial state
   s ->
   -- | Stateful computation
-  (forall e. State s e -> Eff (e :& es) (s -> a)) ->
+  (forall e. Modify s e -> Eff (e :& es) (s -> a)) ->
   -- | Result
   Eff es a
 withState s f = do
@@ -1273,10 +1411,10 @@
   Eff es r
 withC2 c f = withCompound c (\_ i -> f i)
 
-putC :: forall ss es e. (ss <: es) => Compound e (State Int) ss -> Int -> Eff es ()
+putC :: forall ss es e. (ss <: es) => Compound e (Modify Int) ss -> Int -> Eff es ()
 putC c i = withC2 c (\h -> put h i)
 
-getC :: forall ss es e. (ss <: es) => Compound e (State Int) ss -> Eff es Int
+getC :: forall ss es e. (ss <: es) => Compound e (Modify Int) ss -> Eff es Int
 getC c = withC2 c (\h -> get h)
 
 -- TODO: Make this (s1 <: es, s2 <: es), like withC
@@ -1300,13 +1438,19 @@
 -- ([1,2,100], ())
 -- @
 yieldToList ::
-  (forall e1. Stream a e1 -> Eff (e1 :& es) r) ->
+  (forall e1. Yield a e1 -> Eff (e1 :& es) r) ->
   -- | Yielded elements and final result
   Eff es ([a], r)
 yieldToList f = do
   (as, r) <- yieldToReverseList f
   pure (reverse as, r)
 
+-- | Gather all yielded elements into a list, purely.  Can be used
+-- when only when there are no other capabilities in scope, besides
+-- the 'Yield'.
+yieldToPureList :: (forall e. Yield a e -> Eff e r) -> ([a], r)
+yieldToPureList f = runPureEff $ yieldToList $ \y -> useImpl (f y)
+
 -- |
 -- @
 -- >>> runPureEff $ withYieldToList $ \\y -> do
@@ -1317,8 +1461,8 @@
 -- 3
 -- @
 withYieldToList ::
-  -- | Stream computation
-  (forall e. Stream a e -> Eff (e :& es) ([a] -> r)) ->
+  -- | Yield computation
+  (forall e. Yield a e -> Eff (e :& es) ([a] -> r)) ->
   -- | Result
   Eff es r
 withYieldToList f = do
@@ -1337,34 +1481,24 @@
 -- ([100,2,1], ())
 -- @
 yieldToReverseList ::
-  (forall e. Stream a e -> Eff (e :& es) r) ->
+  (forall e. Yield a e -> Eff (e :& es) r) ->
   -- | Yielded elements in reverse order, and final result
   Eff es ([a], r)
 yieldToReverseList f = do
-  evalState [] $ \(s :: State lo st) -> do
+  evalState [] $ \(s :: Modify lo st) -> do
     r <- forEach (useImplUnder . f) $ \i ->
       modify s (i :)
     as <- get s
     pure (as, r)
 
-mapStream ::
-  (e2 <: es) =>
-  -- | Apply this function to all elements of the input stream.
-  (a -> b) ->
-  -- | Input stream
-  (forall e1. Stream a e1 -> Eff (e1 :& es) r) ->
-  Stream b e2 ->
-  Eff es r
-mapStream f = mapMaybe (Just . f)
-
 mapMaybe ::
   (e2 <: es) =>
   -- | Yield from the output stream all of the elemnts of the input
   -- stream for which this function returns @Just@
   (a -> Maybe b) ->
   -- | Input stream
-  (forall e1. Stream a e1 -> Eff (e1 :& es) r) ->
-  Stream b e2 ->
+  (forall e1. Yield a e1 -> Eff (e1 :& es) r) ->
+  Yield b e2 ->
   Eff es r
 mapMaybe f s y = forEach s $ \a -> do
   case f a of
@@ -1375,8 +1509,8 @@
 catMaybes ::
   (e2 <: es) =>
   -- | Input stream
-  (forall e1. Stream (Maybe a) e1 -> Eff (e1 :& es) r) ->
-  Stream a e2 ->
+  (forall e1. Yield (Maybe a) e1 -> Eff (e1 :& es) r) ->
+  Yield a e2 ->
   Eff es r
 catMaybes s y = mapMaybe id s y
 
@@ -1391,7 +1525,7 @@
 cycleToStream ::
   (Foldable f, e1 <: es) =>
   f a ->
-  Stream a e1 ->
+  Yield a e1 ->
   -- | ͘
   Eff es ()
 cycleToStream f y = do
@@ -1419,7 +1553,7 @@
       await source >>= yield sink
       loop (c - 1)
 
-type Jump = EarlyReturn ()
+type JumpTo = ReturnEarly ()
 
 -- |
 -- @
@@ -1427,10 +1561,10 @@
 --   n <- 'newState' source 5
 --   total <- newState source 0
 --
---   'Bluefin.Jump.withJump' $ \\done -> forever $ do
---     n' <- 'Bluefin.State.get' n
---     'Bluefin.State.modify' total (+ n')
---     when (n' == 0) $ 'Bluefin.Jump.jumpTo' done
+--   'Bluefin.Capability.JumpTo.withJumpTo' $ \\done -> forever $ do
+--     n' <- 'Bluefin.Capability.Modify.get' n
+--     'Bluefin.Capability.Modify.modify' total (+ n')
+--     when (n' == 0) $ 'Bluefin.Capability.JumpTo.jumpTo' done
 --     modify n (subtract 1)
 --
 --   get total
@@ -1448,10 +1582,10 @@
 --   n <- 'newState' source 5
 --   total <- newState source 0
 --
---   'Bluefin.Jump.withJump' $ \\done -> forever $ do
---     n' <- 'Bluefin.State.get' n
---     'Bluefin.State.modify' total (+ n')
---     when (n' == 0) $ 'Bluefin.Jump.jumpTo' done
+--   'Bluefin.Capability.JumpTo.withJumpTo' $ \\done -> forever $ do
+--     n' <- 'Bluefin.Capability.Modify.get' n
+--     'Bluefin.Capability.Modify.modify' total (+ n')
+--     when (n' == 0) $ 'Bluefin.Capability.JumpTo.jumpTo' done
 --     modify n (subtract 1)
 --
 --   get total
@@ -1459,12 +1593,12 @@
 -- @
 jumpTo ::
   (e <: es) =>
-  Jump e ->
+  JumpTo e ->
   -- | ͘
   Eff es a
 jumpTo tag = throw tag ()
 
-unwrap :: (e <: es) => Jump e -> Maybe a -> Eff es a
+unwrap :: (e <: es) => JumpTo e -> Maybe a -> Eff es a
 unwrap j = \case
   Nothing -> jumpTo j
   Just a -> pure a
@@ -1491,7 +1625,7 @@
   IO a ->
   -- | ͘
   Eff es a
-effIO MkIOE = UnsafeMkEff
+effIO MkIOE = UnsafeMkEff . const
 
 -- | Run an 'Eff' whose only unhandled effect is 'IO'.
 --
@@ -1520,7 +1654,9 @@
   (forall e. IOE e -> Eff e a) ->
   -- | ͘
   IO a
-runEff_ eff = unsafeUnEff (eff MkIOE)
+runEff_ eff = do
+  emptyEnv <- newIORef Vault.empty
+  unsafeUnEff (eff MkIOE) emptyEnv
 
 unsafeProvideIO ::
   (forall e. IOE e -> Eff (e :& es) a) ->
@@ -1528,40 +1664,10 @@
   Eff es a
 unsafeProvideIO eff = useImplIn eff MkIOE
 
-connect ::
-  (forall e1. Coroutine a b e1 -> Eff (e1 :& es) r1) ->
-  (forall e2. a -> Coroutine b a e2 -> Eff (e2 :& es) r2) ->
-  forall e1 e2.
-  (e1 <: es, e2 <: es) =>
-  Eff
-    es
-    ( Either
-        (r1, a -> Coroutine b a e2 -> Eff es r2)
-        (r2, b -> Coroutine a b e1 -> Eff es r1)
-    )
-connect _ _ = error "connect unimplemented, sorry"
-
-head' ::
-  forall a b r es.
-  (forall e. Coroutine a b e -> Eff (e :& es) r) ->
-  forall e.
-  (e <: es) =>
-  Eff
-    es
-    ( Either
-        r
-        (a, b -> Coroutine a b e -> Eff es r)
-    )
-head' c = do
-  r <- connect c (\a _ -> pure a) @_ @es
-  pure $ case r of
-    Right r' -> Right r'
-    Left (l, _) -> Left l
-
-newtype Writer w e = Writer (Stream w e)
-  deriving (Handle) via OneWayCoercibleHandle (Writer w)
+newtype Tell w e = Tell (Yield w e)
+  deriving (Handle) via OneWayCoercibleHandle (Tell w)
 
-instance (e <: es) => OneWayCoercible (Writer w e) (Writer w es) where
+instance (e <: es) => OneWayCoercible (Tell w e) (Tell w es) where
   oneWayCoercibleImpl = oneWayCoercible
 
 -- |
@@ -1577,7 +1683,7 @@
   (forall e. Writer w e -> Eff (e :& es) r) ->
   Eff es (r, w)
 runWriter f = runState mempty $ \st -> do
-  forEach (useImplUnder . f . Writer) $ \ww -> do
+  forEach (useImplUnder . f . Tell) $ \ww -> do
     modify st (<> ww)
 
 -- |
@@ -1610,24 +1716,35 @@
 -- @
 tell ::
   (e <: es) =>
-  Writer w e ->
+  Tell w e ->
   -- | ͘
   w ->
   Eff es ()
-tell (Writer y) = yield y
+tell (Tell y) = yield y
 
-newtype Reader r e = MkReader (State r e)
-  deriving newtype (Handle)
+type Ask :: Type -> Effects -> Type
+newtype Ask r e = MkReader (Vault.Key r)
+  deriving (Handle) via OneWayCoercibleHandle (Ask r)
 
-instance (e <: es) => OneWayCoercible (Reader r e) (Reader r es) where
+instance (e <: es) => OneWayCoercible (Ask r e) (Ask r es) where
   oneWayCoercibleImpl = oneWayCoercible
 
+type role Ask representational nominal
+
 runReader ::
   -- | Initial value for @Reader@.
   r ->
   (forall e. Reader r e -> Eff (e :& es) a) ->
   Eff es a
-runReader r f = evalState r (f . MkReader)
+runReader r f = do
+  bracket
+    ( UnsafeMkEff $ \vault -> do
+        k <- Vault.newKey
+        modifyIORef' vault (\v -> Vault.insert k r v)
+        pure k
+    )
+    (\k -> UnsafeMkEff $ \vault -> modifyIORef' vault (Vault.delete k))
+    (\k -> makeOp (f (MkReader k)))
 
 -- | Read the value.  Note that @ask@ has the property that these two
 -- operations are always equivalent:
@@ -1647,38 +1764,53 @@
 ask ::
   (e <: es) =>
   -- | ͘
-  Reader r e ->
+  Ask r e ->
   Eff es r
-ask (MkReader st) = get st
+ask (MkReader k) = UnsafeMkEff $ \vault -> do
+  v <- readIORef vault
+  case Vault.lookup k v of
+    Nothing -> error msg
+    Just ref -> pure ref
+  where
+    msg =
+      unlines
+        [ "ask called on out of scope reference",
+          unwords
+            [ "If you haven't subverted Bluefin's type system",
+              "then this is a Bluefin bug.",
+              "Please report it at",
+              "https://github.com/tomjaguarpaw/bluefin/issues/new"
+            ]
+        ]
 
 -- | Read the value modified by a function
 asks ::
   (e <: es) =>
-  Reader r e ->
+  Ask r e ->
   -- | Read the value modified by this function
   (r -> a) ->
   Eff es a
-asks (MkReader st) f = fmap f (get st)
+asks r f = fmap f (ask r)
 
--- | Locally override the value in the @Reader@. It will be restored
+-- | Locally override the value in the @Ask@. It will be restored
 -- when the @local@ block ends.
 local ::
   (e1 <: es) =>
-  Reader r e1 ->
+  Ask r e1 ->
   -- | In the body, the reader value is modified by this function.
   (r -> r) ->
   -- | Body
   Eff es a ->
   Eff es a
-local (MkReader st) f k = do
-  orig <- get st
-  bracket
-    (put st (f orig))
-    (\() -> put st orig)
-    (\() -> k)
+local (MkReader key) f k = UnsafeMkEff $ \env@vault -> do
+  orig <- readIORef vault
+  Control.Exception.bracket
+    (writeIORef vault (Vault.adjust f key orig))
+    (\() -> writeIORef vault orig)
+    (\() -> case k of UnsafeMkEff m -> m env)
 
-newtype HandleReader h e = UnsafeMkHandleReader (State (h e) e)
-  deriving (Handle) via OneWayCoercibleHandle (HandleReader h)
+newtype AskCapability h e = UnsafeMkHandleReader (Ask (h e) e)
+  deriving (Handle) via OneWayCoercibleHandle (AskCapability h)
 
 -- In general this is really tremendously unsafe because we could take
 -- an `HandleReader h e`, map it to `HandleReader h es`, write an `h
@@ -1695,8 +1827,11 @@
   HandleReader h es
 mapHandleReader = case coerceH of Coercion -> coerce
   where
+    oneWayCoerceH :: OneWayCoercion (h e) (h es)
+    oneWayCoerceH = oneWayCoercion
+
     coerceH :: Coercion (h e) (h es)
-    coerceH = unsafeCoerce (Coercion :: Coercion (h e) (h e))
+    coerceH = unsafeCoercionOfOneWayCoercion oneWayCoerceH
 
 localHandle ::
   (e <: es, Handle h) =>
@@ -1705,20 +1840,16 @@
   Eff es r ->
   -- | ͘
   Eff es r
-localHandle hh@(UnsafeMkHandleReader st) f k = do
-  let UnsafeMkHandleReader st' = mapHandle hh
-  orig <- get st
-  bracket
-    (put st' (f (mapHandle orig)))
-    (\() -> put st orig)
-    (\() -> k)
+localHandle hh f k = do
+  let UnsafeMkHandleReader st = mapHandle hh
+  local st f k
 
 askHandle ::
   (e <: es, Handle h) =>
   HandleReader h e ->
   -- | ͘
   Eff es (h es)
-askHandle hh = let UnsafeMkHandleReader st = mapHandle hh in get st
+askHandle hh = let UnsafeMkHandleReader st = mapHandle hh in ask st
 
 asksHandle ::
   (e1 <: es, Handle h) =>
@@ -1737,11 +1868,14 @@
   -- | ͘
   Eff es r
 runHandleReader h k = do
-  evalState (mapHandle h) $ \(st :: State (h es) e) -> do
+  runReader (mapHandle h) $ \(st :: Ask (h es) e) -> do
+    let oneWayCoerceH :: OneWayCoercion (h es) (h (e :& es))
+        oneWayCoerceH = oneWayCoercion
+
     let coerceH :: Coercion (h es) (h (e :& es))
-        coerceH = unsafeCoerce (Coercion :: Coercion (h es) (h es))
+        coerceH = unsafeCoercionOfOneWayCoercion oneWayCoerceH
 
-    let mapS :: State (h es) e' -> State (h (e :& es)) e'
+    let mapS :: Ask (h es) e' -> Ask (h (e :& es)) e'
         mapS = case coerceH of Coercion -> coerce
 
     let h' :: HandleReader h (e :& es)
@@ -1751,7 +1885,7 @@
 
     useImplIn k h'
 
-instance (e <: es) => OneWayCoercible (HandleReader h e) (HandleReader h es) where
+instance (e <: es) => OneWayCoercible (AskCapability h e) (AskCapability h es) where
   oneWayCoercibleImpl = unsafeOneWayCoercible
 
 newtype ConstEffect r (e :: Effects) = MkConstEffect r
@@ -1769,33 +1903,33 @@
 
 -- Capbility synonyms
 
-type Ask = Reader
+type Reader = Ask
 
-type AskCapability = HandleReader
+type HandleReader = AskCapability
 
 -- | Capability to await values of type @a@
-type Await a = Consume a
+type Consume a = Await a
 
-type JumpTo = Jump
+type Jump = JumpTo
 
 -- | Capability to yield a value of type @a@ and then await a value of
 -- type @b@ in response.
-type Request = Coroutine
+type Coroutine = Request
 
-type ReturnEarly = EarlyReturn
+type EarlyReturn r = ReturnEarly r
 
 -- | Capability to modify a reference to an @s@
-type Modify = State
+type State = Modify
 
-type Tell = Writer
+type Writer = Tell
 
 -- | Capability to throw an exception of type @exn@
-type Throw = Exception
+type Exception = Throw
 
 -- | Capability to yield values of type @a@.  It is implemented as a
 -- 'Bluefin.Capability.Request' capability that can yield values of
 -- type @a@ and then await values of type @()@.
-type Yield a = Stream a
+type Stream a = Yield a
 
 runAsk ::
   -- | Initial value for @Ask@.
@@ -1828,7 +1962,7 @@
 -- | 'awaitYield' is 'Bluefin.Capability.Request.connectRequests'
 -- specialized to @Await@ and @Yield@, which is the most common case.
 awaitYield ::
-  -- | Starts running first. Each 'await' from the @Consume@ ...
+  -- | Starts running first. Each 'await' from the @Await@ ...
   (forall e. Await a e -> Eff (e :& es) r) ->
   -- | ... receives the value 'yield'ed from the @Yield@
   (forall e. Yield a e -> Eff (e :& es) r) ->
@@ -1877,7 +2011,7 @@
 -- 42
 -- @
 ignoreYield ::
-  (forall e1. Stream a e1 -> Eff (e1 :& es) r) ->
+  (forall e1. Yield a e1 -> Eff (e1 :& es) r) ->
   -- | ͘
   Eff es r
 ignoreYield = ignoreStream
@@ -1911,7 +2045,7 @@
 -- "Returned early with 5"
 -- @
 withReturnEarly ::
-  (forall e. EarlyReturn r e -> Eff (e :& es) r) ->
+  (forall e. ReturnEarly r e -> Eff (e :& es) r) ->
   -- | ͘
   Eff es r
 withReturnEarly = withEarlyReturn
@@ -2002,14 +2136,16 @@
 runAskCapability ::
   (e1 <: es, Handle h) =>
   h e1 ->
-  (forall e. HandleReader h e -> Eff (e :& es) r) ->
+  (forall e. AskCapability h e -> Eff (e :& es) r) ->
   -- | ͘
   Eff es r
 runAskCapability = runHandleReader
 
+-- | Do not use @askCapability@.  It is unsafe and will be removed in
+-- a future version.  Use 'asksCapability' instead.
 askCapability ::
   (e <: es, Handle h) =>
-  HandleReader h e ->
+  AskCapability h e ->
   -- | ͘
   Eff es (h es)
 askCapability = askHandle
@@ -2037,10 +2173,10 @@
 --   n <- 'newState' source 5
 --   total <- newState source 0
 --
---   'Bluefin.JumpTo.withJumpTo' $ \\done -> forever $ do
+--   'Bluefin.Capability.JumpTo.withJumpTo' $ \\done -> forever $ do
 --     n' <- 'Bluefin.Capability.Modify.get' n
 --     'Bluefin.Capability.Modify.modify' total (+ n')
---     when (n' == 0) $ 'Bluefin.JumpTo.jumpTo' done
+--     when (n' == 0) $ 'Bluefin.Capability.JumpTo.jumpTo' done
 --     modify n (subtract 1)
 --
 --   get total
diff --git a/src/Bluefin/Internal/Capability/ThrowCatch.hs b/src/Bluefin/Internal/Capability/ThrowCatch.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin/Internal/Capability/ThrowCatch.hs
@@ -0,0 +1,88 @@
+{-# LANGUAGE ExplicitNamespaces #-}
+{-# LANGUAGE ImportQualifiedPost #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TypeOperators #-}
+
+module Bluefin.Internal.Capability.ThrowCatch
+  ( ThrowCatch,
+    module Bluefin.Internal.Capability.ThrowCatch,
+  )
+where
+
+import Bluefin.Internal
+  ( ThrowCatch (MkThrowCatch),
+    Eff,
+    throwCatchTry,
+    throwCatchThrow,
+    unsafeProvideIO,
+    useImpl,
+    withEffToIO_,
+    (:&),
+    type (<:),
+  )
+import Bluefin.Internal.Exception.Scoped qualified as Scoped
+
+try ::
+  (forall e. ThrowCatch ex e -> Eff (e :& es) a) ->
+  -- | ͘
+  Eff es (Either ex a)
+try = throwCatchTry
+
+handle ::
+  (ex -> Eff es a) ->
+  (forall e. ThrowCatch ex e -> Eff (e :& es) a) ->
+  -- | ͘
+  Eff es a
+handle h f =
+  try f >>= \case
+    Left ex -> h ex
+    Right a -> pure a
+
+catch ::
+  (forall e. ThrowCatch ex e -> Eff (e :& es) a) ->
+  (ex -> Eff es a) ->
+  -- | ͘
+  Eff es a
+catch f h = handle h f
+
+localCatch ::
+  (e <: es) =>
+  ThrowCatch ex e ->
+  Eff es a ->
+  (ex -> Eff es a) ->
+  -- | ͘
+  Eff es a
+localCatch h action handler =
+  localTry h action >>= \case
+    Left ex -> handler ex
+    Right a -> pure a
+
+localHandle ::
+  (e <: es) =>
+  ThrowCatch ex e ->
+  (ex -> Eff es a) ->
+  Eff es a ->
+  -- | ͘
+  Eff es a
+localHandle h handler action = localCatch h action handler
+
+throw ::
+  (e <: es) =>
+  ThrowCatch ex e ->
+  ex ->
+  -- | ͘
+  Eff es a
+throw = throwCatchThrow
+
+localTry ::
+  (e <: es) =>
+  ThrowCatch ex e ->
+  Eff es a ->
+  -- | ͘
+  Eff es (Either ex a)
+localTry h action = case h of
+  MkThrowCatch ex ->
+    unsafeProvideIO $ \io ->
+      withEffToIO_ io $ \runInIO ->
+        Scoped.localTry ex (runInIO (useImpl action))
diff --git a/src/Bluefin/Internal/CloneableHandle.hs b/src/Bluefin/Internal/CloneableHandle.hs
--- a/src/Bluefin/Internal/CloneableHandle.hs
+++ b/src/Bluefin/Internal/CloneableHandle.hs
@@ -34,12 +34,13 @@
   ((forall r. (forall e. IOE e -> h e -> Eff e r) -> IO r) -> IO a) ->
   Eff es a
 withEffToIOCloneHandle io h k = do
-  withEffToIO_ io $ \runInIO -> do
-    k $ \body -> do
-      runInIO $ do
-        cloneHandleClass h $ \h' -> do
-          cloneHandleClass io $ \io' -> do
-            body (mapHandle io') (mapHandle h')
+  withClonedEnv $ do
+    withEffToIO_ io $ \runInIO -> do
+      k $ \body -> do
+        runInIO $ do
+          cloneHandleClass h $ \h' -> do
+            cloneHandleClass io $ \io' -> do
+              body (mapHandle io') (mapHandle h')
 
 newtype HandleCloner h1 h2 es
   = MkHandleCloner
@@ -76,35 +77,34 @@
 hcIOE = MkHandleCloner $ \io k -> do
   useImplIn k (mapHandle io)
 
--- | Cloning a @State@ copies its contents to a new @State@.  Changes
+-- | Cloning a @Modify@ copies its contents to a new @Modify@.  Changes
 -- to one will not effect the other.
-instance CloneableHandle (State s) where
+instance CloneableHandle (Modify s) where
   cloneableHandleImpl = MkCloneableHandleD hcState
 
-hcState :: HandleCloner (State s) (State s) e
+hcState :: HandleCloner (Modify s) (Modify s) e
 hcState = MkHandleCloner $ \st k -> do
   s <- get st
   evalState s $ \st' ->
     useImplIn k (mapHandle st')
 
-instance CloneableHandle (Exception a) where
+instance CloneableHandle (Throw a) where
   cloneableHandleImpl = MkCloneableHandleD hcException
 
-hcException :: HandleCloner (Exception ex) (Exception ex) e
+hcException :: HandleCloner (Throw ex) (Throw ex) e
 hcException = MkHandleCloner $ \ex k -> do
   useImplIn k (mapHandle ex)
 
-instance CloneableHandle (Reader r) where
+instance CloneableHandle (Ask r) where
   cloneableHandleImpl = MkCloneableHandleD hcReader
 
-hcReader :: HandleCloner (Reader r) (Reader r) e
-hcReader = MkHandleCloner $ \(MkReader s) k -> do
-  cloneHandleClass s $ \s' -> do
-    useImplIn k (MkReader (mapHandle s'))
+hcReader :: HandleCloner (Ask r) (Ask r) e
+hcReader = MkHandleCloner $ \r k -> do
+  useImplIn k (mapHandle r)
 
--- | Cloning a @HandleReader@ copies its contents to a new
--- @HandleReader@.  Changes to one will not effect the other.
-instance (CloneableHandle h) => CloneableHandle (HandleReader h) where
+-- | Cloning an @AskCapability@ copies its contents to a new
+-- @AskCapability@.  Changes to one will not effect the other.
+instance (CloneableHandle h) => CloneableHandle (AskCapability h) where
   cloneableHandleImpl = MkCloneableHandleD hcHandleReader
 
 cloneHandleClass ::
@@ -115,26 +115,26 @@
 cloneHandleClass =
   cloneHandle2 (case cloneableHandleImpl of MkCloneableHandleD c' -> c')
 
-hcHandleReader :: (CloneableHandle h) => HandleCloner (HandleReader h) (HandleReader h) e
+hcHandleReader :: (CloneableHandle h) => HandleCloner (AskCapability h) (AskCapability h) e
 hcHandleReader = MkHandleCloner $ \hr k -> do
-  h <- askHandle hr
-  cloneHandleClass h $ \h' -> do
-    runHandleReader h' $ \hr' -> do
-      useImplIn k (mapHandle hr')
+  asksHandle hr $ \h -> do
+    cloneHandleClass h $ \h' -> do
+      runHandleReader h' $ \hr' -> do
+        useImplIn k (mapHandle hr')
 
 instance
-  (TypeError (Text "Coroutine cannot be cloned. Perhaps you want an STM channel?")) =>
-  CloneableHandle (Coroutine a b)
+  (TypeError (Text "Request cannot be cloned. Perhaps you want an STM channel?")) =>
+  CloneableHandle (Request a b)
   where
   cloneableHandleImpl =
-    error "instance CloneableHandle (Coroutine a b) not implemented"
+    error "instance CloneableHandle (Request a b) not implemented"
 
 instance
-  (TypeError (Text "Writer cannot be cloned. Perhaps you want an STM channel?")) =>
-  CloneableHandle (Writer w)
+  (TypeError (Text "Tell cannot be cloned. Perhaps you want an STM channel?")) =>
+  CloneableHandle (Tell w)
   where
   cloneableHandleImpl =
-    error "instance CloneableHandle (Writer a) not implemented"
+    error "instance CloneableHandle (Tell a) not implemented"
 
 newtype (h1 :~> h2) es = MkArrow (forall e. h1 e -> h2 (e :& es))
 
@@ -254,7 +254,7 @@
 -- example:
 --
 -- @
--- data MyHandle e = MkMyHandle ('Bluefin.Exception.Exception' String e) ('Bluefin.State.State' Int e)
+-- data MyHandle e = MkMyHandle ('Bluefin.Capability.Throw.Throw' String e) ('Bluefin.Capability.Modify.Modify' Int e)
 --   deriving ('Bluefin.Compound.Generic', 'Generic1')
 --   deriving ('Bluefin.Compound.Handle') via t'Bluefin.Compound.OneWayCoercibleHandle' MyHandle
 --   deriving ('CloneableHandle') via 'GenericCloneableHandle' MyHandle
diff --git a/src/Bluefin/Internal/DslBuilder.hs b/src/Bluefin/Internal/DslBuilder.hs
--- a/src/Bluefin/Internal/DslBuilder.hs
+++ b/src/Bluefin/Internal/DslBuilder.hs
@@ -3,11 +3,7 @@
 module Bluefin.Internal.DslBuilder where
 
 import Bluefin.Internal
-import Bluefin.Internal.DslBuilderEffects
-  ( DslBuilderEffects,
-    dslBuilderEffects,
-    runDslBuilderEffects,
-  )
+import Bluefin.Internal.DslBuilderEff
 
 newtype Forall f r = MkForall {unForall :: forall es. f es r}
 
@@ -27,13 +23,13 @@
     unForall (f r)
 
 newtype DslBuilder h r
-  = MkDslBuilder {unMkDslBuilder :: Forall (DslBuilderEffects h) r}
+  = MkDslBuilder {unMkDslBuilder :: Forall (DslBuilderEff h) r}
 
 runDslBuilder :: (Handle h) => h es -> DslBuilder h r -> Eff es r
-runDslBuilder h f = runDslBuilderEffects h (unForall (unMkDslBuilder f))
+runDslBuilder h f = runDslBuilderEff h (unForall (unMkDslBuilder f))
 
 dslBuilder :: (forall e. h e -> Eff e r) -> DslBuilder h r
-dslBuilder k = MkDslBuilder (mkForall (dslBuilderEffects (useImpl . k)))
+dslBuilder k = MkDslBuilder (mkForall (dslBuilderEff (useImpl . k)))
 
 instance (Handle h) => Functor (DslBuilder h) where
   fmap f g = dslBuilder (\h -> fmap f (runDslBuilder h g))
diff --git a/src/Bluefin/Internal/DslBuilderEff.hs b/src/Bluefin/Internal/DslBuilderEff.hs
--- a/src/Bluefin/Internal/DslBuilderEff.hs
+++ b/src/Bluefin/Internal/DslBuilderEff.hs
@@ -10,6 +10,8 @@
     oneWayCoercible,
     oneWayCoercibleImpl,
   )
+import GHC.Base (oneShot)
+import GHC.IO (IO (IO))
 
 newtype DslBuilderEff h es r
   = MkDslBuilderEff {unMkDslBuilderEff :: forall e. h e -> Eff (e :& es) r}
@@ -27,12 +29,35 @@
   -- | ͘
   Eff es r
 runDslBuilderEff h f = makeOp (unMkDslBuilderEff f h)
+{-# INLINE [0] runDslBuilderEff #-}
+-- GHC's simplifier phase numbers count down toward 0. INLINE [0] keeps this
+-- wrapper intact until phase 0, the final phase, so earlier simplifications
+-- can work with the call before its body is exposed; it then strongly
+-- encourages inlining to remove the wrapper. See
+-- https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/pragmas.html#phase-control
 
+-- oneShot is essential for good performance. I don't fully understand
+-- why.
+
+runDslBuilderEffMappedArgs ::
+  forall e1 e2 es h r.
+  (Handle h, e1 <: es, e2 <: es) =>
+  h e1 ->
+  DslBuilderEff h e2 r ->
+  -- | ͘
+  Eff es r
+runDslBuilderEffMappedArgs h f =
+  runDslBuilderEff (mapHandle h) (useImplDslBuilderEff f)
+{-# INLINE runDslBuilderEffMappedArgs #-}
+
 dslBuilderEff ::
   (forall e. h e -> Eff (e :& es) r) ->
   -- | ͘
   DslBuilderEff h es r
-dslBuilderEff = MkDslBuilderEff
+dslBuilderEff f = MkDslBuilderEff $ \h -> case f h of
+  UnsafeMkEff g -> UnsafeMkEff $ oneShot $ \env -> case g env of
+    -- Expose IO's state transformer so it too can be marked one-shot
+    IO io -> IO (oneShot io)
 
 instance
   (e <: es) =>
@@ -43,15 +68,15 @@
 instance (Handle h) => Functor (DslBuilderEff h es) where
   fmap f g =
     dslBuilderEff $ \h ->
-      fmap f (runDslBuilderEff (mapHandle h) (useImplDslBuilderEff g))
+      fmap f (runDslBuilderEffMappedArgs h g)
 
 instance (Handle h) => Applicative (DslBuilderEff h es) where
   pure x = dslBuilderEff (pure (pure x))
   f <*> x = dslBuilderEff $ \h ->
-    runDslBuilderEff (mapHandle h) (useImplDslBuilderEff f)
-      <*> runDslBuilderEff (mapHandle h) (useImplDslBuilderEff x)
+    runDslBuilderEffMappedArgs h f
+      <*> runDslBuilderEffMappedArgs h x
 
 instance (Handle h) => Monad (DslBuilderEff h es) where
   m >>= f = dslBuilderEff $ \h -> do
-    r <- runDslBuilderEff (mapHandle h) (useImplDslBuilderEff m)
-    runDslBuilderEff (mapHandle h) (useImplDslBuilderEff (f r))
+    r <- runDslBuilderEffMappedArgs h m
+    runDslBuilderEffMappedArgs h (f r)
diff --git a/src/Bluefin/Internal/DslBuilderEffects.hs b/src/Bluefin/Internal/DslBuilderEffects.hs
deleted file mode 100644
--- a/src/Bluefin/Internal/DslBuilderEffects.hs
+++ /dev/null
@@ -1,48 +0,0 @@
--- This will probably be superseded by DslBuilderEff because the
--- latter has a better name
-{-# LANGUAGE DerivingVia #-}
-{-# LANGUAGE QuantifiedConstraints #-}
-
-module Bluefin.Internal.DslBuilderEffects where
-
-import Bluefin.Internal
-import Bluefin.Internal.OneWayCoercible
-  ( OneWayCoercible,
-    oneWayCoerce,
-    oneWayCoercible,
-    oneWayCoercibleImpl,
-  )
-
-newtype DslBuilderEffects h es r
-  = MkDslBuilderEffects {unMkDslBuilderEffects :: forall e. h e -> Eff (e :& es) r}
-
-useImplDslBuilderEffects :: (e <: es) => DslBuilderEffects h e r -> DslBuilderEffects h es r
-useImplDslBuilderEffects = oneWayCoerce
-
-runDslBuilderEffects :: h es -> DslBuilderEffects h es r -> Eff es r
-runDslBuilderEffects h f = makeOp (unMkDslBuilderEffects f h)
-
-dslBuilderEffects :: (forall e. h e -> Eff (e :& es) r) -> DslBuilderEffects h es r
-dslBuilderEffects = MkDslBuilderEffects
-
-instance
-  (e <: es) =>
-  OneWayCoercible (DslBuilderEffects h e r) (DslBuilderEffects h es r)
-  where
-  oneWayCoercibleImpl = oneWayCoercible
-
-instance (Handle h) => Functor (DslBuilderEffects h es) where
-  fmap f g =
-    dslBuilderEffects $ \h ->
-      fmap f (runDslBuilderEffects (mapHandle h) (useImplDslBuilderEffects g))
-
-instance (Handle h) => Applicative (DslBuilderEffects h es) where
-  pure x = dslBuilderEffects (pure (pure x))
-  f <*> x = dslBuilderEffects $ \h ->
-    runDslBuilderEffects (mapHandle h) (useImplDslBuilderEffects f)
-      <*> runDslBuilderEffects (mapHandle h) (useImplDslBuilderEffects x)
-
-instance (Handle h) => Monad (DslBuilderEffects h es) where
-  m >>= f = dslBuilderEffects $ \h -> do
-    r <- runDslBuilderEffects (mapHandle h) (useImplDslBuilderEffects m)
-    runDslBuilderEffects (mapHandle h) (useImplDslBuilderEffects (f r))
diff --git a/src/Bluefin/Internal/Examples.hs b/src/Bluefin/Internal/Examples.hs
--- a/src/Bluefin/Internal/Examples.hs
+++ b/src/Bluefin/Internal/Examples.hs
@@ -1,1086 +1,7 @@
-{-# LANGUAGE DerivingVia #-}
-{-# LANGUAGE NoMonoLocalBinds #-}
-{-# LANGUAGE NoMonomorphismRestriction #-}
-
-module Bluefin.Internal.Examples where
-
-import Bluefin.Internal hiding (b, w)
-import Bluefin.Internal.OneWayCoercible
-import Bluefin.Internal.Pipes
-  ( Producer,
-    runEffect,
-    stdinLn,
-    stdoutLn,
-    takeWhile',
-    (>->),
-  )
-import Bluefin.Internal.Pipes qualified as P
-import Control.Exception (IOException)
-import Control.Exception qualified
-import Control.Monad (forever, replicateM_, unless, when)
-import Control.Monad.IO.Class (liftIO)
-import Data.Foldable (for_)
-import Data.Monoid (Any (Any, getAny))
-import Data.Proxy (Proxy (Proxy))
-import Text.Read (readMaybe)
-import Prelude hiding
-  ( break,
-    drop,
-    head,
-    read,
-    readFile,
-    return,
-    writeFile,
-  )
-import Prelude qualified
-
-monadIOExample :: IO ()
-monadIOExample = runEff $ \io -> withMonadIO io $ liftIO $ do
-  name <- readLn
-  putStrLn ("Hello " ++ name)
-
-monadFailExample :: Either String ()
-monadFailExample = runPureEff $ try $ \e ->
-  when ((2 :: Int) > 1) $
-    withMonadFail e (fail "2 was bigger than 1")
-
-throwExample :: Either Int String
-throwExample = runPureEff $ try $ \e -> do
-  _ <- throw e 42
-  pure "No exception thrown"
-
-handleExample :: String
-handleExample = runPureEff $ handle (pure . show) $ \e -> do
-  _ <- throw e (42 :: Int)
-  pure "No exception thrown"
-
-exampleGet :: (Int, Int)
-exampleGet = runPureEff $ runModify 10 $ \st -> do
-  n <- get st
-  pure (2 * n)
-
-examplePut :: ((), Int)
-examplePut = runPureEff $ runModify 10 $ \st -> do
-  put st 30
-
-exampleModify :: ((), Int)
-exampleModify = runPureEff $ runModify 10 $ \st -> do
-  modify st (* 2)
-
-yieldExample :: ([Int], ())
-yieldExample = runPureEff $ yieldToList $ \y -> do
-  yield y 1
-  yield y 2
-  yield y 100
-
-withYieldToListExample :: Int
-withYieldToListExample = runPureEff $ withYieldToList @Int $ \y -> do
-  yield y 1
-  yield y 2
-  yield y 100
-  pure length
-
--- This shows we can use forEach at any level of nesting with
--- insertManySecond
-doubleNestedForEach ::
-  (forall e. Stream () e -> Eff (e :& es) ()) ->
-  Eff es ()
-doubleNestedForEach f =
-  withModify () $ \_ -> do
-    withModify () $ \_ -> do
-      forEach (insertManySecond . f) (\_ -> pure ())
-      pure (\_ _ -> ())
-
-forEachExample :: ([Int], ())
-forEachExample = runPureEff $ yieldToList $ \y -> do
-  forEach (inFoldable [0 .. 4]) $ \i -> do
-    yield y i
-    yield y (i * 10)
-
-ignoreStreamExample :: Int
-ignoreStreamExample = runPureEff $ ignoreStream @Int $ \y -> do
-  for_ [0 .. 4] $ \i -> do
-    yield y i
-    yield y (i * 10)
-
-  pure 42
-
--- ([1,2,3,1,2,3],())
-cycleToStreamExample :: ([Int], ())
-cycleToStreamExample = runPureEff $ yieldToList $ \yOut -> do
-  consumeStream
-    (\c -> takeConsume 6 c yOut)
-    (\yIn -> cycleToStream [1 .. 3] yIn)
-
--- ([1,2,3,4],())
-takeConsumeExample :: ([Int], ())
-takeConsumeExample = runPureEff $ yieldToList $ \yOut -> do
-  consumeStream
-    (\c -> takeConsume 4 c yOut)
-    (\yIn -> inFoldable [1 .. 10] yIn)
-
-inFoldableExample :: ([Int], ())
-inFoldableExample = runPureEff $ yieldToList $ inFoldable [1, 2, 100]
-
-enumerateExample :: ([(Int, String)], ())
-enumerateExample = runPureEff $ yieldToList $ enumerate (inFoldable ["A", "B", "C"])
-
-returnEarlyExample :: String
-returnEarlyExample = runPureEff $ withEarlyReturn $ \e -> do
-  for_ [1 :: Int .. 10] $ \i -> do
-    when (i >= 5) $
-      returnEarly e ("Returned early with " ++ show i)
-  pure "End of loop"
-
-effIOExample :: IO ()
-effIOExample = runEff $ \io -> do
-  effIO io (putStrLn "Hello world!")
-
-example1_ :: (Int, Int)
-example1_ =
-  let example1 :: Int -> Int
-      example1 n = runPureEff $ evalModify n $ \st -> do
-        n' <- get st
-        when (n' < 10) $
-          put st (n' + 10)
-        get st
-   in (example1 5, example1 12)
-
-example2_ :: ((Int, Int), (Int, Int))
-example2_ =
-  let example2 :: (Int, Int) -> (Int, Int)
-      example2 (m, n) = runPureEff $
-        evalModify m $ \sm -> do
-          evalModify n $ \sn -> do
-            do
-              n' <- get sn
-              m' <- get sm
-
-              if n' < m'
-                then put sn (n' + 10)
-                else put sm (m' + 10)
-
-            n' <- get sn
-            m' <- get sm
-
-            pure (n', m')
-   in (example2 (5, 10), example2 (12, 5))
-
-example3' :: Int -> Either String Int
-example3' n = runPureEff $
-  try $ \ex -> do
-    evalModify 0 $ \total -> do
-      for_ [1 .. n] $ \i -> do
-        soFar <- get total
-        when (soFar > 20) $ do
-          throw ex ("Became too big: " ++ show soFar)
-        put total (soFar + i)
-
-      get total
-
--- Count non-empty lines from stdin, and print a friendly message,
--- until we see "STOP".
-example3_ :: IO ()
-example3_ = runEff $ \io -> do
-  let getLineUntilStop y = withJump $ \stop -> forever $ do
-        line <- effIO io getLine
-        when (line == "STOP") $
-          jumpTo stop
-        yield y line
-
-      nonEmptyLines =
-        mapMaybe
-          ( \case
-              "" -> Nothing
-              line -> Just line
-          )
-          getLineUntilStop
-
-      enumeratedLines = enumerateFrom 1 nonEmptyLines
-
-      formattedLines =
-        mapStream
-          (\(i, line) -> show i ++ ". Hello! You said " ++ line)
-          enumeratedLines
-
-  forEach formattedLines $ \line -> effIO io (putStrLn line)
-
-awaitList ::
-  (e <: es) =>
-  [a] ->
-  IOE e ->
-  (forall e1. Consume a e1 -> Eff (e1 :& es) ()) ->
-  Eff es ()
-awaitList l io k = evalModify l $ \s -> do
-  withJump $ \done ->
-    bracket
-      (pure ())
-      (\() -> effIO io (putStrLn "Released"))
-      $ \() -> do
-        consumeEach (useImplUnder . k) $ do
-          (x, xs) <-
-            get s >>= \case
-              [] -> jumpTo done
-              x : xs -> pure (x, xs)
-          put s xs
-          pure x
-
-takeRec ::
-  (e3 <: es) =>
-  Int ->
-  (forall e. Consume a e -> Eff (e :& es) ()) ->
-  Consume a e3 ->
-  Eff es ()
-takeRec n k rec =
-  withJump $ \done -> evalModify n $ \s -> consumeEach (useImplUnder . k) $ do
-    s' <- get s
-    if s' <= 0
-      then jumpTo done
-      else do
-        modify s (subtract 1)
-        await rec
-
-mapRec ::
-  (e <: es) =>
-  (a -> b) ->
-  (forall e1. Consume b e1 -> Eff (e1 :& es) ()) ->
-  Consume a e ->
-  Eff es ()
-mapRec f = traverseRec (pure . f)
-
-traverseRec ::
-  (e <: es) =>
-  (a -> Eff es b) ->
-  (forall e1. Consume b e1 -> Eff (e1 :& es) ()) ->
-  Consume a e ->
-  Eff es ()
-traverseRec f k rec = forEach k $ \() -> do
-  r <- await rec
-  f r
-
-awaitUsage ::
-  (e1 <: es, e2 <: es) =>
-  IOE e1 ->
-  (forall e. Consume () e -> Eff (e :& es) ()) ->
-  Consume Int e2 ->
-  Eff es ()
-awaitUsage io x = do
-  mapRec (* 11) $
-    mapRec (subtract 1) $
-      takeRec 3 $
-        traverseRec (effIO io . print) $
-          useImplUnder . x
-
-awaitExample :: IO ()
-awaitExample = runEff $ \io -> do
-  awaitList [1 :: Int ..] io $ awaitUsage io $ \rec -> do
-    replicateM_ 5 (await rec)
-
-consumeStreamExample :: IO (Either String String)
-consumeStreamExample = runEff $ \io -> do
-  try $ \ex -> do
-    consumeStream
-      ( \r ->
-          bracket
-            (effIO io (putStrLn "Starting 2"))
-            (\_ -> effIO io (putStrLn "Leaving 2"))
-            $ \_ -> do
-              for_ [1 :: Int .. 100] $ \n -> do
-                b <- await r
-                effIO
-                  io
-                  ( putStrLn
-                      ("Consumed body " ++ show b ++ " at time " ++ show n)
-                  )
-              pure "Consumer finished first"
-      )
-      ( \y -> bracket
-          (effIO io (putStrLn "Starting 1"))
-          (\_ -> effIO io (putStrLn "Leaving 1"))
-          $ \_ -> do
-            for_ [1 :: Int .. 10] $ \n -> do
-              effIO io (putStrLn ("Sending " ++ show n))
-              yield y n
-              when (n > 5) $ do
-                effIO io (putStrLn "Aborting...")
-                throw ex "Aborted"
-
-            pure "Yielder finished first"
-      )
-
-consumeStreamExample2 :: IO ()
-consumeStreamExample2 = runEff $ \io -> do
-  let counter yeven yodd = for_ [0 :: Int .. 10] $ \i -> do
-        if even i
-          then yield yeven i
-          else yield yodd i
-
-  let foo yeven =
-        consumeStream
-          ( \r -> forever $ do
-              i <- await r
-              effIO io (putStrLn ("Odd: " ++ show i))
-          )
-          (counter yeven)
-
-  let bar =
-        consumeStream
-          ( \r -> forever $ do
-              i <- await r
-              effIO io (putStrLn ("Even: " ++ show i))
-          )
-          foo
-
-  bar
-
-connectExample :: IO (Either String String)
-connectExample = runEff $ \io -> do
-  try $ \ex -> do
-    connectCoroutines
-      ( \y -> bracket
-          (effIO io (putStrLn "Starting 1"))
-          (\_ -> effIO io (putStrLn "Leaving 1"))
-          $ \_ -> do
-            for_ [1 :: Int .. 10] $ \n -> do
-              effIO io (putStrLn ("Sending " ++ show n))
-              yield y n
-              when (n > 5) $ do
-                effIO io (putStrLn "Aborting...")
-                throw ex "Aborted"
-
-            pure "Yielder finished first"
-      )
-      ( \binit r ->
-          bracket
-            (effIO io (putStrLn "Starting 2"))
-            (\_ -> effIO io (putStrLn "Leaving 2"))
-            $ \_ -> do
-              effIO io (putStrLn ("Consumed intial " ++ show binit))
-              for_ [1 :: Int .. 100] $ \n -> do
-                b <- await r
-                effIO
-                  io
-                  ( putStrLn
-                      ("Consumed body " ++ show b ++ " at time " ++ show n)
-                  )
-              pure "Consumer finished first"
-      )
-
-zipCoroutinesExample :: IO ()
-zipCoroutinesExample = runEff $ \io -> do
-  let m1 y = do
-        r <- yieldCoroutine y 1
-        evalModify r $ \rs -> do
-          for_ [1 .. 10 :: Int] $ \i -> do
-            r' <- get rs
-            r'' <- yieldCoroutine y (r' + i)
-            put rs r''
-
-  let m2 y = do
-        r <- yieldCoroutine y 1
-        evalModify r $ \rs -> do
-          for_ [1 .. 5 :: Int] $ \i -> do
-            r' <- get rs
-            r'' <- yieldCoroutine y (r' - i)
-            put rs r''
-
-  forEach (\c -> zipCoroutines c m1 m2) $ \i@(i1, i2) -> do
-    effIO io (print i)
-    pure (i1 + i2)
-
--- Count the number of (strictly) positives and (strictly) negatives
--- in a list, unless we see a zero, in which case we bail with an
--- error message.
-countPositivesNegatives :: [Int] -> String
-countPositivesNegatives is = runPureEff $
-  evalModify (0 :: Int) $ \positives -> do
-    r <- try $ \ex ->
-      evalModify (0 :: Int) $ \negatives -> do
-        for_ is $ \i -> do
-          case compare i 0 of
-            GT -> modify positives (+ 1)
-            EQ -> throw ex ()
-            LT -> modify negatives (+ 1)
-
-        p <- get positives
-        n <- get negatives
-
-        pure $
-          "Positives: "
-            ++ show p
-            ++ ", negatives "
-            ++ show n
-
-    case r of
-      Right r' -> pure r'
-      Left () -> do
-        p <- get positives
-        pure $
-          "We saw a zero, but before that there were "
-            ++ show p
-            ++ " positives"
-
--- How to make compound effects
-
-type MyHandle = Compound (Modify Int) (Throw String)
-
-myInc :: (e <: es) => MyHandle e -> Eff es ()
-myInc h = withCompound h (\s _ -> modify s (+ 1))
-
-myBail :: (e <: es) => MyHandle e -> Eff es r
-myBail h = withCompound h $ \s e -> do
-  i <- get s
-  throw e ("Current state was: " ++ show i)
-
-runMyHandle ::
-  (forall e. MyHandle e -> Eff (e :& es) a) ->
-  Eff es (Either String (a, Int))
-runMyHandle f =
-  try $ \e -> do
-    runModify 0 $ \s -> do
-      runCompound s e f
-
-compoundExample :: Either String (a, Int)
-compoundExample = runPureEff $ runMyHandle $ \h -> do
-  myInc h
-  myInc h
-  myBail h
-
-countExample :: IO ()
-countExample = runEff $ \io -> do
-  evalModify @Int 0 $ \sn -> do
-    withJump $ \break -> forever $ do
-      n <- get sn
-      when (n >= 10) (jumpTo break)
-      effIO io (print n)
-      modify sn (+ 1)
-
-writerExample1 :: Bool
-writerExample1 = getAny $ runPureEff $ execWriter $ \w -> do
-  for_ [] $ \_ -> tell w (Any True)
-
-writerExample2 :: Bool
-writerExample2 = getAny $ runPureEff $ execWriter $ \w -> do
-  for_ [1 :: Int .. 10] $ \_ -> tell w (Any True)
-
-while :: Eff es Bool -> Eff es a -> Eff es ()
-while condM body =
-  withJump $ \break_ -> do
-    forever $ do
-      cond <- insertFirst condM
-      unless cond (jumpTo break_)
-      insertFirst body
-
-stateSourceExample :: Int
-stateSourceExample = runPureEff $ withStateSource $ \source -> do
-  n <- newState source 5
-  total <- newState source 0
-
-  withJump $ \done -> forever $ do
-    n' <- get n
-    modify total (+ n')
-    when (n' == 0) $ jumpTo done
-    modify n (subtract 1)
-
-  get total
-
-incrementReadLine ::
-  (e1 <: es, e2 <: es, e3 <: es) =>
-  Modify Int e1 ->
-  Throw String e2 ->
-  IOE e3 ->
-  Eff es ()
-incrementReadLine state exception io = do
-  withJump $ \break -> forever $ do
-    line <- effIO io getLine
-    i <- case readMaybe line of
-      Nothing ->
-        throw exception ("Couldn't read: " ++ line)
-      Just i ->
-        pure i
-
-    when (i == 0) $
-      jumpTo break
-
-    modify state (+ i)
-
-runIncrementReadLine :: IO (Either String Int)
-runIncrementReadLine = runEff $ \io -> do
-  try $ \exception -> do
-    ((), r) <- runModify 0 $ \state -> do
-      incrementReadLine state exception io
-    pure r
-
--- Counter 1
-
-newtype Counter1 e = MkCounter1 (Modify Int e)
-
-incCounter1 :: (e <: es) => Counter1 e -> Eff es ()
-incCounter1 (MkCounter1 st) = modify st (+ 1)
-
-runCounter1 ::
-  (forall e. Counter1 e -> Eff (e :& es) r) ->
-  Eff es Int
-runCounter1 k =
-  evalModify 0 $ \st -> do
-    _ <- k (MkCounter1 st)
-    get st
-
-exampleCounter1 :: Int
-exampleCounter1 = runPureEff $ runCounter1 $ \c -> do
-  incCounter1 c
-  incCounter1 c
-  incCounter1 c
-
--- > exampleCounter1
--- 3
-
--- Counter 2
-
-data Counter2 e1 e2 = MkCounter2 (Modify Int e1) (Throw () e2)
-
-incCounter2 :: (e1 <: es, e2 <: es) => Counter2 e1 e2 -> Eff es ()
-incCounter2 (MkCounter2 st ex) = do
-  count <- get st
-  when (count >= 10) $
-    throw ex ()
-  put st (count + 1)
-
-runCounter2 ::
-  (forall e1 e2. Counter2 e1 e2 -> Eff (e2 :& e1 :& es) r) ->
-  Eff es Int
-runCounter2 k =
-  evalModify 0 $ \st -> do
-    _ <- try $ \ex -> do
-      k (MkCounter2 st ex)
-    get st
-
-exampleCounter2 :: Int
-exampleCounter2 = runPureEff $ runCounter2 $ \c ->
-  forever $
-    incCounter2 c
-
--- > exampleCounter2
--- 10
-
--- Counter 3
-
-data Counter3 e = MkCounter3 (Modify Int e) (Throw () e)
-
-incCounter3 :: (e <: es) => Counter3 e -> Eff es ()
-incCounter3 (MkCounter3 st ex) = do
-  count <- get st
-  when (count >= 10) $
-    throw ex ()
-  put st (count + 1)
-
-runCounter3 ::
-  (forall e. Counter3 e -> Eff (e :& es) r) ->
-  Eff es Int
-runCounter3 k =
-  evalModify 0 $ \st -> do
-    _ <- try $ \ex -> do
-      useImplIn k (MkCounter3 (mapHandle st) (mapHandle ex))
-    get st
-
-exampleCounter3 :: Int
-exampleCounter3 = runPureEff $ runCounter3 $ \c ->
-  forever $
-    incCounter3 c
-
--- > exampleCounter3
--- 10
-
--- Counter 3B
-
-newtype Counter3B e = MkCounter3B (IOE e)
-
-incCounter3B :: (e <: es) => Counter3B e -> Eff es ()
-incCounter3B (MkCounter3B io) =
-  effIO io (putStrLn "You tried to increment the counter")
-
-runCounter3B ::
-  (e1 <: es) =>
-  IOE e1 ->
-  (forall e. Counter3B e -> Eff (e :& es) r) ->
-  Eff es r
-runCounter3B io k = useImplIn k (MkCounter3B (mapHandle io))
-
-exampleCounter3B :: IO ()
-exampleCounter3B = runEff $ \io -> runCounter3B io $ \c -> do
-  incCounter3B c
-  incCounter3B c
-  incCounter3B c
-
--- ghci> exampleCounter3B
--- You tried to increment the counter
--- You tried to increment the counter
--- You tried to increment the counter
-
--- Counter 4
-
-data Counter4 e
-  = MkCounter4 (Modify Int e) (Throw () e) (Stream String e)
-
-incCounter4 :: (e <: es) => Counter4 e -> Eff es ()
-incCounter4 (MkCounter4 st ex y) = do
-  count <- get st
-
-  when (even count) $
-    yield y "Count was even"
-
-  when (count >= 10) $
-    throw ex ()
-
-  put st (count + 1)
-
-getCounter4 :: (e <: es) => Counter4 e -> String -> Eff es Int
-getCounter4 (MkCounter4 st _ y) msg = do
-  yield y msg
-  get st
-
-runCounter4 ::
-  (e1 <: es) =>
-  Stream String e1 ->
-  (forall e. Counter4 e -> Eff (e :& es) r) ->
-  Eff es Int
-runCounter4 y k =
-  evalModify 0 $ \st -> do
-    _ <- try $ \ex -> do
-      useImplIn k (MkCounter4 (mapHandle st) (mapHandle ex) (mapHandle y))
-    get st
-
-exampleCounter4 :: ([String], Int)
-exampleCounter4 = runPureEff $ yieldToList $ \y -> do
-  runCounter4 y $ \c -> do
-    incCounter4 c
-    incCounter4 c
-    n <- getCounter4 c "I'm getting the counter"
-    when (n == 2) $
-      yield y "n was 2, as expected"
-
--- > exampleCounter4
--- (["Count was even","I'm getting the counter","n was 2, as expected"],2)
-
--- Counter 5
-
-data Counter5 e = MkCounter5
-  { incCounter5Impl :: Eff e (),
-    getCounter5Impl :: String -> Eff e Int
-  }
-  deriving (Generic)
-  deriving (Handle) via OneWayCoercibleHandle Counter5
-
-instance (e <: es) => OneWayCoercible (Counter5 e) (Counter5 es) where
-  oneWayCoercibleImpl = gOneWayCoercible
-
-incCounter5 :: (e <: es) => Counter5 e -> Eff es ()
-incCounter5 e = incCounter5Impl (mapHandle e)
-
-getCounter5 :: (e <: es) => Counter5 e -> String -> Eff es Int
-getCounter5 e msg = getCounter5Impl (mapHandle e) msg
-
-runCounter5 ::
-  (e1 <: es) =>
-  Stream String e1 ->
-  (forall e. Counter5 e -> Eff (e :& es) r) ->
-  Eff es Int
-runCounter5 y k =
-  evalModify 0 $ \st -> do
-    _ <- try $ \ex -> do
-      useImplIn
-        k
-        ( MkCounter5
-            { incCounter5Impl = do
-                count <- get st
-
-                when (even count) $
-                  yield y "Count was even"
-
-                when (count >= 10) $
-                  throw ex ()
-
-                put st (count + 1),
-              getCounter5Impl = \msg -> do
-                yield y msg
-                get st
-            }
-        )
-    get st
-
-exampleCounter5 :: ([String], Int)
-exampleCounter5 = runPureEff $ yieldToList $ \y -> do
-  runCounter5 y $ \c -> do
-    incCounter5 c
-    incCounter5 c
-    n <- getCounter5 c "I'm getting the counter"
-    when (n == 2) $
-      yield y "n was 2, as expected"
-
--- > exampleCounter5
--- (["Count was even","I'm getting the counter","n was 2, as expected"],2)
-
--- Counter 6
-
-data Counter6 e = MkCounter6
-  { incCounter6Impl :: Eff e (),
-    counter6Modify :: Modify Int e,
-    counter6Stream :: Stream String e
-  }
-  deriving (Generic)
-  deriving (Handle) via OneWayCoercibleHandle Counter6
-
-instance (e <: es) => OneWayCoercible (Counter6 e) (Counter6 es) where
-  oneWayCoercibleImpl = gOneWayCoercible
-
-incCounter6 :: (e <: es) => Counter6 e -> Eff es ()
-incCounter6 e = incCounter6Impl (mapHandle e)
-
-getCounter6 :: (e <: es) => Counter6 e -> String -> Eff es Int
-getCounter6 (MkCounter6 _ st y) msg = do
-  yield y msg
-  get st
-
-runCounter6 ::
-  (e1 <: es) =>
-  Stream String e1 ->
-  (forall e. Counter6 e -> Eff (e :& es) r) ->
-  Eff es Int
-runCounter6 y k =
-  evalModify 0 $ \st -> do
-    _ <- try $ \ex -> do
-      useImplIn
-        k
-        ( MkCounter6
-            { incCounter6Impl = do
-                count <- get st
-
-                when (even count) $
-                  yield y "Count was even"
-
-                when (count >= 10) $
-                  throw ex ()
-
-                put st (count + 1),
-              counter6Modify = mapHandle st,
-              counter6Stream = mapHandle y
-            }
-        )
-    get st
-
-exampleCounter6 :: ([String], Int)
-exampleCounter6 = runPureEff $ yieldToList $ \y -> do
-  runCounter6 y $ \c -> do
-    incCounter6 c
-    incCounter6 c
-    n <- getCounter6 c "I'm getting the counter"
-    when (n == 2) $
-      yield y "n was 2, as expected"
-
--- > exampleCounter6
--- (["Count was even","I'm getting the counter","n was 2, as expected"],2)
-
--- Counter 7
-
-data Counter7 e = MkCounter7
-  { incCounter7Impl :: forall e'. Throw () e' -> Eff (e' :& e) (),
-    counter7Modify :: Modify Int e,
-    counter7Stream :: Stream String e
-  }
-  deriving (Handle) via OneWayCoercibleHandle Counter7
-
--- | The "forall" in the type of @incCounter7@ means that we can't
--- derive the @OneWayCoercible@ instance with 'gOneWayCoercible' so
--- instead we use @oneWayCoercibleTrustMe@.
-instance (e <: es) => OneWayCoercible (Counter7 e) (Counter7 es) where
-  oneWayCoercibleImpl = oneWayCoercibleTrustMe $ \c ->
-    MkCounter7
-      { incCounter7Impl = \ex -> useImplUnder (incCounter7Impl c ex),
-        counter7Modify = mapHandle (counter7Modify c),
-        counter7Stream = mapHandle (counter7Stream c)
-      }
-
-incCounter7 ::
-  (e <: es, e1 <: es) => Counter7 e -> Throw () e1 -> Eff es ()
-incCounter7 e ex = makeOp (incCounter7Impl (mapHandle e) (mapHandle ex))
-
-getCounter7 :: (e <: es) => Counter7 e -> String -> Eff es Int
-getCounter7 (MkCounter7 _ st y) msg = do
-  yield y msg
-  get st
-
-runCounter7 ::
-  (e1 <: es) =>
-  Stream String e1 ->
-  (forall e. Counter7 e -> Eff (e :& es) r) ->
-  Eff es Int
-runCounter7 y k =
-  evalModify 0 $ \st -> do
-    _ <-
-      useImplIn
-        k
-        ( MkCounter7
-            { incCounter7Impl = \ex -> do
-                count <- get st
-
-                when (even count) $
-                  yield y "Count was even"
-
-                when (count >= 10) $
-                  throw ex ()
-
-                put st (count + 1),
-              counter7Modify = mapHandle st,
-              counter7Stream = mapHandle y
-            }
-        )
-    get st
-
-exampleCounter7A :: ([String], Int)
-exampleCounter7A = runPureEff $ yieldToList $ \y -> do
-  handle (\() -> pure (-42)) $ \ex ->
-    runCounter7 y $ \c -> do
-      incCounter7 c ex
-      incCounter7 c ex
-      n <- getCounter7 c "I'm getting the counter"
-      when (n == 2) $
-        yield y "n was 2, as expected"
-
--- > exampleCounter7A
--- (["Count was even","I'm getting the counter","n was 2, as expected"],2)
-
-exampleCounter7B :: ([String], Int)
-exampleCounter7B = runPureEff $ yieldToList $ \y -> do
-  handle (\() -> pure (-42)) $ \ex ->
-    runCounter7 y $ \c -> do
-      forever (incCounter7 c ex)
-
--- > exampleCounter7B
--- (["Count was even","Count was even","Count was even","Count was even","Count was even","Count was even"],-42)
-
--- FileSystem
-
-data FileSystem es = MkFileSystem
-  { readFileImpl :: FilePath -> Eff es String,
-    writeFileImpl :: FilePath -> String -> Eff es ()
-  }
-  deriving (Generic)
-  deriving (Handle) via OneWayCoercibleHandle FileSystem
-
-instance (e <: es) => OneWayCoercible (FileSystem e) (FileSystem es) where
-  oneWayCoercibleImpl = gOneWayCoercible
-
-readFile :: (e <: es) => FileSystem e -> FilePath -> Eff es String
-readFile fs filepath = readFileImpl (mapHandle fs) filepath
-
-writeFile :: (e <: es) => FileSystem e -> FilePath -> String -> Eff es ()
-writeFile fs filepath contents =
-  writeFileImpl (mapHandle fs) filepath contents
-
-runFileSystemPure ::
-  (e1 <: es) =>
-  Throw String e1 ->
-  [(FilePath, String)] ->
-  (forall e2. FileSystem e2 -> Eff (e2 :& es) r) ->
-  Eff es r
-runFileSystemPure ex fs0 k =
-  evalModify fs0 $ \fs ->
-    useImplIn
-      k
-      MkFileSystem
-        { readFileImpl = \path -> do
-            fs' <- get fs
-            case lookup path fs' of
-              Nothing ->
-                throw ex ("File not found: " <> path)
-              Just s -> pure s,
-          writeFileImpl = \path contents ->
-            modify fs ((path, contents) :)
-        }
-
-runFileSystemIO ::
-  forall e1 e2 es r.
-  (e1 <: es, e2 <: es) =>
-  Throw String e1 ->
-  IOE e2 ->
-  (forall e. FileSystem e -> Eff (e :& es) r) ->
-  Eff es r
-runFileSystemIO ex io k =
-  useImplIn
-    k
-    MkFileSystem
-      { readFileImpl =
-          adapt . Prelude.readFile,
-        writeFileImpl =
-          \path -> adapt . Prelude.writeFile path
-      }
-  where
-    adapt :: (e1 <: ess, e2 <: ess) => IO a -> Eff ess a
-    adapt m =
-      effIO io (Control.Exception.try @IOException m) >>= \case
-        Left e -> throw ex (show e)
-        Right r -> pure r
-
-action :: (e <: es) => FileSystem e -> Eff es String
-action fs = do
-  file <- readFile fs "/dev/null"
-  when (length file == 0) $ do
-    writeFile fs "/tmp/bluefin" "Hello!\n"
-  readFile fs "/tmp/doesn't exist"
-
-exampleRunFileSystemPure :: Either String String
-exampleRunFileSystemPure = runPureEff $ try $ \ex ->
-  runFileSystemPure ex [("/dev/null", "")] action
-
--- > exampleRunFileSystemPure
--- Left "File not found: /tmp/doesn't exist"
-
-exampleRunFileSystemIO :: IO (Either String String)
-exampleRunFileSystemIO = runEff $ \io -> try $ \ex ->
-  runFileSystemIO ex io action
-
--- > exampleRunFileSystemIO
--- Left "/tmp/doesn't exist: openFile: does not exist (No such file or directory)"
--- \$ cat /tmp/bluefin
--- Hello!
-
--- instance Handle example
-
-data Application e = MkApplication
-  { queryDatabase :: String -> Int -> Eff e [String],
-    applicationModify :: Modify (Int, Bool) e,
-    logger :: Stream String e
-  }
-  deriving (Generic)
-  deriving (Handle) via OneWayCoercibleHandle Application
-
-instance (e <: es) => OneWayCoercible (Application e) (Application es) where
-  oneWayCoercibleImpl = gOneWayCoercible
-
--- This example shows a case where we can use @bracket@ polymorphically
--- in order to perform correct cleanup if @es@ is instantiated to a
--- set of effects that includes exceptions.
-polymorphicBracket ::
-  (st <: es) =>
-  Modify (Integer, Bool) st ->
-  Eff es () ->
-  Eff es ()
-polymorphicBracket st act =
-  bracket
-    (pure ())
-    -- Always set the boolean indicating that we have terminated
-    (\_ -> modify st (\(c, _b) -> (c, True)))
-    -- Perform the given effectful action, then increment the counter
-    (\_ -> do act; modify st (\(c, b) -> ((c + 1), b)))
-
--- Results in (1, True)
-polymorphicBracketExample1 :: (Integer, Bool)
-polymorphicBracketExample1 =
-  runPureEff $ do
-    (_res, st) <- runModify (0, False) $ \st -> polymorphicBracket st (pure ())
-    pure st
-
--- Results in (0, True)
-polymorphicBracketExample2 :: (Integer, Bool)
-polymorphicBracketExample2 =
-  runPureEff $ do
-    (_res, st) <- runModify (0, False) $ \st -> try @Int $ \e -> polymorphicBracket st (throw e 42)
-    pure st
-
-pipesExample1 :: IO ()
-pipesExample1 = runEff $ \io -> runEffect (count >-> P.print io)
-  where
-    count :: (e <: es) => Producer Int e -> Eff es ()
-    count p = for_ [1 .. 5] $ \i -> P.yield p i
-
-pipesExample2 :: IO String
-pipesExample2 = runEff $ \io -> runEffect $ do
-  stdinLn io >-> takeWhile' (/= "quit") >-> stdoutLn io
-
--- Acquiring resource
--- 1
--- 2
--- 3
--- 4
--- 5
--- Releasing resource
--- Finishing
-promptCoroutine :: IO ()
-promptCoroutine = runEff $ \io -> do
-  -- consumeStream connects a consumer to a producer
-  consumeStream
-    -- Like a pipes Consumer.  Prints the first five elements it
-    -- awaits.
-    ( \r -> for_ [1 :: Int .. 5] $ \_ -> do
-        v <- await r
-        effIO io (print v)
-    )
-    -- Like a pipes Producer. Yields successive integers indefinitely.
-    -- Unlike in pipes, we can simply use Bluefin's standard bracket
-    -- for prompt release of a resource
-    ( \y ->
-        bracket
-          (effIO io (putStrLn "Acquiring resource"))
-          (\_ -> effIO io (putStrLn "Releasing resource"))
-          (\_ -> for_ [1 :: Int ..] $ \i -> yield y i)
-    )
-  effIO io (putStrLn "Finishing")
-
-rethrowIOExample :: IO ()
-rethrowIOExample = runEff $ \io -> do
-  r <- try $ \ex -> do
-    rethrowIO @Control.Exception.IOException io ex $ do
-      effIO io (Prelude.readFile "/tmp/doesnt-exist")
-
-  effIO io $ putStrLn $ case r of
-    Left e -> "Caught IOException:\n" ++ show e
-    Right contents -> contents
-
--- | The "forall" in the type of @localRImpl@ means that we can't
--- derive the @OneWayCoercible@ instance with 'gOneWayCoercible' so
--- instead we use @oneWayCoercibleTrustMe@.
-data DynamicReader r e = DynamicReader
-  { askLRImpl :: Eff e r,
-    localLRImpl :: forall e' a. (r -> r) -> Eff e' a -> Eff (e' :& e) a
-  }
-  deriving (Handle) via OneWayCoercibleHandle (DynamicReader r)
-
-instance
-  (e <: es) =>
-  OneWayCoercible (DynamicReader r e) (DynamicReader r es)
-  where
-  oneWayCoercibleImpl = oneWayCoercibleTrustMe $ \h ->
-    DynamicReader
-      { askLRImpl = useImpl (askLRImpl h),
-        localLRImpl = \f k -> useImplUnder (localLRImpl h f k)
-      }
-
-askLR ::
-  (e <: es) =>
-  DynamicReader r e ->
-  Eff es r
-askLR c = askLRImpl (mapHandle c)
-
-localLR ::
-  (e <: es) =>
-  DynamicReader r e ->
-  (r -> r) ->
-  Eff es a ->
-  Eff es a
-localLR c f m = makeOp (localLRImpl (mapHandle c) f m)
-
-runDynamicReader ::
-  r ->
-  (forall e. DynamicReader r e -> Eff (e :& es) a) ->
-  Eff es a
-runDynamicReader r k =
-  runReader r $ \h -> do
-    useImplIn
-      k
-      DynamicReader
-        { askLRImpl = ask h,
-          localLRImpl = \f k' -> local h f (useImpl k')
-        }
+module Bluefin.Internal.Examples where
+
+import Bluefin.Internal
+import Data.Proxy (Proxy (Proxy))
 
 -- Fails to compile unless '(e <: es) => e <: (x :& es)' is incoherent
 -- (otherwise I guess it "commits to it too soon")
diff --git a/src/Bluefin/Internal/Exception.hs b/src/Bluefin/Internal/Exception.hs
--- a/src/Bluefin/Internal/Exception.hs
+++ b/src/Bluefin/Internal/Exception.hs
@@ -7,7 +7,7 @@
 
 import Bluefin.Internal
   ( Eff,
-    Exception (..),
+    Throw (..),
     Handle,
     OneWayCoercibleHandle (..),
     effIO,
@@ -156,7 +156,7 @@
   forall ex r a es.
   (r -> ex -> Eff es a) ->
   -- | ͘
-  MakeExceptions r a (Exception ex) es
+  MakeExceptions r a (Throw ex) es
 catchWithResource f = MkMakeExceptions $ unsafeProvideIO $ \io -> do
   scopedEx <- effIO io (SE.newException @ex)
   let hk = MkHandledKey scopedEx (flip f)
diff --git a/src/Bluefin/Internal/Exception/Scoped.hs b/src/Bluefin/Internal/Exception/Scoped.hs
--- a/src/Bluefin/Internal/Exception/Scoped.hs
+++ b/src/Bluefin/Internal/Exception/Scoped.hs
@@ -2,6 +2,7 @@
   ( Exception,
     InFlight,
     try,
+    localTry,
     throw,
     newException,
     checkException,
@@ -17,9 +18,13 @@
 try :: (Exception e -> IO a) -> IO (Either e a)
 try k = do
   ex <- newException
+  localTry ex (k ex)
+
+localTry :: Exception e -> IO a -> IO (Either e a)
+localTry ex action =
   tryJust
     (checkException ex)
-    (k ex)
+    action
 
 throw :: Exception e -> e -> IO a
 throw ex e = throwIO (MkInFlight ex e)
diff --git a/src/Bluefin/Internal/GadtEffect.hs b/src/Bluefin/Internal/GadtEffect.hs
--- a/src/Bluefin/Internal/GadtEffect.hs
+++ b/src/Bluefin/Internal/GadtEffect.hs
@@ -6,7 +6,7 @@
   ( Eff,
     Effects,
     Handle,
-    HandleReader,
+    AskCapability,
     OneWayCoercibleHandle (..),
     localHandle,
     mapHandle,
@@ -69,7 +69,7 @@
 --   forall es e1 e2 r.
 --   (e1 \<: es, e2 \<: es) =>
 --   t'Bluefin.IO.IOE' e1 ->
---   t'Bluefin.Exception.Exception' t'Control.Exception.IOException' e2 ->
+--   t'Bluefin.Capability.Throw.Throw' t'Control.Exception.IOException' e2 ->
 --   (forall e. 'Send' FileSystem e -> Eff (e :& es) r) ->
 --   Eff es r
 -- runFileSystem io ex = 'interpret' $ \\case
@@ -129,7 +129,7 @@
 -- augmentOp2Interpose ::
 --   (e1 \<: es, e2 \<: es) =>
 --   IOE e2 ->
---   t'Bluefin.HandleReader.HandleReader' (Send E) e1 ->
+--   t'Bluefin.Capability.AskCapability.AskCapability' (Send E) e1 ->
 --   Eff es r ->
 --   Eff es r
 -- augmentOp2Interpose io = 'interpose' $ \\fc -> \\case
@@ -149,7 +149,7 @@
 -- augmentOp2Interpose ::
 --   (e1 \<: es, e2 \<: es) =>
 --   IOE e2 ->
---   t'Bluefin.HandleReader.HandleReader' (Send E) e1 ->
+--   t'Bluefin.Capability.AskCapability.AskCapability' (Send E) e1 ->
 --   Eff es r ->
 --   Eff es r
 -- augmentOp2Interpose io = 'interpose' $ \\fc -> \\case
@@ -162,7 +162,7 @@
   -- the original effect handler, which is passed as the argument
   (Send f es -> EffectHandler f es) ->
   -- | Original effect handler
-  HandleReader (Send f) e1 ->
+  AskCapability (Send f) e1 ->
   -- | Within this block, @send@ has the implementation given above.
   Eff es r ->
   Eff es r
diff --git a/src/Bluefin/Internal/OneWayCoercible.hs b/src/Bluefin/Internal/OneWayCoercible.hs
--- a/src/Bluefin/Internal/OneWayCoercible.hs
+++ b/src/Bluefin/Internal/OneWayCoercible.hs
@@ -1,3 +1,13 @@
+-- | 'OneWayCoercible' morally belongs to GHC as erased,
+-- compiler-checked evidence of representational "castability"
+-- (i.e. conversion in one direction only), just like 'Coercible` is
+-- evidence of representational equality (i.e. "castability" in both
+-- directions).  But such a feature is missing in GHC and Bluefin
+-- simulates it with an ordinary class.  To obtain zero-cost
+-- conversions it must trust the evidence without evaluating its
+-- dictionary, introducing potential unsafety when 'OneWayCoercible'
+-- instances are not valid.
+
 {-# OPTIONS_HADDOCK not-home #-}
 
 module Bluefin.Internal.OneWayCoercible
@@ -46,6 +56,9 @@
 oneWayCoerce :: forall a b. (OneWayCoercible a b) => a -> b
 oneWayCoerce = oneWayCoerceWith (oneWayCoercion @a @b)
 
+unsafeOneWayCoerce :: forall a b. (OneWayCoercible a b) => a -> b
+unsafeOneWayCoerce = unsafeCoerce
+
 oneWayCoerceWith :: OneWayCoercion a b -> a -> b
 oneWayCoerceWith (MkOneWayCoercion Coercion) = coerce
 
@@ -95,6 +108,18 @@
 trans c1 c2 = case unsafeCoercionOfOneWayCoercion c1 of
   Coercion -> case unsafeCoercionOfOneWayCoercion c2 of
     Coercion -> MkOneWayCoercion Coercion
+
+trans3D ::
+  forall t1 t2 t3 t4.
+  OneWayCoercibleD t1 t2 ->
+  OneWayCoercibleD t2 t3 ->
+  OneWayCoercibleD t3 t4 ->
+  OneWayCoercibleD t1 t4
+trans3D
+  (MkOneWayCoercibleD t1t2)
+  (MkOneWayCoercibleD t2t3)
+  (MkOneWayCoercibleD t3t4) =
+    MkOneWayCoercibleD ((t1t2 `trans` t2t3) `trans` t3t4)
 
 class GOneWayCoercible a b
 
diff --git a/src/Bluefin/Internal/Pipes.hs b/src/Bluefin/Internal/Pipes.hs
deleted file mode 100644
--- a/src/Bluefin/Internal/Pipes.hs
+++ /dev/null
@@ -1,285 +0,0 @@
-module Bluefin.Internal.Pipes where
-
-import Bluefin.Internal
-  ( Coroutine,
-    Eff,
-    IOE,
-    effIO,
-    evalState,
-    forEach,
-    get,
-    mapHandle,
-    put,
-    receiveStream,
-    returnEarly,
-    useImpl,
-    useImplIn,
-    withEarlyReturn,
-    yieldCoroutine,
-    (:&),
-    type (<:),
-  )
-import Bluefin.Internal qualified
-import Control.Monad (forever)
-import Data.Foldable (for_)
-import Data.Void (Void, absurd)
-import Prelude hiding (break, print, takeWhile)
-import Prelude qualified
-
-data Proxy a' a b' b e = MkProxy (Coroutine a' a e) (Coroutine b b' e)
-
-type Pipe a = Proxy () a ()
-
-type Producer = Proxy Void () ()
-
-type Consumer a = Pipe a Void
-
-type Effect = Producer Void
-
-infixl 7 >->
-
-(>->) ::
-  (e1 <: es) =>
-  (forall e. Proxy a' a () b e -> Eff (e :& es) r) ->
-  (forall e. Proxy () b c' c e -> Eff (e :& es) r) ->
-  Proxy a' a c' c e1 ->
-  -- | ͘
-  Eff es r
-(>->) k1 k2 (MkProxy c1 c2) =
-  receiveStream
-    (\c -> useImplIn k2 (MkProxy (mapHandle c) (mapHandle c2)))
-    (\s -> useImplIn k1 (MkProxy (mapHandle c1) (mapHandle s)))
-
-infixr 7 <-<
-
-(<-<) ::
-  (e1 <: es) =>
-  (forall e. Proxy () b c' c e -> Eff (e :& es) r) ->
-  (forall e. Proxy a' a () b e -> Eff (e :& es) r) ->
-  Proxy a' a c' c e1 ->
-  -- | ͘
-  Eff es r
-k1 <-< k2 = k2 >-> k1
-
-for ::
-  (e1 <: es) =>
-  (forall e. Proxy x' x b' b e -> Eff (e :& es) a') ->
-  (b -> forall e. Proxy x' x c' c e -> Eff (e :& es) b') ->
-  Proxy x' x c' c e1 ->
-  -- | ͘
-  Eff es a'
-for k1 k2 (MkProxy c1 c2) =
-  forEach (\bk -> useImplIn k1 (MkProxy (mapHandle c1) (mapHandle bk))) $ \b_ ->
-    useImplIn (k2 b_) (MkProxy (mapHandle c1) (mapHandle c2))
-
-infixr 4 ~>
-
-(~>) ::
-  (e1 <: es) =>
-  (a -> forall e. Proxy x' x b' b e -> Eff (e :& es) a') ->
-  (b -> forall e. Proxy x' x c' c e -> Eff (e :& es) b') ->
-  a ->
-  Proxy x' x c' c e1 ->
-  -- | ͘
-  Eff es a'
-(k1 ~> k2) a = for (k1 a) k2
-
-infixl 4 <~
-
-(<~) ::
-  (e1 <: es) =>
-  (b -> forall e. Proxy x' x c' c e -> Eff (e :& es) b') ->
-  (a -> forall e. Proxy x' x b' b e -> Eff (e :& es) a') ->
-  a ->
-  Proxy x' x c' c e1 ->
-  -- | ͘
-  Eff es a'
-k2 <~ k1 = k1 ~> k2
-
-reverseProxy :: Proxy a' a b' b e -> Proxy b b' a a' e
-reverseProxy (MkProxy c1 c2) = MkProxy c2 c1
-
-infixl 5 >~
-
-(>~) ::
-  (e1 <: es) =>
-  (forall e. Proxy a' a y' y e -> Eff (e :& es) b) ->
-  (forall e. Proxy () b y' y e -> Eff (e :& es) c) ->
-  Proxy a' a y' y e1 ->
-  -- | ͘
-  Eff es c
-(>~) k1 k2 p =
-  for
-    ( \p1 ->
-        k2 (reverseProxy p1)
-    )
-    (\() p1 -> k1 (reverseProxy p1))
-    (reverseProxy p)
-
-infixr 5 ~<
-
-(~<) ::
-  (e1 <: es) =>
-  (forall e. Proxy () b y' y e -> Eff (e :& es) c) ->
-  (forall e. Proxy a' a y' y e -> Eff (e :& es) b) ->
-  Proxy a' a y' y e1 ->
-  -- | ͘
-  Eff es c
-(~<) k1 k2 = (>~) k2 k1
-
-cat :: Pipe a a e -> Eff (e :& es) r
-cat (MkProxy c1 c2) = forever $ do
-  a <- yieldCoroutine c1 ()
-  yieldCoroutine c2 a
-
-runEffect ::
-  (forall e. Effect e -> Eff (e :& es) r) ->
-  -- | ͘
-  Eff es r
-runEffect k =
-  forEach
-    ( \c1 ->
-        forEach
-          ( \c2 ->
-              useImplIn
-                k
-                (MkProxy (mapHandle c1) (mapHandle c2))
-          )
-          absurd
-    )
-    absurd
-
-yield ::
-  (e <: es) =>
-  Proxy x1 x () a e ->
-  a ->
-  -- | ͘
-  Eff es ()
-yield (MkProxy _ c) = Bluefin.Internal.yield c
-
-await :: (e <: es) => Proxy () a y' y e -> Eff es a
-await (MkProxy c _) = yieldCoroutine c ()
-
--- | @pipe@'s 'next' doesn't exist in Bluefin
-next :: ()
-next = ()
-
-each ::
-  (Foldable f) =>
-  f a ->
-  Proxy x' x () a e ->
-  -- | ͘
-  Eff (e :& es) ()
-each f p = for_ f (yield p)
-
-repeatM ::
-  (e <: es) =>
-  Eff es a ->
-  Proxy x' x () a e ->
-  -- | ͘
-  Eff es r
-repeatM e p = forever $ do
-  a <- e
-  yield p a
-
-replicateM ::
-  (e <: es) =>
-  Int ->
-  Eff es a ->
-  Proxy x' x () a e ->
-  -- | ͘
-  Eff es ()
-replicateM n e p = for_ [0 .. n] $ \_ -> do
-  a <- e
-  yield p a
-
-print ::
-  (e2 <: es, e1 <: es, Show a) =>
-  IOE e1 ->
-  Consumer a e2 ->
-  -- | ͘
-  Eff es r
-print io p = forever $ do
-  a <- await p
-  effIO io (Prelude.print a)
-
-unfoldr ::
-  (e <: es) =>
-  (s -> Eff es (Either r (a, s))) ->
-  s ->
-  Proxy x1 x () a e ->
-  -- | ͘
-  Eff es r
-unfoldr next_ sInit p =
-  withEarlyReturn $ \break -> evalState sInit $ \ss -> forever $ do
-    s <- get ss
-    useImpl (next_ s) >>= \case
-      Left r -> returnEarly break r
-      Right (a, s') -> do
-        put ss s'
-        yield p a
-
-mapM_ ::
-  (e <: es) =>
-  (a -> Eff es ()) ->
-  Proxy () a b b' e ->
-  -- | ͘
-  Eff es r
-mapM_ f = for cat (\a _ -> useImpl (f a))
-
-drain ::
-  (e <: es) =>
-  Proxy () b c' c e ->
-  -- | ͘
-  Eff es r
-drain = for cat (\_ _ -> pure ())
-
-map ::
-  (e <: es) =>
-  (a -> b) ->
-  Pipe a b e ->
-  -- | ͘
-  Eff es r
-map f = for cat (\a p1 -> yield p1 (f a))
-
-mapM ::
-  (e <: es) =>
-  (a -> Eff es b) ->
-  Pipe a b e ->
-  -- | ͘
-  Eff es r
-mapM f = for cat $ \a p -> do
-  b_ <- useImpl (f a)
-  yield p b_
-
-takeWhile' ::
-  (e <: es) =>
-  (r -> Bool) ->
-  Pipe r r e ->
-  -- | ͘
-  Eff es r
-takeWhile' predicate p = withEarlyReturn $ \early -> forever $ do
-  a <- await p
-  if predicate a
-    then yield p a
-    else returnEarly early a
-
-stdinLn ::
-  (e1 <: es, e2 <: es) =>
-  IOE e1 ->
-  Producer String e2 ->
-  -- | ͘
-  Eff es r
-stdinLn io c = forever $ do
-  line <- effIO io getLine
-  yield c line
-
-stdoutLn ::
-  (e1 <: es, e2 <: es) =>
-  IOE e1 ->
-  Consumer String e2 ->
-  -- | ͘
-  Eff es r
-stdoutLn io c = forever $ do
-  line <- await c
-  effIO io (putStrLn line)
diff --git a/src/Bluefin/Internal/Prim.hs b/src/Bluefin/Internal/Prim.hs
--- a/src/Bluefin/Internal/Prim.hs
+++ b/src/Bluefin/Internal/Prim.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DerivingVia #-}
 {-# LANGUAGE MagicHash #-}
+{-# LANGUAGE RoleAnnotations #-}
 {-# LANGUAGE UnboxedTuples #-}
 
 module Bluefin.Internal.Prim where
@@ -21,19 +22,23 @@
     unsafeOneWayCoercible,
   )
 import Control.Monad.Primitive qualified as P
+import Data.Kind (Type)
 import GHC.Exts (State#)
 import Unsafe.Coerce (unsafeCoerce)
 
-data Prim (e :: Effects) = UnsafeMkPrim
-  deriving (Handle) via OneWayCoercibleHandle Prim
+type Prim :: Effects -> Effects -> Type
+data Prim e1 e2 = UnsafeMkPrim
+  deriving (Handle) via OneWayCoercibleHandle (Prim e1)
 
+type role Prim nominal nominal
+
 data PrimStateEff (es :: Effects)
 
-instance (e <: es) => OneWayCoercible (Prim e) (Prim es) where
+instance (e2 <: es) => OneWayCoercible (Prim e1 e2) (Prim e1 es) where
   oneWayCoercibleImpl = unsafeOneWayCoercible
 
 runPrim ::
-  (forall e. Prim e -> Eff (e :& es) r) ->
+  (forall e. Prim e e -> Eff (e :& es) r) ->
   -- | ͘
   Eff es r
 runPrim k = makeOp (k UnsafeMkPrim)
@@ -44,9 +49,9 @@
 unsafeCoerceStateM = unsafeCoerce
 
 primitive ::
-  forall e1 es a.
-  (e1 <: es) =>
-  Prim e1 ->
+  forall e1 e2 es a.
+  (e2 <: es) =>
+  Prim e1 e2 ->
   (State# (PrimStateEff e1) -> (# State# (PrimStateEff e1), a #)) ->
   -- | ͘
   Eff es a
diff --git a/src/Bluefin/Internal/Vault.hs b/src/Bluefin/Internal/Vault.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin/Internal/Vault.hs
@@ -0,0 +1,44 @@
+{-# LANGUAGE RoleAnnotations #-}
+
+module Bluefin.Internal.Vault
+  ( module Bluefin.Internal.Vault,
+    Vault,
+    Vault.empty,
+  )
+where
+
+import Data.Kind (Type)
+import Data.Vault.Strict (Vault)
+import Data.Vault.Strict qualified as Vault
+import GHC.Exts (Any)
+import Unsafe.Coerce (unsafeCoerce)
+
+-- Vault.Key doesn't have representational role for its "value"
+-- argument. I think it should. We hack it.
+--
+--     https://github.com/HeinrichApfelmus/vault/issues/56
+type Key :: Type -> Type
+newtype Key a = MkKey (Vault.Key Any)
+
+type role Key representational
+
+fromMine :: Key a -> Vault.Key a
+fromMine (MkKey k) = unsafeCoerce k
+
+toMine :: Vault.Key a -> Key a
+toMine k = MkKey (unsafeCoerce k)
+
+lookup :: Key a -> Vault -> Maybe a
+lookup = Vault.lookup . fromMine
+
+adjust :: (a -> a) -> Key a -> Vault -> Vault
+adjust f = Vault.adjust f . fromMine
+
+newKey :: IO (Key a)
+newKey = fmap toMine Vault.newKey
+
+insert :: Key a -> a -> Vault -> Vault
+insert = Vault.insert . fromMine
+
+delete :: Key a -> Vault -> Vault
+delete = Vault.delete . fromMine
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -4,10 +4,22 @@
 module Main (main) where
 
 import Bluefin.Internal
+import Bluefin.Internal.Vault qualified as Vault
+import Control.Exception (AsyncException (ThreadKilled))
 import Control.Monad (forever, when)
 import Data.Foldable (for_)
+import Data.IORef (readIORef)
+import Data.Maybe (isNothing)
 import Test.GeneralBracket (test_generalBracket)
+import Test.RunPureEff
+  ( assertInterruptedBracketOutcome,
+    isClean,
+    isPoisoned,
+    isRanAtLeastTwice,
+    test_runPureEffAsyncSafeReapsWorker,
+  )
 import Test.SpecH (SpecH, assertEqual, runSpecH)
+import Test.ThrowCatch (test_throwCatch)
 import Prelude hiding (break, read)
 
 main :: IO ()
@@ -15,6 +27,30 @@
   runSpecH io $ \y -> do
     let assertEqual' = assertEqual y
 
+    assertInterruptedBracketOutcome
+      io
+      y
+      "runPureEff retains bracket's rethrown exception"
+      isPoisoned
+      runPureEff
+    assertInterruptedBracketOutcome
+      io
+      y
+      "runPureEffAsyncSafe survives an interrupted bracket"
+      isClean
+      runPureEffAsyncSafe
+    assertInterruptedBracketOutcome
+      io
+      y
+      "runPureEffAsyncSafeRestarting restarts interrupted work"
+      isRanAtLeastTwice
+      runPureEffAsyncSafeRestarting
+    workerException <- effIO io test_runPureEffAsyncSafeReapsWorker
+    assertEqual'
+      "runPureEffAsyncSafe worker receives ThreadKilled after result thunk GC"
+      (Just ThreadKilled)
+      workerException
+
     assertEqual' "oddsUntilFirstGreaterThan5" oddsUntilFirstGreaterThan5 [1, 3, 5, 7]
     assertEqual' "index 1" ([0, 1, 2, 3] !? 2) (Just 2)
     assertEqual' "index 2" ([0, 1, 2, 3] !? 4) Nothing
@@ -34,11 +70,23 @@
       "List"
       (runPureEff (yieldToList (listEff ([20, 30, 40], "Hello"))))
       ([20, 30, 40], "Hello")
+    assertEqual'
+      "Pure list"
+      ( yieldToPureList $ \y -> do
+          yield y 20
+          yield y 30
+          pure "Hello"
+      )
+      ([20, 30], "Hello")
 
     test_localInHandler y
+    test_readerCleanup y
     test_generalBracket io y
+    test_throwCatch y
     test_streamConsumeReader y
     test_streamConsumeHandleReader y
+    test_unliftIOReader io y
+    test_askCapabilityEscape y
 
 (!?) :: [a] -> Int -> Maybe a
 xs !? i = runPureEff $
@@ -84,15 +132,34 @@
   for_ as (yield y)
   pure r
 
+test_readerCleanup :: (e <: es) => SpecH e -> Eff es ()
+test_readerCleanup y = runReader @Int 1 $ \outer -> do
+  for_ [False, True] $ \abort -> do
+    key <- withEarlyReturn $ \ex ->
+      runReader @Int 2 $ \(MkReader key) ->
+        if abort then returnEarly ex key else pure key
+    do
+      -- Use the internals to check a property that cannot be tested without them.
+      cleanedUp <- UnsafeMkEff $ \vault -> do
+        contents <- readIORef vault
+        pure (isNothing (Vault.lookup key contents))
+      assertEqual
+        y
+        "Reader key released on normal and exceptional exit"
+        True
+        cleanedUp
+    do
+      outerValue <- ask outer
+      assertEqual y "Outer Reader survives inner cleanup" 1 outerValue
+
 test_localInHandler :: (e <: es) => SpecH e -> Eff es ()
 test_localInHandler y = runReader "global" $ \re ->
   forEach
     (\y2 -> local re (const "local") (yield y2 ()))
     (\() -> assertEqual y "Reader local" "local" =<< ask re)
 
--- This test confirms the buggy behavior reported in
---
---    https://github.com/tomjaguarpaw/bluefin/issues/98
+-- local run in one branch of a streamConsume should not affect ask in
+-- the other branch.
 test_streamConsumeReader :: (e <: es) => SpecH e -> Eff es ()
 test_streamConsumeReader spech = do
   runReader @Int 0 $ \r -> do
@@ -102,23 +169,23 @@
           let check i = assertEqual spech "Reader local" i =<< ask r
           check 0
           s
-          check (-100) -- Should be 0
+          check 0
           s
-          check (-200) -- Should be 0
+          check 0
           local r (+ 1) $ do
-            check (-199) -- Should be 1
+            check 1
             s
-            check (-299) -- Should be 1
+            check 1
             local r (+ 1) $ do
-              check (-298) -- Should be 2
+              check 2
               s
-              check (-398) -- Should be 2
-            check (-299) -- Should be 1
+              check 2
+            check 1
             s
-            check (-299) -- Should be 1
-          check (-200) -- Should be 0
+            check 1
+          check 0
           s
-          check (-200) -- Should be 0
+          check 0
       )
       ( \a -> do
           let p = await a
@@ -133,9 +200,8 @@
                   forever p
       )
 
--- This test confirms the buggy behavior reported in
---
---    https://github.com/tomjaguarpaw/bluefin/issues/98
+-- localHandle run in one branch of a streamConsume should not affect
+-- asksHandle in the other branch.
 test_streamConsumeHandleReader :: (e <: es) => SpecH e -> Eff es ()
 test_streamConsumeHandleReader spech = do
   runConstEffect @Int 0 $ \ce ->
@@ -144,11 +210,11 @@
         ( \y -> do
             let s = yield y ()
             let check i = do
-                  MkConstEffect i' <- askHandle r
-                  assertEqual spech "HandleReader local" i i'
+                  asksHandle r $ \(MkConstEffect i') -> do
+                    assertEqual spech "HandleReader local" i i'
             check 0
             s
-            check (-100) -- Should be 0
+            check 0
         )
         ( \a -> do
             let p = await a
@@ -156,3 +222,35 @@
             localHandle r (\(MkConstEffect i) -> MkConstEffect (subtract 100 i)) $ do
               forever p
         )
+
+-- Reader should interact well with withEffToIO_
+test_unliftIOReader :: (e1 <: es, e2 <: es) => IOE e1 -> SpecH e2 -> Eff es ()
+test_unliftIOReader io spech = do
+  let foo :: (Int -> IO ()) -> (forall r. Int -> IO r -> IO r) -> IO ()
+      foo myYield mySetLocal = do
+        myYield 0
+        mySetLocal 1 $ do
+          myYield 1
+          mySetLocal 2 $ do
+            myYield 2
+
+  runReader @Int 0 $ \r -> do
+    withEffToIO_ io $ \effToIO -> do
+      foo
+        ( \i -> effToIO $ do
+            i' <- ask r
+            assertEqual spech "myLocal" i i'
+        )
+        (\x -> effToIO . local r (const x) . effIO io)
+
+test_askCapabilityEscape :: (e <: es) => SpecH e -> Eff es ()
+test_askCapabilityEscape y = runConstEffect False $ \cFalse ->
+  runAskCapability cFalse $ \(ac :: HandleReader (ConstEffect Bool) e) -> do
+    -- We should not be allowed to escape `escaped` like this.  A
+    -- future version of Bluefin should fix this by removing
+    -- `askCapability`.
+    escaped <- runConstEffect True $ \cTrue -> do
+      localCapability ac (const (mapHandle cTrue)) $ do
+        useImpl (askCapability @_ @e ac)
+    let MkConstEffect escapedValue = escaped
+    assertEqual y "askCapability escape" True escapedValue
diff --git a/test/Test/RunPureEff.hs b/test/Test/RunPureEff.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/RunPureEff.hs
@@ -0,0 +1,144 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE NoMonoLocalBinds #-}
+{-# LANGUAGE NoMonomorphismRestriction #-}
+
+module Test.RunPureEff where
+
+import Bluefin.Internal
+import Control.Concurrent (threadDelay, throwTo)
+import Control.Concurrent.Async (asyncThreadId, waitCatch, withAsync)
+import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar, tryPutMVar)
+import Control.Exception (AsyncException (ThreadKilled), SomeException, evaluate)
+import Control.Exception qualified as Exception
+import Control.Monad (forever, unless)
+import Data.IORef (atomicModifyIORef', newIORef, readIORef, writeIORef)
+import System.Mem (performMajorGC)
+import System.Timeout (timeout)
+import Test.SpecH (SpecH, assertSatisfies)
+
+-- Interrupt a thread while it forces a shared thunk which runs inside
+-- a bracket, then force the thunk again and check from what point the
+-- computation resumed.
+test_runPureEffAsyncSafeSurvivesInterruptedBracket ::
+  (forall r. (forall e. Eff e r) -> r) ->
+  IO InterruptedBracketResult
+test_runPureEffAsyncSafeSurvivesInterruptedBracket run = do
+  started <- newEmptyMVar
+  continue <- newEmptyMVar
+  forceThunk <- do
+    modified <- newIORef False
+    ref <- newIORef $ run $ bracket (pure ()) (\() -> pure ()) $ \() -> do
+      let prog = do
+            previouslyModified <-
+              atomicModifyIORef' modified $ \previous ->
+                (True, previous)
+            -- Unless the IORef was previously modified, this is our
+            -- first run. Therefore, wait for the caller to unblock us,
+            -- and throw us an asynchronous exception.
+            unless previouslyModified $ do
+              _ <- tryPutMVar started ()
+              takeMVar continue
+            pure previouslyModified
+      unsafeProvideIO (\io -> effIO io prog)
+    pure (evaluate =<< readIORef ref)
+  interrupted <- withAsync forceThunk $ \worker -> do
+    takeMVar started
+    throwTo (asyncThreadId worker) ThreadKilled
+    waitCatch worker
+  case interrupted of
+    Left eInterrupted
+      | Just ThreadKilled <- Exception.fromException eInterrupted -> do
+          resumed <- do
+            -- If the first evaluation is resumed, it will need
+            -- unblocking.  If evaluation started from scratch it
+            -- won't block on continue anyway, because
+            -- previouslyModified is true.
+            putMVar continue ()
+            Exception.try @SomeException $ do
+              forceThunk
+          case resumed of
+            Left eResumed
+              | Just ThreadKilled <- Exception.fromException eResumed ->
+                  pure ThunkPoisoned
+              | otherwise ->
+                  pure (UnexpectedException eResumed)
+            Right previouslyModified ->
+              pure $ case previouslyModified of
+                False -> ThunkClean
+                True -> RanAtLeastTwice
+      | otherwise -> pure (UnexpectedException eInterrupted)
+    Right _ -> pure FinishedEarly
+
+-- Drop the result thunk after cancelling its forcing thread, then collect it
+-- and check that its weak finalizer kills the computation worker.
+test_runPureEffAsyncSafeReapsWorker :: IO (Maybe AsyncException)
+test_runPureEffAsyncSafeReapsWorker = do
+  started <- newEmptyMVar
+  caught <- newEmptyMVar
+  (release, forceThunk) <- do
+    shared <- newIORef @(Maybe ()) $ Just $ runPureEffAsyncSafe $ do
+      unsafeProvideIO $ \io -> do
+        effIO io $ do
+          putMVar started ()
+          Exception.handle @SomeException
+            (putMVar caught)
+            (forever (threadDelay 1_000_000))
+    pure
+      ( writeIORef shared Nothing,
+        maybe (fail "result thunk released") evaluate =<< readIORef shared
+      )
+  withAsync forceThunk $ \forcingThunk -> do
+    takeMVar started
+    throwTo (asyncThreadId forcingThunk) ThreadKilled
+    waitCatch forcingThunk >>= \case
+      Right () ->
+        fail "forcing thunk completed instead of being killed"
+      Left e
+        | -- We expect forcingThurk to be killed by ThreadKilled,
+          -- because that's what we just threw to it.
+          Just ThreadKilled <- Exception.fromException e ->
+            pure ()
+        | -- If we were killed by anything else, that should be
+          -- reported
+          otherwise ->
+            Exception.throwIO e
+
+  release
+  performMajorGC
+  caughtException <- timeout 1000000 (takeMVar caught)
+  pure (Exception.fromException =<< caughtException)
+
+data InterruptedBracketResult
+  = FinishedEarly
+  | UnexpectedException !SomeException
+  | ThunkPoisoned
+  | ThunkClean
+  | RanAtLeastTwice
+  deriving stock (Show)
+
+isClean :: InterruptedBracketResult -> Bool
+isClean = \case
+  ThunkClean -> True
+  _ -> False
+
+isPoisoned :: InterruptedBracketResult -> Bool
+isPoisoned = \case
+  ThunkPoisoned -> True
+  _ -> False
+
+isRanAtLeastTwice :: InterruptedBracketResult -> Bool
+isRanAtLeastTwice = \case
+  RanAtLeastTwice -> True
+  _ -> False
+
+assertInterruptedBracketOutcome ::
+  (e1 <: es, e2 <: es) =>
+  IOE e1 ->
+  SpecH e2 ->
+  String ->
+  (InterruptedBracketResult -> Bool) ->
+  (forall r. (forall e. Eff e r) -> r) ->
+  Eff es ()
+assertInterruptedBracketOutcome io y name predicate run = do
+  actual <- effIO io $ test_runPureEffAsyncSafeSurvivesInterruptedBracket run
+  assertSatisfies y name predicate actual
diff --git a/test/Test/SpecH.hs b/test/Test/SpecH.hs
--- a/test/Test/SpecH.hs
+++ b/test/Test/SpecH.hs
@@ -23,6 +23,23 @@
           yield y2 ("But got: " ++ show c2)
     )
 
+assertSatisfies ::
+  (e <: es, Show a) =>
+  SpecH e ->
+  String ->
+  (a -> Bool) ->
+  a ->
+  Eff es ()
+assertSatisfies y n predicate actual =
+  yield
+    y
+    ( n,
+      if predicate actual
+        then Nothing
+        else Just $ dslBuilder $ \y2 ->
+          yield y2 ("Predicate was not satisfied by: " ++ show actual)
+    )
+
 type SpecInfo r = DslBuilder (Stream String) r
 
 runTests ::
diff --git a/test/Test/ThrowCatch.hs b/test/Test/ThrowCatch.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/ThrowCatch.hs
@@ -0,0 +1,29 @@
+module Test.ThrowCatch where
+
+import Bluefin.Internal
+import Bluefin.Internal.Capability.ThrowCatch qualified as ThrowCatch
+import Test.SpecH (SpecH, assertEqual)
+
+test_throwCatch :: (e <: es) => SpecH e -> Eff es ()
+test_throwCatch y = do
+  assertEqual
+    y
+    "ThrowCatch.try catches a thrown exception"
+    (Left @String @() "caught")
+    (runPureEff (ThrowCatch.try $ \ex -> ThrowCatch.throw ex "caught"))
+  assertEqual
+    y
+    "ThrowCatch.try returns a successful result"
+    (Right @String @Int 42)
+    (runPureEff (ThrowCatch.try $ \_ -> pure 42))
+  assertEqual
+    y
+    "ThrowCatch.localTry catches a thrown exception"
+    (Left @String @() "outer")
+    ( runPureEff $ ThrowCatch.try $ \ex -> do
+        localResult <- ThrowCatch.localTry ex (ThrowCatch.throw ex "inner")
+        ThrowCatch.throw ex $ case localResult of
+          Left "inner" -> "outer"
+          Left _ -> "unexpected local exception"
+          Right () -> "localTry did not catch"
+    )
