diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+# 0.4.0.0
+
+* Move `mapHandle` out of class `Handle`
+
+* Remove `Bluefin.Internal.handleOneWayCoercion`
+
+* Remove `Bluefin.Internal.handleMapHandle`
+
+* Add a variety of `Handle` functionality
+
 # 0.3.5.0
 
 * Add `Bluefin.Internal.oneWayCoercibleTrustMe`
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.3.5.0
+version:            0.4.0.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
@@ -16,10 +16,8 @@
 import Bluefin.Internal.OneWayCoercible
   ( OneWayCoercible (oneWayCoercibleImpl),
     OneWayCoercibleD,
-    OneWayCoercion,
     gOneWayCoercible,
     oneWayCoerce,
-    oneWayCoerceWith,
     oneWayCoercible,
     unsafeOneWayCoercible,
   )
@@ -38,8 +36,8 @@
 import Data.Kind (Type)
 import Data.Proxy (Proxy (Proxy))
 import Data.Type.Coercion (Coercion (Coercion))
-import GHC.Exts (Proxy#, proxy#)
-import GHC.Generics (Generic, M1 (M1), Rec1 (Rec1), (:*:) ((:*:)))
+import GHC.Exts (Any, Proxy#, proxy#)
+import GHC.Generics (Generic, M1, Rec1, (:*:))
 import System.IO.Unsafe (unsafePerformIO)
 import Unsafe.Coerce (unsafeCoerce)
 import Prelude hiding (drop, head, read, return)
@@ -399,85 +397,136 @@
 -- instance (e :> es) => 'OneWayCoercible' (Application e) (Application es) where
 --   oneWayCoercibleImpl = 'gOneWayCoercible'
 -- @
+--
+-- In occasional cases, @gOneWayCoercible@ won't work, in which can
+-- you can instead use
+--
+-- @
+-- instance (e :> es) => OneWayCoercible (MyHandle e) (MyHandle es) where
+--   oneWayCoercibleImpl = 'oneWayCoercibleTrustMe' $ \\h -> \<mapHandle definition\>
+-- @
 class Handle (h :: Effects -> Type) where
-  {-# MINIMAL handleImpl | mapHandle #-}
-
-  -- | Define @handleImpl@ using 'handleMapHandle'.
   handleImpl :: HandleD h
-  handleImpl = MkHandleD mapHandle
 
-  -- | Used to create compound effects, i.e. handles that contain
-  -- other handles.
-  --
-  -- You should not define this method in your type classes. Instead
-  -- define @handleImpl@.  In a future version of Bluefin, @mapHandle@
-  -- will no longer be a method.  If you previously had a definition
-  -- of @mapHandle@, for example like this:
-  --
-  -- @
-  -- instance Handle MyHandle where
-  --   mapHandle h = \<mapHandle definition\>
-  -- @
-  --
-  -- you should change it to
-  --
-  -- @
-  -- data MyHandle e = ...
-  --   deriving (Generic)
-  --   deriving (Handle) via 'OneWayCoercibleHandle' MyHandle
-  --
-  -- instance (e :> es) => 'OneWayCoercible' (MyHandle e) (MyHandle es) where
-  --   'oneWayCoercibleImpl' = 'gOneWayCoercible'
-  -- @
-  --
-  -- If that doesn't work for any reason you can always reuse your old
-  -- definition of @mapHandle@ as follows.  However,
-  -- 'handleMapHandle' will be removed at some point in the future, so
-  -- you can [open a new issue on
-  -- Bluefin](https://github.com/tomjaguarpaw/bluefin/issues/new) to
-  -- ask for advice.
-  --
-  -- @
-  -- instance Handle MyHandle where
-  --   handleImpl = handleMapHandle $ \\h -> \<mapHandle definition\>
-  -- @
-  mapHandle :: (e :> es) => h e -> h es
-  mapHandle = case handleImpl of MkHandleD f -> f
+-- | This was previously a method of class 'Handle' using which you
+-- could define @Handle@ instances. Now, you should define
+-- @handleImpl@ instead.  If you previously had a definition of
+-- @mapHandle@, for example like this:
+--
+-- @
+-- instance Handle MyHandle where
+--   mapHandle h = \<mapHandle definition\>
+-- @
+--
+-- you should change it to
+--
+-- @
+-- data MyHandle e = ...
+--   deriving (Generic)
+--   deriving (Handle) via 'OneWayCoercibleHandle' MyHandle
+--
+-- instance (e :> es) => 'OneWayCoercible' (MyHandle e) (MyHandle es) where
+--   'oneWayCoercibleImpl' = 'gOneWayCoercible'
+-- @
+--
+-- In occasional cases, @gOneWayCoercible@ won't work, in which can
+-- you can instead use
+--
+-- @
+-- instance (e :> es) => OneWayCoercible (MyHandle e) (MyHandle es) where
+--   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
 
+withHandle ::
+  forall h r.
+  (Handle h) =>
+  ((forall e es. (e :> es) => OneWayCoercible (h e) (h es)) => r) ->
+  r
+withHandle r = case handleDictImpl @h of MkHandleDict -> r
+
+type HandleDict :: (Effects -> Type) -> Type
+data HandleDict h where
+  MkHandleDict ::
+    (forall e es. (e :> es) => OneWayCoercible (h e) (h es)) =>
+    HandleDict h
+
+type role HandleDict nominal
+
+-- The essential properties of HandleD h are
+--
+-- (defining Handle' h =
+--    forall e es. (e :> es) => OneWayCoercible (h e) (h es))
+--
+-- 1. It can be created by having Handle' h in scope
+--
+-- 2. Having it can put Handle' h into scope
+--
+-- 3. There is an instance
+--
+--      Coercible h1 h2 => Coercible (Handle' h1) (Handle' h2)
+--
+-- 1 is used by handleOneWayCoercible
+--
+-- 2 is used by withHandle
+--
+-- 3 is used by deriving via of OneWayCoercibleHandle
+--
+-- The only way I have worked out how to satisfy all three properties
+-- is to have `HandleD h` not depend on `h`, instead have `Any` in
+-- place of where we would use `h`.  That way we can get property 3
+-- (which requires cooperation with GHC) and achieve properties 1 and
+-- 2 by unsafeCoerce.
+
 -- | The type of the 'handleImpl' method of the 'Handle' class.
--- Create a @HandleD@ using 'handleMapHandle'.
+-- Create a @HandleD@ using deriving via of 'OneWayCoercibleHandle'.
 type HandleD :: (Effects -> Type) -> Type
-newtype HandleD h = MkHandleD (forall e es. (e :> es) => h e -> h es)
+newtype HandleD h = MkHandleD (HandleDict Any)
 
--- | For defining the 'handleImpl' method of the 'Handle' class.
-handleMapHandle ::
-  (forall e es. (e :> es) => h e -> h es) ->
-  -- | ͘
-  HandleD h
-handleMapHandle = MkHandleD
+handleDictOfHandleD :: HandleD h -> HandleDict h
+-- SPJ suggests this might be safe on ghc-devs
+--
+--https://mailman.haskell.org/archives/list/ghc-devs@haskell.org/thread/A4AJPPA3WSORHKCMFWAFX26XNQQVYT5R/
+handleDictOfHandleD (MkHandleD f) = unsafeCoerce f
 
+handleDictImpl :: (Handle h) => HandleDict h
+handleDictImpl = handleDictOfHandleD handleImpl
+
+type role HandleD representational
+
 handleOneWayCoercible ::
   forall h.
   (forall e es. (e :> es) => OneWayCoercible (h e) (h es)) =>
   -- | ͘
   HandleD h
-handleOneWayCoercible = MkHandleD oneWayCoerce
-
-handleOneWayCoercion ::
-  (forall e es. (e :> es) => OneWayCoercion (h e) (h es)) ->
-  -- | ͘
-  HandleD h
-handleOneWayCoercion owc = MkHandleD (oneWayCoerceWith owc)
+-- SPJ suggests this might be safe on ghc-devs
+--
+--https://mailman.haskell.org/archives/list/ghc-devs@haskell.org/thread/A4AJPPA3WSORHKCMFWAFX26XNQQVYT5R/
+handleOneWayCoercible = MkHandleD (unsafeCoerce (MkHandleDict @h))
 
 instance (Handle h) => Handle (Rec1 h) where
-  handleImpl = handleMapHandle $ \(Rec1 h) -> Rec1 (mapHandle h)
+  handleImpl = withHandle @h handleOneWayCoercible
 
 instance (Handle h) => Handle (M1 i t h) where
-  handleImpl = handleMapHandle $ \(M1 h) -> M1 (mapHandle h)
+  handleImpl = withHandle @h handleOneWayCoercible
 
 instance (Handle h1, Handle h2) => Handle (h1 :*: h2) where
-  handleImpl = handleMapHandle $ \(h1 :*: h2) -> mapHandle h1 :*: mapHandle h2
+  handleImpl = withHandle @h1 (withHandle @h2 handleOneWayCoercible)
 
+-- | It is not always possible to derive an instance of
+-- 'OneWayCoercible'.  In such cases write a definition of
+-- 'oneWayCoercibleImpl' using @oneWayCoercibleTrustMe@.  Its argument
+-- must be a total function mapping the effect parameter of @h@.
+-- Normally you'll write the function by mapping the constitients of
+-- @h@ using the functions in this module like 'mapHandle', 'useImpl'
+-- and 'useImplUnder'.
+--
+-- If this function is not total then it's possible you are violating
+-- the type system, which could lead to segfaults or worse.  It's not
+-- easy to supply a partial function by accident, but be careful!
+-- (N.B. it must not be literally @mapHandle@ otherwise you'll have a
+-- circular definition!)
 oneWayCoercibleTrustMe ::
   (e :> es) =>
   (forall e' es'. (e' :> es') => h e' -> h es') ->
@@ -1582,6 +1631,7 @@
     (\() -> k)
 
 newtype HandleReader h e = UnsafeMkHandleReader (State (h e) e)
+  deriving (Handle) via OneWayCoercibleHandle (HandleReader 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
@@ -1644,11 +1694,8 @@
 
     useImplIn k h'
 
-instance (Handle h) => Handle (HandleReader h) where
-  -- This handleImpl is now a special case that doesn't use
-  -- OneWayCoercion. We can only fix that when we store a
-  -- OneWayCoercion inside `Handle`.
-  handleImpl = handleMapHandle mapHandleReader
+instance (e :> es) => OneWayCoercible (HandleReader h e) (HandleReader h es) where
+  oneWayCoercibleImpl = unsafeOneWayCoercible
 
 newtype ConstEffect r (e :: Effects) = MkConstEffect r
   deriving (Handle) via OneWayCoercibleHandle (ConstEffect r)
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
@@ -216,9 +216,13 @@
 -- a 'CloneableHandle' instance for your type, as long as it is a
 -- product type of @CloneableHandle@s.
 newtype GenericCloneableHandle h e = MkGenericCloneableHandle (h e)
+  deriving newtype (Handle)
 
-instance (Handle h) => Handle (GenericCloneableHandle h) where
-  handleImpl = handleMapHandle $ \(MkGenericCloneableHandle h) ->
+instance
+  (Handle h, e :> es) =>
+  OneWayCoercible (GenericCloneableHandle h e) (GenericCloneableHandle h es)
+  where
+  oneWayCoercibleImpl = oneWayCoercibleTrustMe $ \(MkGenericCloneableHandle h) ->
     MkGenericCloneableHandle (mapHandle h)
 
 instance
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
@@ -6,9 +6,6 @@
 
 import Bluefin.Internal hiding (b, w)
 import Bluefin.Internal.OneWayCoercible
-  ( OneWayCoercible (oneWayCoercibleImpl),
-    gOneWayCoercible,
-  )
 import Bluefin.Internal.Pipes
   ( Producer,
     runEffect,
@@ -25,7 +22,6 @@
 import Data.Foldable (for_)
 import Data.Monoid (Any (Any, getAny))
 import Data.Proxy (Proxy (Proxy))
-import GHC.Generics (Generic)
 import Text.Read (readMaybe)
 import Prelude hiding
   ( break,
@@ -791,9 +787,13 @@
     counter7State :: State Int e,
     counter7Stream :: Stream String e
   }
+  deriving (Handle) via OneWayCoercibleHandle Counter7
 
-instance Handle Counter7 where
-  handleImpl = handleMapHandle $ \c ->
+-- | 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),
         counter7State = mapHandle (counter7State c),
@@ -1036,13 +1036,20 @@
     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 Handle (DynamicReader r) where
-  handleImpl = handleMapHandle $ \h ->
+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)
diff --git a/src/Bluefin/Internal/System/IO.hs b/src/Bluefin/Internal/System/IO.hs
--- a/src/Bluefin/Internal/System/IO.hs
+++ b/src/Bluefin/Internal/System/IO.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE DerivingVia #-}
+
 module Bluefin.Internal.System.IO
   ( module Bluefin.Internal.System.IO,
     System.IO.IOMode (..),
@@ -9,21 +11,25 @@
     IOE,
     bracket,
     effIO,
-    handleMapHandle,
     mapHandle,
+    oneWayCoercibleTrustMe,
     useImplIn,
     (:&),
     (:>),
   )
 import Bluefin.Internal qualified
+import Bluefin.Internal.OneWayCoercible (OneWayCoercible (oneWayCoercibleImpl))
 import System.IO qualified
 
 -- We can probably get away without the IOE and just use
 -- unsafeProvideIO on all Handle functions
 data Handle e = UnsafeMkHandle System.IO.Handle (IOE e)
+  deriving
+    (Bluefin.Internal.Handle)
+    via Bluefin.Internal.OneWayCoercibleHandle Handle
 
-instance Bluefin.Internal.Handle Handle where
-  handleImpl = handleMapHandle $ \(UnsafeMkHandle h io) ->
+instance (e :> es) => OneWayCoercible (Handle e) (Handle es) where
+  oneWayCoercibleImpl = oneWayCoercibleTrustMe $ \(UnsafeMkHandle h io) ->
     UnsafeMkHandle h (mapHandle io)
 
 withFile ::
