diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,17 +1,21 @@
+
 # chan
 
 This is just some extra `Chan` and `TChan` kit that might help the average user. It relies on
 spawning threads with `async` and either canceling (debouncing) or waiting (throttling) messages.
 
-Unfortunately, the current design is untyped in the sense that the channel which you supply is the
+Unfortunately, the current design is slightly untyped in the sense that the channel which you supply is the
 _output_ channel, and the returned channel is the one you would write to. I'm not sure how this should
-be fixed.
+be fixed. There's a series of "typed" alternatives to each underlying channel type, that allows users to
+restrict them to "read-only", "write-only", or "Writable a => ..." etc.
 
 An example might be the following:
 
 ```haskell
 import Control.Concurrent.Chan (readChan)
-import Control.Concurrent.Chan.Extra (throttleStatic, intersperseStatic)
+import Control.Concurrent.Chan.Scope (Scope (..))
+import Control.Concurrent.Chan.Typed (ChanRW)
+import Control.Concurrent.Chan.Extra (allowReading, throttleStatic, intersperseStatic)
 
 
 
@@ -19,23 +23,26 @@
 
 data SomeMessage
   = Ping
+  | Foo
   -- | ...
 
-throttleLayer :: Chan SomeMessage -> IO (Chan SomeMessage)
+throttleLayer :: ChanRW 'Read SomeMessage -> IO (ChanRW 'Write SomeMessage)
 throttleLayer output = do
   (x,_) <- throttleStatic output 1000000 -- nanoseconds, = 1 second
   pure x
 
-pingLayer :: Chan SomeMessage -> IO (Chan SomeMessage)
+pingLayer :: ChanRW 'Read SomeMessage -> IO (ChanRW 'Write SomeMessage)
 pingLayer output = do
   (x,_,_) <- intersperseStatic output (pure Ping) 1000000
   pure x
 
-performWebsocket :: Chan SomeMessage -> IO ()
+performWebsocket :: ChanRW 'Read SomeMessage -> IO ()
 performWebsocket output = do
-  output' <- pingLayer =<< throttleLayer output
-  _ <- async $ forever $ do
-    msg <- readChan output'
+  sendable <- pingLayer . allowReading =<< throttleLayer output
+  emittingThread <- async $ forever $ do
+    -- the thread that actually facilitates the sending of messages queued in the chans
+    msg <- readChanRW output
     send msg -- something like that - it'll include Ping messages for us,
              -- and throttle the outgoing messages at the same time.
+  writeChanRW sendable Foo
 ```
diff --git a/chan.cabal b/chan.cabal
--- a/chan.cabal
+++ b/chan.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 9429247fea789bb683e6f4ed7fb173854b095d3e98824b896891eb68655e73a8
+-- hash: ccb631c185767be2ee9b87d1fdc3c68a599868947989a396f06221b2c06a9b22
 
 name:           chan
-version:        0.0.4
+version:        0.0.4.1
 synopsis:       Some extra kit for Chans
 description:    Please see the README on Github at <https://github.com/athanclark/chan#readme>
 category:       Concurrency
diff --git a/src/Control/Concurrent/Chan/Extra.hs b/src/Control/Concurrent/Chan/Extra.hs
--- a/src/Control/Concurrent/Chan/Extra.hs
+++ b/src/Control/Concurrent/Chan/Extra.hs
@@ -16,11 +16,10 @@
   ( TChanRW (..), writeTChanRW, readTChanRW, newTChanRW)
 import Control.Concurrent.Chan.Scope (Scope (..), Writable, Readable)
 
-import Data.IORef (newIORef, readIORef, writeIORef)
 import Control.Monad (forever)
 import Control.Concurrent (threadDelay)
 import Control.Concurrent.MVar (newEmptyMVar, tryTakeMVar, putMVar)
-import Control.Concurrent.Chan (Chan, readChan, writeChan, newChan)
+import Control.Concurrent.Chan (Chan)
 import Control.Concurrent.Async (Async, async, cancel, wait)
 import Control.Concurrent.STM
   (atomically, tryTakeTMVar, putTMVar, newEmptyTMVarIO)
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,15 +1,15 @@
 import Control.Monad (forever)
 import Control.Concurrent (threadDelay)
 import Control.Concurrent.Chan (newChan, writeChan, readChan)
-import Control.Concurrent.Chan.Extra (debounceStatic, throttleStatic, intersperseStatic)
+import Control.Concurrent.Chan.Extra (ChanExtra (debounceStatic, throttleStatic, intersperseStatic))
 import Control.Concurrent.Async (async)
 import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
 import Control.Concurrent.STM (atomically)
 import Control.Concurrent.STM.TChan (newTChan, writeTChan, readTChan)
-import qualified Control.Concurrent.STM.TChan.Extra as TChan
 import Control.Concurrent.STM.TMVar (newEmptyTMVar, putTMVar, takeTMVar)
 
 
+chanDebounceTest :: IO ()
 chanDebounceTest = do
   putStrLn "Debounce ---------"
   outgoing <- newChan
@@ -35,6 +35,7 @@
 
   pure ()
 
+chanThrottleTest :: IO ()
 chanThrottleTest = do
   putStrLn "Throttle ---------"
   outgoing <- newChan
@@ -60,6 +61,7 @@
 
   pure ()
 
+chanIntersperseTest :: IO ()
 chanIntersperseTest = do
   putStrLn "Intersperse ---------"
   outgoing <- newChan
@@ -84,15 +86,17 @@
   pure ()
 
 
+chanTests :: IO ()
 chanTests = do
   chanDebounceTest
   chanThrottleTest
   chanIntersperseTest
 
+tchanDebounceTests :: IO ()
 tchanDebounceTests = do
   putStrLn "Debounce STM ---------"
   outgoing <- atomically newTChan
-  (incoming, _) <- TChan.debounceStatic 1000000 outgoing
+  (incoming, _) <- debounceStatic 1000000 outgoing
 
   lock <- atomically newEmptyTMVar
 
@@ -114,10 +118,11 @@
 
   pure ()
 
+tchanThrottleTests :: IO ()
 tchanThrottleTests = do
   putStrLn "Throttle STM ---------"
   outgoing <- atomically newTChan
-  (incoming, _) <- TChan.throttleStatic 1000000 outgoing
+  (incoming, _) <- throttleStatic 1000000 outgoing
 
   lock <- atomically newTChan
 
@@ -140,10 +145,11 @@
   pure ()
 
 
+tchanIntersperseTests :: IO ()
 tchanIntersperseTests = do
   putStrLn "Intersperse STM ---------"
   outgoing <- atomically newTChan
-  (incoming, _, _) <- TChan.intersperseStatic 1000000 (pure 0) outgoing
+  (incoming, _, _) <- intersperseStatic 1000000 (pure 0) outgoing
 
   lock <- atomically newEmptyTMVar
 
@@ -164,6 +170,7 @@
   pure ()
 
 
+tchanTests :: IO ()
 tchanTests = do
   tchanDebounceTests
   tchanThrottleTests
