diff --git a/Control/Monad/Logic.hs b/Control/Monad/Logic.hs
--- a/Control/Monad/Logic.hs
+++ b/Control/Monad/Logic.hs
@@ -52,9 +52,10 @@
     embedLogicT
   ) where
 
-import Control.Applicative
+import Prelude (error, (-))
 
-import Control.Monad
+import Control.Applicative (Alternative(..), Applicative, liftA2, pure, (<*>))
+import Control.Monad (join, MonadPlus(..), liftM, Monad(..), fail)
 import qualified Control.Monad.Fail as Fail
 import Control.Monad.Identity (Identity(..))
 import Control.Monad.IO.Class (MonadIO(..))
@@ -67,15 +68,20 @@
 import Control.Monad.State.Class (MonadState(..))
 import Control.Monad.Error.Class (MonadError(..))
 
-#if !MIN_VERSION_base(4,8,0)
-import Data.Monoid (Monoid (..))
-#endif
-
 #if MIN_VERSION_base(4,9,0)
 import Data.Semigroup (Semigroup (..))
 #endif
 
+import Data.Bool (otherwise)
+import Data.Eq ((==))
 import qualified Data.Foldable as F
+import Data.Function (($), (.), const)
+import Data.Functor (Functor(..), (<$>))
+import Data.Int
+import qualified Data.List as L
+import Data.Maybe (Maybe(..))
+import Data.Monoid (Monoid (..))
+import Data.Ord ((<=))
 import qualified Data.Traversable as T
 
 import Control.Monad.Logic.Class
@@ -88,16 +94,19 @@
 -- (see 'Logic'). Thus 'LogicT' @m@ for non-trivial @m@ can be imagined
 -- as a list, pattern matching on which causes monadic effects.
 --
+-- @since 0.2
 newtype LogicT m a =
     LogicT { unLogicT :: forall r. (a -> m r -> m r) -> m r -> m r }
 
 -------------------------------------------------------------------------
 -- | Extracts the first result from a 'LogicT' computation,
 -- failing if there are no results at all.
+--
+-- @since 0.2
 #if !MIN_VERSION_base(4,13,0)
 observeT :: Monad m => LogicT m a -> m a
 #else
-observeT :: MonadFail m => LogicT m a -> m a
+observeT :: Fail.MonadFail m => LogicT m a -> m a
 #endif
 observeT lt = unLogicT lt (const . return) (fail "No answer.")
 
@@ -124,11 +133,15 @@
 -- In general, if the underlying monad manages control flow then
 -- 'observeAllT' may be unproductive under infinite branching,
 -- and 'observeManyT' should be used instead.
+--
+-- @since 0.2
 observeAllT :: Applicative m => LogicT m a -> m [a]
 observeAllT m = unLogicT m (fmap . (:)) (pure [])
 
 -------------------------------------------------------------------------
 -- | Extracts up to a given number of results from a 'LogicT' computation.
+--
+-- @since 0.2
 observeManyT :: Monad m => Int -> LogicT m a -> m [a]
 observeManyT n m
     | n <= 0 = return []
@@ -163,6 +176,7 @@
 -- >>> runLogicT (yieldWords ["foo", "bar"]) showFirst (putStrLn "none!")
 -- foo
 --
+-- @since 0.2
 runLogicT :: LogicT m a -> (a -> m r -> m r) -> m r -> m r
 runLogicT (LogicT r) = r
 
@@ -179,6 +193,8 @@
 --
 -- 'show' $ fromLogicT @ListT l
 -- @
+--
+-- @since 0.8.0.0
 #if MIN_VERSION_base(4,8,0)
 fromLogicT :: (Alternative (t m), MonadTrans t, Monad m, Monad (t m))
   => LogicT m a -> t m a
@@ -202,6 +218,8 @@
 -- The first argument should be a
 -- <https://hackage.haskell.org/package/mmorph/docs/Control-Monad-Morph.html monad morphism>.
 -- to produce sensible results.
+--
+-- @since 0.8.0.0
 fromLogicTWith :: (Applicative m, Monad n, Alternative n)
   => (forall x. m x -> n x) -> LogicT m a -> n a
 fromLogicTWith p (LogicT f) = join . p $
