diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.0.15.0
+
+* Add `runEff_` and `ignoreStream`
+
 ## 0.0.14.0
 
 * Add `hGetLine` and `hIsEOF`
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.0.14.0
+version:            0.0.15.0
 license:            MIT
 license-file:       LICENSE
 author:             Tom Ellis
@@ -78,7 +78,7 @@
     hs-source-dirs: src
     build-depends:
       async >= 2.2 && < 2.3,
-      base >= 4.12 && < 4.21,
+      base >= 4.12 && < 4.22,
       unliftio-core < 0.3,
       transformers < 0.7,
       transformers-base < 0.5,
diff --git a/src/Bluefin/Internal.hs b/src/Bluefin/Internal.hs
--- a/src/Bluefin/Internal.hs
+++ b/src/Bluefin/Internal.hs
@@ -217,7 +217,7 @@
 -- | Run `MonadIO` operations in 'Eff'.
 --
 -- @
--- >>> runEff $ \\io -> withMonadIO io $ liftIO $ do
+-- >>> runEff_ $ \\io -> withMonadIO io $ liftIO $ do
 --       putStrLn "Hello world!"
 -- Hello, world!
 -- @
@@ -321,7 +321,7 @@
   t ->
   -- | ͘
   Eff (e1 :& es) r
-useImplWithin k fsh = insertManySecond (k fsh)
+useImplWithin k = useImplUnder . k
 
 -- | Handle to a capability to create strict mutable state handles
 data StateSource (e :: Effects) = StateSource
@@ -537,7 +537,7 @@
 -- due to pure exceptions being imprecise.
 --
 -- f :: IO (Either Ex1 (Either Ex2 a))
--- f = runEff $ \io -> do
+-- f = runEff_ $ \io -> do
 --   try $ \e1 -> do
 --     try $ \e2 -> do
 --       rethrowIO io e1 $ do
@@ -753,6 +753,9 @@
 yieldCoroutine (MkCoroutine f) = useImpl . f
 
 -- |
+--
+-- Yield an element to the stream.
+--
 -- @
 -- >>> runPureEff $ yieldToList $ \\y -> do
 --       yield y 1
@@ -778,9 +781,12 @@
   finish z
 
 -- |
+--
+-- Apply an effectful function to each element yielded to the stream.
+--
 -- @
 -- >>> runPureEff $ yieldToList $ \\y -> do
---       forEach (inFoldable [0 .. 3]) $ \\i -> do
+--       for_ [0 .. 4] $ \\i -> do
 --         yield y i
 --         yield y (i * 10)
 -- ([0, 0, 1, 10, 2, 20, 3, 30], ())
@@ -793,7 +799,26 @@
 forEach f h = useImplIn f (MkCoroutine h)
 
 -- |
+--
+-- Ignore all elements yielded to the stream.
+--
 -- @
+-- >>> runPureEff $ ignoreStream $ \\y -> do
+--      for_ [0 .. 4] $ \\i -> do
+--        yield y i
+--        yield y (i * 10)
+--
+--      pure 42
+-- 42
+-- @
+ignoreStream ::
+  (forall e1. Stream a e1 -> Eff (e1 :& es) r) ->
+  -- | ͘
+  Eff es r
+ignoreStream k = forEach k (\_ -> pure ())
+
+-- |
+-- @
 -- >>> runPureEff $ yieldToList $ inFoldable [1, 2, 100]
 -- ([1, 2, 100], ())
 -- @
@@ -835,7 +860,7 @@
   Stream (Int, a) e2 ->
   Eff es r
 enumerateFrom n ss st =
-  evalState n $ \i -> forEach (insertSecond . ss) $ \s -> do
+  evalState n $ \i -> forEach (useImplUnder . ss) $ \s -> do
     ii <- get i
     yield st (ii, s)
     put i (ii + 1)
@@ -990,6 +1015,9 @@
 runCompound e1 e2 k = assoc1Eff (k (compound e1 e2))
 
 -- |
+--
+-- Gather all yielded elements into a list.
+--
 -- @
 -- >>> runPureEff $ yieldToList $ \\y -> do
 --       yield y 1
