diff --git a/bluefin.cabal b/bluefin.cabal
--- a/bluefin.cabal
+++ b/bluefin.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               bluefin
-version:            0.0.2.0
+version:            0.0.3.0
 license:            MIT
 license-file:       LICENSE
 author:             Tom Ellis
@@ -26,13 +26,14 @@
       Bluefin.EarlyReturn,
       Bluefin.Eff,
       Bluefin.Exception,
-      Bluefin.Jump,
       Bluefin.IO,
+      Bluefin.Jump,
+      Bluefin.Reader,
       Bluefin.State,
       Bluefin.StateSource,
       Bluefin.Stream,
       Bluefin.Writer,
     build-depends:
-      bluefin-internal >= 0.0.1 && < 0.1
+      bluefin-internal >= 0.0.3 && < 0.1
     hs-source-dirs:   src
     default-language: Haskell2010
diff --git a/src/Bluefin.hs b/src/Bluefin.hs
--- a/src/Bluefin.hs
+++ b/src/Bluefin.hs
@@ -93,6 +93,97 @@
     -- finished running there is no way you can use the handle
     -- anymore.
 
+    -- ** Type signatures
+
+    -- | Bluefin type signatures follow a common pattern which looks
+    -- like
+    --
+    -- @
+    -- (e1 :> es, ...) -> \<Handle\> e1 -> ... -> Eff es r
+    -- @
+    --
+    --
+    -- Consider the example below, @incrementReadLine@, which reads
+    -- integers from standard input and accumulates them into a state.
+    -- It returns when it reads the input integer @0@ and it throws an
+    -- exception if it encounters an input line it cannot parse.
+    --
+    -- Firstly, let's look at the arguments, which are all handles to
+    -- Bluefin effects.  There is a state handle, an exception handle,
+    -- and an IO handle, which allow modification of an @Int@ state,
+    -- throwing a @String@ exception, and performing @IO@ operations
+    -- respectively.  They are each tagged with a different effect
+    -- type, @e1@, @e2@ and @e3@ respectively, which are always kept
+    -- polymorphic.
+    --
+    -- Secondly, let's look at the return value, @Eff es ()@.  This
+    -- means the computation is performed in the t'Bluefin.Eff.Eff'
+    -- monad and the resulting value produced is of type @()@.  @Eff@
+    -- is tagged with the effect type @es@, which is also always kept
+    -- polymorphic.
+    --
+    -- Finally, let's look at the constraints.  They are what tie
+    -- together the effect tags of the arguments to the effect tag of
+    -- the result.  For every argument effect tag @en@ we have a
+    -- constraint @en :> es@.  That tells us the that effect handle
+    -- with tag @en@ is allowed to be used within the effectful
+    -- computation.  If we didn't have the @e1 :> es@ constraint, for
+    -- example, that would tell us that the @State Int e1@ isn't
+    -- actually used anywhere in the computation.
+    --
+    -- GHC and editor tools like HLS do a good job of inferring these
+    -- type signatures.
+    --
+    -- @
+    -- incrementReadLine ::
+    --   (e1 :> es, e2 :> es, e3 :> es) =>
+    --   State Int e1  ->
+    --   Exception String e2  ->
+    --   IOE e3 ->
+    --   Eff es ()
+    -- incrementReadLine state exception io = do
+    --   'Bluefin.Jump.withJump' $ \\break -> 'Control.Monad.forever' $ do
+    --     line <- 'Bluefin.IO.effIO' io getLine
+    --     i <- case 'Text.Maybe.readMaybe' line of
+    --       Nothing ->
+    --         'Bluefin.Exception.throw' exception ("Couldn't read: " ++ line)
+    --       Just i ->
+    --         pure i
+    --
+    --     when (i == 0) $
+    --       'Bluefin.Jump.jumpTo' break
+    --
+    --     'Bluefin.State.modify' state (+ i)
+    -- @
+    --
+    -- Now let's look at how we can run such a function.  Each effect
+    -- must be handled by a corresponding handler, for example
+    -- 'Bluefin.State.runState' for the state effect,
+    -- 'Bluefin.Exception.try' for the exception effect and
+    -- 'Bluefin.Eff.runEff' for the @IO@ effect.
+    --
+    -- @
+    -- runIncrementReadLine :: IO (Either String Int)
+    -- runIncrementReadLine = 'Bluefin.Eff.runEff' $ \\io -> do
+    --   'Bluefin.Exception.try' $ \\exception -> do
+    --     ((), r) \<- 'Bluefin.State.runState' 0 $ \\state -> do
+    --       incrementReadLine state exception io
+    --     pure r
+    --
+    -- >>> runIncrementReadLine
+    -- 1
+    -- 2
+    -- 3
+    -- 0
+    -- Right 6
+    -- >>>> runIncrementReadLine
+    -- 1
+    -- 2
+    -- 3
+    -- Hello
+    -- Left "Couldn't read: Hello"
+    -- @
+
     -- * Comparison to other effect systems
 
     -- ** Everything except effectful