@@ -217,6 +235,8 @@
 -- The first argument should be a
 -- <https://hackage.haskell.org/package/mmorph/docs/Control-Monad-Morph.html monad morphism>.
 -- to produce sensible results.
+--
+-- @since 0.8.0.0
 hoistLogicT :: (Applicative m, Monad n) => (forall x. m x -> n x) -> LogicT m a -> LogicT n a
 hoistLogicT f = fromLogicTWith (lift . f)
 
@@ -225,6 +245,8 @@
 -- The first argument should be a
 -- <https://hackage.haskell.org/package/mmorph/docs/Control-Monad-Morph.html monad morphism>.
 -- to produce sensible results.
+--
+-- @since 0.8.0.0
 embedLogicT :: Applicative m => (forall a. m a -> LogicT n a) -> LogicT m b -> LogicT n b
 embedLogicT f = fromLogicTWith f
 
@@ -247,10 +269,48 @@
 -- > toList :: Logic a -> [a]
 -- > toList (LogicT fld) = runIdentity $ fld (\x (Identity xs) -> Identity (x : xs)) (Identity [])
 --
+-- Here is a systematic derivation of the isomorphism. We start with observing
+-- that @[a]@ is isomorphic to a fix point of a non-recursive
+-- base algebra @Fix@ (@ListF@ @a@):
+--
+-- > newtype Fix f = Fix (f (Fix f))
+-- > data ListF a r = ConsF a r | NilF deriving (Functor)
+-- >
+-- > cata :: Functor f => (f r -> r) -> Fix f -> r
+-- > cata f = go where go (Fix x) = f (fmap go x)
+-- >
+-- > from :: [a] -> Fix (ListF a)
+-- > from = foldr (\a acc -> Fix (ConsF a acc)) (Fix NilF)
+-- >
+-- > to :: Fix (ListF a) -> [a]
+-- > to = cata (\case ConsF a r -> a : r; NilF -> [])
+--
+-- Further, @Fix@ (@ListF@ @a@) is isomorphic to Boehm-Berarducci encoding @ListC@ @a@:
+--
+-- > newtype ListC a = ListC (forall r. (ListF a r -> r) -> r)
+-- >
+-- > from :: Fix (ListF a) -> ListC a
+-- > from xs = ListC (\f -> cata f xs)
+-- >
+-- > to :: ListC a -> Fix (ListF a)
+-- > to (ListC f) = f Fix
+--
+-- Finally, @ListF@ @a@ @r@ → @r@ is isomorphic to a pair (@a@ → @r@ → @r@, @r@),
+-- so @ListC@ is isomorphic to the 'Logic' type modulo 'Identity' wrappers:
+--
+-- > newtype Logic a = Logic (forall r. (a -> r -> r) -> r -> r)
+--
+-- And wrapping every occurence of @r@ into @m@ gives us 'LogicT':
+--
+-- > newtype LogicT m a = Logic (forall r. (a -> m r -> m r) -> m r -> m r)
+--
+-- @since 0.5.0
 type Logic = LogicT Identity
 
 -------------------------------------------------------------------------
 -- | A smart constructor for 'Logic' computations.
+--
+-- @since 0.5.0
 logic :: (forall r. (a -> r -> r) -> r -> r) -> Logic a
 logic f = LogicT $ \k -> Identity .
                          f (\a -> runIdentity . k a . Identity) .
@@ -268,6 +328,7 @@
 --
 -- Since 'Logic' is isomorphic to a list, 'observe' is analogous to 'head'.
 --
+-- @since 0.2
 observe :: Logic a -> a
 observe lt = runIdentity $ unLogicT lt (const . pure) (error "No answer.")
 
@@ -280,6 +341,7 @@
 -- 'observeAll' reveals a half of the isomorphism between 'Logic'
 -- and lists. See description of 'runLogic' for the other half.
 --
+-- @since 0.2
 observeAll :: Logic a -> [a]
 observeAll = runIdentity . observeAllT
 
