diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.0.1.0
+
+* Add `Bluefin.Internal.Writer`
+
 ## 0.0.0.0
 
 * Initial version
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.0.0
+version:            0.0.1.0
 license:            MIT
 license-file:       LICENSE
 author:             Tom Ellis
diff --git a/src/Bluefin/Internal.hs b/src/Bluefin/Internal.hs
--- a/src/Bluefin/Internal.hs
+++ b/src/Bluefin/Internal.hs
@@ -770,3 +770,57 @@
   pure $ case r of
     Right r' -> Right r'
     Left (l, _) -> Left l
+
+newtype Writer w e = Writer (Stream w e)
+
+-- |
+-- @
+-- >>> 'Data.Monoid.getAny' $ snd $ runPureEff $ runWriter $ \\w -> do
+--       -- Non-empty list (the tell event does happen)
+--       for_ [1 .. 10] $ \\_ -> tell w ('Data.Monoid.Any' True)
+-- True
+-- @
+runWriter ::
+  (Monoid w) =>
+  -- | ͘
+  (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
+    modify st (<> ww)
+
+-- |
+-- @
+-- >>> 'Data.Monoid.getAny' $ runPureEff $ execWriter $ \\w -> do
+--       -- Non-empty list (the tell event does happen)
+--       for_ [1 .. 10] $ \\_ -> tell w ('Data.Monoid.Any' True)
+-- True
+-- @
+--
+-- @
+-- >>> 'Data.Monoid.getAny' $ runPureEff $ execWriter $ \\w -> do
+--       -- Empty list (the tell event does not happen)
+--       for_ [] $ \\_ -> tell w ('Data.Monoid.Any' True)
+-- False
+-- @
+execWriter ::
+  (Monoid w) =>
+  -- | ͘
+  (forall e. Writer w e -> Eff (e :& es) r) ->
+  Eff es w
+execWriter f = fmap snd (runWriter f)
+
+-- |
+-- @
+-- >>> 'Data.Monoid.getAny' $ runPureEff $ execWriter $ \\w -> do
+--       -- Non-empty list (the tell event does happen)
+--       for_ [1 .. 10] $ \\_ -> tell w ('Data.Monoid.Any' True)
+-- True
+-- @
+tell ::
+  (e :> es) =>
+  Writer w e ->
+  -- | ͘
+  w ->
+  Eff es ()
+tell (Writer y) = yield y
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
@@ -3,10 +3,11 @@
 
 module Bluefin.Internal.Examples where
 
-import Bluefin.Internal
+import Bluefin.Internal hiding (w)
 import Control.Monad (forever, when)
 import Control.Monad.IO.Class (liftIO)
 import Data.Foldable (for_)
+import Data.Monoid (Any (Any, getAny))
 import Prelude hiding (break, drop, head, read, return)
 
 monadIOExample :: IO ()
@@ -194,3 +195,11 @@
       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 .. 10] $ \_ -> tell w (Any True)
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -6,12 +6,13 @@
 import Bluefin.Internal
 import Control.Monad (when)
 import Data.Foldable (for_)
+import Data.Monoid (All (All))
 import System.Exit (ExitCode (ExitFailure), exitWith)
 import Prelude hiding (break, read)
 
 main :: IO ()
 main = do
-  allTrue $ \y -> do
+  runSpecH $ \y -> do
     let assertEqual' = assertEqual y
 
     assertEqual' "oddsUntilFirstGreaterThan5" oddsUntilFirstGreaterThan5 [1, 3, 5, 7]
@@ -39,18 +40,6 @@
 --   (name, Maybe (stream of error text))
 type SpecH = Stream (String, Maybe (SpecInfo ()))
 
--- I'm still not convinced that this scheme is practical for calling
--- outer effects from the inner.  The problem is that at the time of
--- interpretation some outer effects are unavailable because they have
--- already been handled (for example some state which the test cases
--- use) or, in the case of the Stream effect itself, because they are
--- currently being handled (we can't yield more results to the Stream
--- whilst we're handling it).
---
--- It seems likely that with a lot of awkwardness we can arrange for
--- the type parameters to be compatible with the order of handling,
--- but then we've coupled the order of the handlers to the effectful
--- operation, which is antithetical to the point of Bluefin.
 assertEqual ::
   (e :> es, Eq a, Show a) => SpecH e -> String -> a -> a -> Eff es ()
 assertEqual y n c1 c2 =
@@ -82,19 +71,19 @@
   Stream String e3 ->
   Eff es Bool
 runTests f y = do
-  evalState True $ \(passedAllSoFar :: State Bool e2) -> do
-    forEach f $ \(name, passedThisOne) -> do
-      case passedThisOne of
-        Just _ -> put passedAllSoFar False
-        Nothing -> pure ()
+  ((), All passedAll) <- runWriter $ \passedAllSoFar -> do
+    forEach f $ \(name, mFailure) -> do
+      let passed = case mFailure of
+            Just _ -> False
+            Nothing -> True
 
-      let mark = case passedThisOne of
-            Nothing -> "✓"
-            Just _ -> "✗"
+      tell passedAllSoFar (All passed)
 
+      let mark = if passed then "✓" else "✗"
+
       yield y (mark ++ " " ++ name)
 
-      case passedThisOne of
+      case mFailure of
         Nothing -> pure ()
         Just n -> do
           yield y "" :: Eff (e2 :& es) ()
@@ -102,12 +91,12 @@
             yield y ("    " ++ entry)
           yield y ""
 
-    get passedAllSoFar
+  pure passedAll
 
-allTrue ::
+runSpecH ::
   (forall e1 es. SpecH e1 -> Eff (e1 :& es) ()) ->
   IO ()
-allTrue f = runEff $ \ioe -> do
+runSpecH f = runEff $ \ioe -> do
   passed <- forEach (runTests f) $ \text ->
     effIO ioe (putStrLn text)
 