@@ -1040,7 +1068,7 @@
   Eff es ([a], r)
 yieldToReverseList f = do
   evalState [] $ \(s :: State lo st) -> do
-    r <- forEach (insertSecond . f) $ \i ->
+    r <- forEach (useImplUnder . f) $ \i ->
       modify s (i :)
     as <- get s
     pure (as, r)
@@ -1134,7 +1162,7 @@
 -- | Run an 'IO' operation in 'Eff'
 --
 -- @
--- >>> runEff $ \\io -> do
+-- >>> runEff_ $ \\io -> do
 --       effIO io (putStrLn "Hello world!")
 -- Hello, world!
 -- @
@@ -1149,16 +1177,35 @@
 -- | Run an 'Eff' whose only unhandled effect is 'IO'.
 --
 -- @
--- >>> runEff $ \\io -> do
+-- >>> runEff_ $ \\io -> do
 --       effIO io (putStrLn "Hello world!")
 -- Hello, world!
 -- @
+--
+-- We suggest you use 'runEff_' instead, as it probably has better
+-- type inference properties.
 runEff ::
   (forall e es. IOE e -> Eff (e :& es) a) ->
   -- | ͘
   IO a
 runEff eff = unsafeUnEff (eff MkIOE)
 
+-- | Run an 'Eff' whose only unhandled effect is 'IO'.
+--
+-- @
+-- >>> runEff_ $ \\io -> do
+--       effIO io (putStrLn "Hello world!")
+-- Hello, world!
+-- @
+--
+-- This probably has better type inference properties than 'runEff'
+-- and so will probably replace it in a later version.
+runEff_ ::
+  (forall e. IOE e -> Eff e a) ->
+  -- | ͘
+  IO a
+runEff_ eff = unsafeUnEff (eff MkIOE)
+
 unsafeProvideIO ::
   (forall e. IOE e -> Eff (e :& es) a) ->
   -- | ͘
@@ -1210,7 +1257,7 @@
   (forall e. Writer w e -> Eff (e :& es) r) ->
   Eff es (r, w)
 runWriter f = runState mempty $ \st -> do
-  forEach (insertSecond . f . Writer) $ \ww -> do
+  forEach (useImplUnder . f . Writer) $ \ww -> do
     modify st (<> ww)
 
 -- |
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
@@ -32,7 +32,7 @@
 import qualified Prelude
 
 monadIOExample :: IO ()
-monadIOExample = runEff $ \io -> withMonadIO io $ liftIO $ do
+monadIOExample = runEff_ $ \io -> withMonadIO io $ liftIO $ do
   name <- readLn
   putStrLn ("Hello " ++ name)
 
@@ -90,10 +90,18 @@
 
 forEachExample :: ([Int], ())
 forEachExample = runPureEff $ yieldToList $ \y -> do
-  forEach (inFoldable [0 .. 4]) $ \i -> do
+  for_ [0 .. 4] $ \i -> do
     yield y i
     yield y (i * 10)
 
+ignoreStreamExample :: Int
+ignoreStreamExample = runPureEff $ ignoreStream $ \y -> do
+  for_ [0 .. 4] $ \i -> do
+    yield y i
+    yield y (i * 10)
+
+  pure 42
+
 inFoldableExample :: ([Int], ())
 inFoldableExample = runPureEff $ yieldToList $ inFoldable [1, 2, 100]
 
@@ -108,7 +116,7 @@
   pure "End of loop"
 
 effIOExample :: IO ()
-effIOExample = runEff $ \io -> do
+effIOExample = runEff_ $ \io -> do
   effIO io (putStrLn "Hello world!")
 
 example1_ :: (Int, Int)
@@ -156,7 +164,7 @@
 -- Count non-empty lines from stdin, and print a friendly message,
 -- until we see "STOP".
 example3_ :: IO ()
-example3_ = runEff $ \io -> do
+example3_ = runEff_ $ \io -> do
   let getLineUntilStop y = withJump $ \stop -> forever $ do
         line <- effIO io getLine
         when (line == "STOP") $