@@ -292,8 +354,9 @@
 --
 -- Since 'Logic' is isomorphic to a list, 'observeMany' is analogous to 'take'.
 --
+-- @since 0.2
 observeMany :: Int -> Logic a -> [a]
-observeMany i = take i . observeAll
+observeMany i = L.take i . observeAll
 -- Implementing 'observeMany' using 'observeManyT' is quite costly,
 -- because it calls 'msplit' multiple times.
 
@@ -311,6 +374,7 @@
 -- a half of the isomorphism between 'Logic' and lists.
 -- See description of 'observeAll' for the other half.
 --
+-- @since 0.2
 runLogic :: Logic a -> (a -> r -> r) -> r -> r
 runLogic l s f = runIdentity $ unLogicT l si fi
  where
@@ -335,6 +399,7 @@
     fail = Fail.fail
 #endif
 
+-- | @since 0.6.0.3
 instance Fail.MonadFail (LogicT m) where
     fail _ = LogicT $ \_ fk -> fk
 
@@ -343,11 +408,13 @@
   mplus = (<|>)
 
 #if MIN_VERSION_base(4,9,0)
+-- | @since 0.7.0.3
 instance Semigroup (LogicT m a) where
   (<>) = mplus
-  sconcat = foldr1 mplus
+  sconcat = F.foldr1 mplus
 #endif
 
+-- | @since 0.7.0.3
 instance Monoid (LogicT m a) where
   mempty = empty
 #if MIN_VERSION_base(4,9,0)
@@ -374,14 +441,17 @@
 
 #if MIN_VERSION_base(4,8,0)
 