@@ -153,6 +244,9 @@
     -- there are no unhandled effects it is safe to run the underlying
     -- @IO@ action using 'System.IO.Unsafe.unsafePerformIO', which is
     -- the approach taken to implement 'Bluefin.Eff.runPureEff'.
+    -- Consequently, it is impossible for a pure value retured from
+    -- `runPureEff` to access any Bluefin internal state or throw a
+    -- Bluefin internal exception.
 
     -- * Tips
 
diff --git a/src/Bluefin/Jump.hs b/src/Bluefin/Jump.hs
--- a/src/Bluefin/Jump.hs
+++ b/src/Bluefin/Jump.hs
@@ -1,5 +1,5 @@
 module Bluefin.Jump
-  ( -- | Jump allows you to jump back to a previously-set location.
+  ( -- | 'Jump' allows you to jump back to a previously-set location.
     -- It is not documented yet.
 
     -- * Handle
diff --git a/src/Bluefin/Reader.hs b/src/Bluefin/Reader.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin/Reader.hs
@@ -0,0 +1,19 @@
+module Bluefin.Reader
+  ( -- | 'Reader' is Bluefin's version of the
+    -- "Control.Monad.Trans.Reader" monad.  Passing around a @Reader r
+    -- e@ is equivalent to just passing around an @r@, and as such it
+    -- is essentially redundant and we don't know of any reasons to
+    -- use it in practice.  It is included for completeness, however.
+
+    -- * Handle
+    Reader,
+
+    -- * Handlers
+    runReader,
+
+    -- * Effectful operations
+    ask,
+  )
+where
+
+import Bluefin.Internal
diff --git a/src/Bluefin/StateSource.hs b/src/Bluefin/StateSource.hs
--- a/src/Bluefin/StateSource.hs
+++ b/src/Bluefin/StateSource.hs
@@ -3,8 +3,8 @@
     -- 'Bluefin.State.State' handles, much like 'Control.Monad.ST'
     -- allows you to allocate new 'Data.STRef.STRef's.  This can be
     -- useful when you want to avoid nested 'Bluefin.State.runState'
-    -- (or `Bluefin.State.evalState' blocks), or you need an only
-    -- dynamically known number of mutable states.
+    -- (or `Bluefin.State.evalState') blocks, or you need a number
+    -- of mutable states that is only dynamically known.
 
     -- * Handle
     StateSource,
diff --git a/src/Bluefin/Stream.hs b/src/Bluefin/Stream.hs
--- a/src/Bluefin/Stream.hs
+++ b/src/Bluefin/Stream.hs
@@ -1,5 +1,12 @@
 module Bluefin.Stream
-  ( -- * Handle
+  ( -- | 'Stream' allows you to yield values during the execution of a
+    -- Bluefin operation.  It provides similar functionality to
+    -- Python's @yield@.  The handler of the 'Stream' will either
+    -- handle each element as soon as it is yielded (for example
+    -- 'forEach') or gather all yielded elements int o a list (for
+    -- example 'yieldToList').
+
+    -- * Handle
     Stream,
     -- * Handlers
     forEach,