@@ -247,12 +255,12 @@
           useImplUnder . x
 
 awaitExample :: IO ()
-awaitExample = runEff $ \io -> do
+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
+consumeStreamExample = runEff_ $ \io -> do
   try $ \ex -> do
     consumeStream
       ( \r ->
@@ -284,7 +292,7 @@
       )
 
 consumeStreamExample2 :: IO ()
-consumeStreamExample2 = runEff $ \io -> do
+consumeStreamExample2 = runEff_ $ \io -> do
   let counter yeven yodd = for_ [0 :: Int .. 10] $ \i -> do
         if even i
           then yield yeven i
@@ -309,7 +317,7 @@
   bar
 
 connectExample :: IO (Either String String)
-connectExample = runEff $ \io -> do
+connectExample = runEff_ $ \io -> do
   try $ \ex -> do
     connectCoroutines
       ( \y -> bracket
@@ -342,7 +350,7 @@
       )
 
 zipCoroutinesExample :: IO ()
-zipCoroutinesExample = runEff $ \io -> do
+zipCoroutinesExample = runEff_ $ \io -> do
   let m1 y = do
         r <- yieldCoroutine y 1
         evalState r $ \rs -> do
@@ -422,7 +430,7 @@
   myBail h
 
 countExample :: IO ()
-countExample = runEff $ \io -> do
+countExample = runEff_ $ \io -> do
   evalState @Int 0 $ \sn -> do
     withJump $ \break -> forever $ do
       n <- get sn
@@ -480,7 +488,7 @@
     modify state (+ i)
 
 runIncrementReadLine :: IO (Either String Int)
-runIncrementReadLine = runEff $ \io -> do
+runIncrementReadLine = runEff_ $ \io -> do
   try $ \exception -> do
     ((), r) <- runState 0 $ \state -> do
       incrementReadLine state exception io
@@ -582,7 +590,7 @@
 runCounter3B io k = useImplIn k (MkCounter3B (mapHandle io))
 
 exampleCounter3B :: IO ()
-exampleCounter3B = runEff $ \io -> runCounter3B io $ \c -> do
+exampleCounter3B = runEff_ $ \io -> runCounter3B io $ \c -> do
   incCounter3B c
   incCounter3B c
   incCounter3B c
@@ -914,7 +922,7 @@
 -- Left "File not found: /tmp/doesn't exist"
 
 exampleRunFileSystemIO :: IO (Either String String)
-exampleRunFileSystemIO = runEff $ \io -> try $ \ex ->
+exampleRunFileSystemIO = runEff_ $ \io -> try $ \ex ->
   runFileSystemIO ex io action
 
 -- > exampleRunFileSystemIO
@@ -974,13 +982,13 @@
     pure st
 
 pipesExample1 :: IO ()
-pipesExample1 = runEff $ \io -> runEffect (count >-> P.print 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
+pipesExample2 = runEff_ $ \io -> runEffect $ do
   stdinLn io >-> takeWhile' (/= "quit") >-> stdoutLn io
 
 -- Acquiring resource
@@ -992,7 +1000,7 @@
 -- Releasing resource
 -- Finishing
 promptCoroutine :: IO ()
-promptCoroutine = runEff $ \io -> do
+promptCoroutine = runEff_ $ \io -> do
   -- consumeStream connects a consumer to a producer
   consumeStream
     -- Like a pipes Consumer.  Prints the first five elements it
@@ -1013,7 +1021,7 @@
   effIO io (putStrLn "Finishing")
 
 rethrowIOExample :: IO ()
-rethrowIOExample = runEff $ \io -> do
+rethrowIOExample = runEff_ $ \io -> do
   r <- try $ \ex -> do
     rethrowIO @Control.Exception.IOException io ex $ do
       effIO io (Prelude.readFile "/tmp/doesnt-exist")
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -11,7 +11,7 @@
 import Prelude hiding (break, read)
 
 main :: IO ()
-main = runEff $ \io -> do
+main = runEff_ $ \io -> do
   runSpecH io $ \y -> do
     let assertEqual' = assertEqual y
 