+-- | @since 0.5.0
 instance {-# OVERLAPPABLE #-} (Applicative m, F.Foldable m) => F.Foldable (LogicT m) where
     foldMap f m = F.fold $ unLogicT m (fmap . mappend . f) (pure mempty)
 
+-- | @since 0.5.0
 instance {-# OVERLAPPING #-} F.Foldable (LogicT Identity) where
     foldr f z m = runLogic m f z
 
 #else
 
+-- | @since 0.5.0
 instance (Applicative m, F.Foldable m) => F.Foldable (LogicT m) where
     foldMap f m = F.fold $ unLogicT m (fmap . mappend . f) (pure mempty)
 
@@ -423,13 +493,17 @@
   ConsML a xs -> pure a <|> fromML xs
 
 #if MIN_VERSION_base(4,8,0)
+-- | @since 0.5.0
 instance {-# OVERLAPPING #-} T.Traversable (LogicT Identity) where
   traverse g l = runLogic l (\a ft -> cons <$> g a <*> ft) (pure empty)
     where
       cons a l' = pure a <|> l'
+
+-- | @since 0.8.0.0
 instance {-# OVERLAPPABLE #-} (Monad m, T.Traversable m) => T.Traversable (LogicT m) where
   traverse f = fmap fromML . T.traverse f . toML
 #else
+-- | @since 0.8.0.0
 instance (Monad m, Applicative m, T.Traversable m) => T.Traversable (LogicT m) where
   traverse f = fmap fromML . T.traverse f . toML
 #endif
@@ -462,25 +536,24 @@
           remains = unzipML listab
           (la, lb) = remains
 
+-- | @since 0.8.0.0
 instance MonadZip m => MonadZip (LogicT m) where
   mzipWith f xs ys = fromML $ zipWithML f (toML xs) (toML ys)
   munzip xys = case unzipML (toML xys) of
     (xs, ys) -> (fromML xs, fromML ys)
 #endif
 
--- Needs undecidable instances
 instance MonadReader r m => MonadReader r (LogicT m) where
     ask = lift ask
     local f (LogicT m) = LogicT $ \sk fk -> do
         env <- ask
         local f $ m ((local (const env) .) . sk) (local (const env) fk)
 
--- Needs undecidable instances
 instance MonadState s m => MonadState s (LogicT m) where
     get = lift get
     put = lift . put
 
--- Needs undecidable instances
+-- | @since 0.4
 instance MonadError e m => MonadError e (LogicT m) where
   throwError = lift . throwError
   catchError m h = LogicT $ \sk fk -> let
diff --git a/Control/Monad/Logic/Class.hs b/Control/Monad/Logic/Class.hs
--- a/Control/Monad/Logic/Class.hs
+++ b/Control/Monad/Logic/Class.hs
@@ -26,14 +26,34 @@
 
 module Control.Monad.Logic.Class (MonadLogic(..), reflect) where
 
-import Control.Applicative
-import Control.Monad
+import Prelude ()
+
+import Control.Applicative (Alternative(..), Applicative(..))
+import Control.Monad (MonadPlus, Monad(..))
 import Control.Monad.Reader (ReaderT(..))
 import Control.Monad.Trans (MonadTrans(..))
 import qualified Control.Monad.State.Lazy as LazyST
 import qualified Control.Monad.State.Strict as StrictST
+import Data.Function (const, ($))
+import Data.Maybe (Maybe(..), maybe)
 
+#if MIN_VERSION_mtl(2,3,0)
+import qualified Control.Monad.Writer.CPS as CpsW
+import qualified Control.Monad.Trans.Writer.CPS as CpsW (writerT, runWriterT)
+import Data.Monoid
+#endif
+
 -- | A backtracking, logic programming monad.
+--
+-- This package offers one implementation of 'MonadLogic': 'Control.Monad.Logic.LogicT'.
+-- Other notable implementations:
+--
+-- * https://hackage.haskell.org/package/list-t/docs/ListT.html#t:ListT
+-- * https://hackage.haskell.org/package/logict-sequence/docs/Control-Monad-Logic-Sequence.html#t:SeqT
+-- * https://hackage.haskell.org/package/logict-state/docs/Control-Monad-LogicState.html#t:LogicStateT
+-- * https://hackage.haskell.org/package/streamt/docs/Control-Monad-Stream.html#t:StreamT
+--
+-- @since 0.2
 class (Monad m, Alternative m) => MonadLogic m where
     -- | Attempts to __split__ the computation, giving access to the first
     --   result. Satisfies the following laws:
@@ -266,6 +286,8 @@
     --
     --   Here if @divisors@ never succeeds, then the 'lnot' will
     --   succeed and the number will be declared as prime.
+    --
+    -- @since 0.7.0.0
     lnot :: m a -> m ()
 
     -- | Logical __conditional.__ The equivalent of
@@ -323,6 +345,8 @@
 -- | The inverse of 'msplit'. Satisfies the following law:
 --
 -- > msplit m >>= reflect == m
+--
+-- @since 0.2
 reflect :: Alternative m => Maybe (a, m a) -> m a
 reflect Nothing = empty
 reflect (Just (a, m)) = pure a <|> m
@@ -346,6 +370,16 @@
                                    case r of
                                      Nothing -> pure Nothing
                                      Just (a, m) -> pure (Just (a, lift m))
+
+#if MIN_VERSION_mtl(2,3,0)
+-- | @since 0.8.1.0
+instance (Monoid w, MonadLogic m, MonadPlus m) => MonadLogic (CpsW.WriterT w m) where
+    msplit wm = CpsW.writerT $ do
+      r <- msplit $ CpsW.runWriterT wm
+      case r of
+        Nothing -> pure (Nothing, mempty)
+        Just ((a, w), m) -> pure (Just (a, CpsW.writerT m), w)
+#endif
 
 -- | See note on splitting above.
 instance (MonadLogic m, MonadPlus m) => MonadLogic (StrictST.StateT s m) where
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,7 @@
+# 0.8.1.0
+
+* Add `instance MonadLogic (Control.Monad.Writer.CPS.WriterT w m)`.
+
 # 0.8.0.0
 
 * Breaking change:
diff --git a/logict.cabal b/logict.cabal
--- a/logict.cabal
+++ b/logict.cabal
@@ -1,5 +1,5 @@
 name: logict
-version: 0.8.0.0
+version: 0.8.1.0
 license: BSD3
 license-file: LICENSE
 copyright:
@@ -22,7 +22,7 @@
   changelog.md
   README.md
 cabal-version: >=1.10
-tested-with: GHC ==7.0.4 GHC ==7.2.2 GHC ==7.4.2 GHC ==7.6.3 GHC ==7.8.4 GHC ==7.10.3 GHC ==8.0.2 GHC ==8.2.2 GHC ==8.4.4 GHC ==8.6.5 GHC ==8.8.4 GHC ==8.10.7 GHC ==9.0.2 GHC ==9.2.2
+tested-with: GHC ==7.0.4 GHC ==7.2.2 GHC ==7.4.2 GHC ==7.6.3 GHC ==7.8.4 GHC ==7.10.3 GHC ==8.0.2 GHC ==8.2.2 GHC ==8.4.4 GHC ==8.6.5 GHC ==8.8.4 GHC ==8.10.7 GHC ==9.0.2 GHC ==9.2.7 GHC ==9.4.5 GHC ==9.6.1
 
 source-repository head
   type: git
@@ -40,11 +40,12 @@
 
   build-depends:
     base >=4.3 && <5,
-    mtl >=2.0 && <2.4
+    mtl >=2.0 && <2.4,
+    transformers <0.7
 
   if impl(ghc <8.0)
     build-depends:
-      fail, transformers
+      fail < 4.10
 
 executable grandparents
   buildable: False
@@ -66,10 +67,11 @@
 
   build-depends:
     base,
-    async >=2.0,
+    async >=2.0 && <2.3,
     logict,
     mtl,
-    tasty,
-    tasty-hunit
+    transformers,
+    tasty <1.5,
+    tasty-hunit <0.11
 
   hs-source-dirs: test
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -18,15 +18,19 @@
 import qualified Control.Monad.State.Strict as SS
 import           Data.Maybe
 
-#if MIN_VERSION_base(4,9,0)
-#if MIN_VERSION_base(4,11,0)
-#else
+#if MIN_VERSION_base(4,9,0) && !MIN_VERSION_base(4,11,0)
 import           Data.Semigroup (Semigroup (..))
 #endif
-#else
+
+-- required by base < 4.9 OR CPS Writer test
+#if !MIN_VERSION_base(4,9,0) || MIN_VERSION_mtl(2,3,0)
 import           Data.Monoid
 #endif
 
+#if MIN_VERSION_mtl(2,3,0)
+import qualified Control.Monad.Writer.CPS as CpsW (WriterT, execWriterT, tell)
+import qualified Control.Monad.Trans.Writer.CPS as CpsW (runWriterT)
+#endif
 
 monadReader1 :: Assertion
 monadReader1 = assertEqual "should be equal" [5 :: Int] $
@@ -53,6 +57,12 @@
   oddsOrTwoUnfair, oddsOrTwoFair,
   odds5down :: Monad m => LogicT m Integer
 
+-- | A `WriterT` version of `evalStateT`.
+#if MIN_VERSION_mtl(2,3,0)
+evalWriterT :: (Monad m, Monoid w) => CpsW.WriterT w m a -> m a
+evalWriterT = fmap fst . CpsW.runWriterT
+#endif
+
 #if MIN_VERSION_base(4,8,0)
 nats = pure 0 `mplus` ((1 +) <$> nats)
 #else
@@ -152,6 +162,13 @@
               z = mzero
           in assertBool "ReaderT" $ null $ catMaybes $ runReaderT (msplit z) 0
 
+#if MIN_VERSION_mtl(2,3,0)
+        , testCase "msplit mzero :: CPS WriterT" $
+          let z :: CpsW.WriterT (Sum Int) [] String
+              z = mzero
+          in assertBool "CPS WriterT" $ null $ catMaybes (evalWriterT (msplit z))
+#endif
+
         , testCase "msplit mzero :: LogicT" $
           let z :: LogicT [] String
               z = mzero
@@ -181,6 +198,15 @@
             extract (msplit op) @?= [sample]
             extract (msplit op >>= (\(Just (_,nxt)) -> msplit nxt)) @?= []
 
+#if MIN_VERSION_mtl(2,3,0)
+        , testCase "msplit CPS WriterT" $ do
+            let op :: CpsW.WriterT (Sum Integer) [] ()
+                op = CpsW.tell 1 `mplus` op
+                extract = CpsW.execWriterT
+            extract (msplit op) @?= [1]
+            extract (msplit op >>= \(Just (_,nxt)) -> msplit nxt) @?= [2]
+#endif
+
         , testCase "msplit LogicT" $ do
             let op :: LogicT [] Integer
                 op = foldr (mplus . return) mzero sample
@@ -240,6 +266,13 @@
         (take 4 $ runReaderT (let oddsR = return 1 `mplus` liftM (2+) oddsR
                               in oddsR `interleave` return (2 :: Integer)) "go")
 
+#if MIN_VERSION_mtl(2,3,0)
+      , testCase "fair disjunction :: CPS WriterT" $ [1,2,3,5] @=?
+        (take 4 $ evalWriterT (let oddsW :: CpsW.WriterT [Char] [] Integer
+                                   oddsW = return 1 `mplus` liftM (2+) oddsW
+                                in oddsW `interleave` return (2 :: Integer)))
+#endif
+
       , testCase "fair disjunction :: strict StateT" $ [1,2,3,5] @=?
         (take 4 $ SS.evalStateT (let oddsS = return 1 `mplus` liftM (2+) oddsS
                                   in oddsS `interleave` return (2 :: Integer)) "go")
@@ -343,6 +376,17 @@
                                     if even x then return x else mzero
                              ) "env")
 
+#if MIN_VERSION_mtl(2,3,0)
+      , testCase "fair conjunction :: CPS WriterT" $ [2,4,6,8] @=?
+        (take 4 $ evalWriterT $
+         (let oddsW :: CpsW.WriterT [Char] [] Integer
+              oddsW = return (1 :: Integer) `mplus` liftM (2+) oddsW
+              oddsPlus n = oddsW >>= \a -> return (a + n)
+           in do x <- (return 0 `mplus` return 1) >>- oddsPlus
+                 if even x then return x else mzero
+         ))
+#endif
+
       , testCase "fair conjunction :: strict StateT" $ [2,4,6,8] @=?
         (take 4 $ SS.evalStateT (let oddsS = return (1 :: Integer) `mplus` liftM (2+) oddsS
                                      oddsPlus n = oddsS >>= \a -> return (a + n)
@@ -426,7 +470,21 @@
       in testCase "indivisible odds :: ReaderT" $ [3,5,7,11,13,17,19,23,29,31] @=?
          (take 10 $ runReaderT oc "env")
 
+#if MIN_VERSION_mtl(2,3,0)
     , let iota n = msum (map return [1..n])
+          oddsW = return (1 :: Integer) `mplus` liftM (2+) oddsW
+          oc :: CpsW.WriterT [Char] [] Integer
+          oc = do n <- oddsW
+                  guard (n > 1)
+                  ifte (do d <- iota (n - 1)
+                           guard (d > 1 && n `mod` d == 0))
+                    (const mzero)
+                    (return n)
+      in testCase "indivisible odds :: CPS WriterT" $ [3,5,7,11,13,17,19,23,29,31] @=?
+         (take 10 $ (fmap fst . CpsW.runWriterT) oc)
+#endif
+
+    , let iota n = msum (map return [1..n])
           oddsS = return (1 :: Integer) `mplus` liftM (2+) oddsS
           oc = do n <- oddsS
                   guard (n > 1)
@@ -498,6 +556,14 @@
       (take 5 $ runReaderT (do v <- foldr (mplus . return) mzero [(1::Integer)..]
                                lnot (isEven v)
                                return v) "env")
+
+#if MIN_VERSION_mtl(2,3,0)
+    , testCase "inversion :: CPS WriterT" $ [1,3,5,7,9] @=?
+      (take 5 $ (evalWriterT :: CpsW.WriterT [Char] [] Integer -> [Integer])
+       (do v <- foldr (mplus . return) mzero [(1::Integer)..]
+           lnot (isEven v)
+           return v))
+#endif
 
     , testCase "inversion :: strict StateT" $ [1,3,5,7,9] @=?
       (take 5 $ SS.evalStateT (do v <- foldr (mplus . return) mzero [(1::Integer)..]
