diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/htf-test/Main.hs b/htf-test/Main.hs
new file mode 100644
--- /dev/null
+++ b/htf-test/Main.hs
@@ -0,0 +1,160 @@
+{-# OPTIONS_GHC -F -pgmF htfpp -Wno-redundant-constraints #-}
+
+import BasePrelude hiding (toList)
+import Control.Monad.Morph
+import qualified ListT as L
+import MTLPrelude
+import Test.Framework
+
+main :: IO ()
+main = htfMain htf_thisModulesTests
+
+-- * MMonad
+
+-- embed lift = id
+prop_mmonadLaw1 :: [Int] -> Bool
+prop_mmonadLaw1 (l :: [Int]) =
+  let s = L.fromFoldable l
+   in runIdentity $ streamsEqual s (embed lift s)
+
+-- embed f (lift m) = f m
+prop_mmonadLaw2 :: [Int] -> Bool
+prop_mmonadLaw2 l =
+  let s = (L.fromFoldable :: [Int] -> L.ListT Identity Int) l
+      f = MaybeT . fmap Just
+      run = runIdentity . L.toList . runMaybeT
+   in run (f s)
+        == run (embed f (lift s))
+
+-- * Applicative
+
+prop_applicativeIdentityLaw :: [Int] -> Bool
+prop_applicativeIdentityLaw (l :: [Int]) =
+  runIdentity $ streamsEqual (pure id <*> s) s
+  where
+    s = L.fromFoldable l
+
+prop_applicativeBehavesLikeList :: [Int] -> Bool
+prop_applicativeBehavesLikeList =
+  \(ns :: [Int]) ->
+    let a = fs <*> ns
+        b = runIdentity (toList $ L.fromFoldable fs <*> L.fromFoldable ns)
+     in a == b
+  where
+    fs = [(+ 1), (+ 3), (+ 5)]
+
+-- * Monad
+
+test_monadLaw1 :: IO ()
+test_monadLaw1 =
+  assertBool =<< streamsEqual (return a >>= k) (k a)
+  where
+    a = 2
+    k a = return $ chr a
+
+test_monadLaw2 :: IO ()
+test_monadLaw2 =
+  assertBool =<< streamsEqual (m >>= return) m
+  where
+    m = L.fromFoldable ['a' .. 'z']
+
+test_monadLaw3 :: IO ()
+test_monadLaw3 =
+  assertBool =<< streamsEqual (m >>= (\x -> k x >>= h)) ((m >>= k) >>= h)
+  where
+    m = L.fromFoldable ['a' .. 'z']
+    k a = return $ ord a
+    h a = return $ a + 1
+
+test_monadLaw4 :: IO ()
+test_monadLaw4 =
+  assertBool =<< streamsEqual (fmap f xs) (xs >>= return . f)
+  where
+    f = ord
+    xs = L.fromFoldable ['a' .. 'z']
+
+-- * Monoid
+
+test_mappend :: IO ()
+test_mappend =
+  assertBool
+    =<< streamsEqual
+      (L.fromFoldable [0 .. 7])
+      (L.fromFoldable [0 .. 3] <> L.fromFoldable [4 .. 7])
+
+test_mappendAndTake :: IO ()
+test_mappendAndTake =
+  assertBool
+    =<< streamsEqual
+      (L.fromFoldable [0 .. 5])
+      (L.take 6 $ L.fromFoldable [0 .. 3] <> L.fromFoldable [4 .. 7])
+
+test_mappendDoesntCauseTraversal :: IO ()
+test_mappendDoesntCauseTraversal =
+  do
+    ref <- newIORef 0
+    (flip runReaderT) ref (toList $ L.take 5 $ stream <> stream)
+    assertEqual 5 =<< readIORef ref
+  where
+    stream =
+      do
+        ref <- lift $ ask
+        x <- L.fromFoldable [0 .. 4]
+        liftIO $ modifyIORef ref (+ 1)
+        return x
+
+-- * Other
+
+test_repeat :: IO ()
+test_repeat =
+  assertEqual [2, 2, 2] =<< do
+    toList $ L.take 3 $ L.repeat (2 :: Int)
+
+test_traverseDoesntCauseTraversal :: IO ()
+test_traverseDoesntCauseTraversal =
+  do
+    ref <- newIORef 0
+    (flip runReaderT) ref (toList stream3)
+    assertEqual 3 =<< readIORef ref
+  where
+    stream1 =
+      do
+        ref <- lift $ ask
+        x <- L.fromFoldable ['a' .. 'z']
+        liftIO $ modifyIORef ref (+ 1)
+        return x
+    stream2 =
+      L.traverse (return . toUpper) stream1
+    stream3 =
+      L.take 3 stream2
+
+test_takeDoesntCauseTraversal :: IO ()
+test_takeDoesntCauseTraversal =
+  do
+    ref <- newIORef 0
+    (flip runReaderT) ref (toList $ L.take 3 $ L.take 7 $ stream)
+    assertEqual 3 =<< readIORef ref
+  where
+    stream =
+      do
+        ref <- lift $ ask
+        x <- L.fromFoldable [0 .. 10]
+        liftIO $ modifyIORef ref (+ 1)
+        return x
+
+test_drop :: IO ()
+test_drop =
+  assertEqual [3, 4] =<< do
+    toList $ L.drop 2 $ L.fromFoldable [1 .. 4]
+
+test_slice :: IO ()
+test_slice =
+  assertEqual ["abc", "def", "gh"] =<< do
+    toList $ L.slice 3 $ L.fromFoldable ("abcdefgh" :: [Char])
+
+toList :: (Monad m) => L.ListT m a -> m [a]
+toList = L.toList
+
+streamsEqual :: (Applicative m, Monad m, Eq a) => L.ListT m a -> L.ListT m a -> m Bool
+streamsEqual a b =
+  (==) <$> L.toList a <*> L.toList b
diff --git a/library/ListT.hs b/library/ListT.hs
--- a/library/ListT.hs
+++ b/library/ListT.hs
@@ -1,122 +1,188 @@
-{-# LANGUAGE UndecidableInstances, CPP #-}
+{-# OPTIONS_GHC -Wno-dodgy-imports #-}
+
 module ListT
-(
-  ListT,
-  -- * Classes
-  MonadTransUncons(..),
-  MonadCons(..),
-  -- * Execution utilities
-  head,
-  tail,
-  null,
-  fold,
-  foldMaybe,
-  toList,
-  toReverseList,
-  traverse_,
-  splitAt,
-  -- * Construction utilities
-  fromFoldable,
-  fromMVar,
-  unfold,
-  unfoldM,
-  repeat,
-  -- * Transformation utilities
-  -- | 
-  -- These utilities only accumulate the transformations
-  -- without actually traversing the stream.
-  -- They only get applied with a single traversal, 
-  -- which happens at the execution.
-  Transformation,
-  traverse,
-  take,
-  drop,
-  slice,
-  -- * Positive numbers
-  Positive,
-  positive,
-)
+  ( ListT (..),
+
+    -- * Execution utilities
+    uncons,
+    head,
+    tail,
+    null,
+    alternate,
+    alternateHoisting,
+    fold,
+    foldMaybe,
+    applyFoldM,
+    toList,
+    toReverseList,
+    traverse_,
+    splitAt,
+
+    -- * Construction utilities
+    cons,
+    fromFoldable,
+    fromMVar,
+    unfold,
+    unfoldM,
+    repeat,
+
+    -- * Transformation utilities
+
+    -- |
+    -- These utilities only accumulate the transformations
+    -- without actually traversing the stream.
+    -- They only get applied in a single traversal,
+    -- which only happens at the execution.
+    traverse,
+    take,
+    drop,
+    slice,
+  )
 where
 
-import BasePrelude hiding (uncons, toList, yield, fold, traverse, head, tail, take, drop, repeat, null, traverse_, splitAt)
-import Control.Monad.Morph hiding (MonadTrans(..))
-import Control.Monad.IO.Class
-import Control.Monad.Error.Class 
-import Control.Monad.Trans.Class
-import Control.Monad.Trans.Reader
-import Control.Monad.Trans.Maybe
-import Control.Monad.Trans.Control hiding (embed, embed_)
-import Control.Monad.Base
+import ListT.Prelude hiding (drop, fold, head, null, repeat, splitAt, tail, take, toList, traverse, traverse_, uncons, yield)
 
 -- |
 -- A proper implementation of the list monad-transformer.
 -- Useful for streaming of monadic data structures.
--- 
+--
 -- Since it has instances of 'MonadPlus' and 'Alternative',
 -- you can use general utilities packages like
 -- <http://hackage.haskell.org/package/monadplus "monadplus">
 -- with it.
-newtype ListT m a =
-  ListT { unListT :: m (Maybe (a, ListT m a)) }
+newtype ListT m a
+  = ListT (m (Maybe (a, ListT m a)))
+  deriving (Foldable, Traversable, Generic)
 
-instance Monad m => Monoid (ListT m a) where
-  mempty =
-    ListT $ 
-      return Nothing
-  mappend (ListT m1) (ListT m2) =
+deriving instance (Show (m (Maybe (a, ListT m a)))) => Show (ListT m a)
+
+deriving instance (Read (m (Maybe (a, ListT m a)))) => Read (ListT m a)
+
+deriving instance (Eq (m (Maybe (a, ListT m a)))) => Eq (ListT m a)
+
+deriving instance (Ord (m (Maybe (a, ListT m a)))) => Ord (ListT m a)
+
+deriving instance (Typeable m, Typeable a, Data (m (Maybe (a, ListT m a)))) => Data (ListT m a)
+
+instance (Eq1 m) => Eq1 (ListT m) where
+  liftEq eq = go
+    where
+      go (ListT m) (ListT n) = liftEq (liftEq (\(a, as) (b, bs) -> eq a b && go as bs)) m n
+
+instance (Ord1 m) => Ord1 (ListT m) where
+  liftCompare cmp = go
+    where
+      go (ListT m) (ListT n) = liftCompare (liftCompare (\(a, as) (b, bs) -> cmp a b <> go as bs)) m n
+
+instance (Show1 m) => Show1 (ListT m) where
+  -- I wish I were joking.
+  liftShowsPrec sp (sl :: [a] -> ShowS) = mark
+    where
+      bob :: Int -> m (Maybe (a, ListT m a)) -> ShowS
+      bob = liftShowsPrec jill edith
+
+      edith :: [Maybe (a, ListT m a)] -> ShowS
+      edith = liftShowList jack martha
+
+      jill :: Int -> Maybe (a, ListT m a) -> ShowS
+      jill = liftShowsPrec jack martha
+
+      martha :: [(a, ListT m a)] -> ShowS
+      martha = liftShowList2 sp sl mark juan
+
+      mark :: Int -> ListT m a -> ShowS
+      mark d (ListT m) = showsUnaryWith bob "ListT" d m
+
+      juan :: [ListT m a] -> ShowS
+      juan = liftShowList sp sl
+
+      jack :: Int -> (a, ListT m a) -> ShowS
+      jack = liftShowsPrec2 sp sl mark juan
+
+instance (Monad m) => Semigroup (ListT m a) where
+  (<>) (ListT m1) (ListT m2) =
     ListT $
-      m1 >>=
-        \case
+      m1
+        >>= \case
           Nothing ->
             m2
           Just (h1, s1') ->
-            return (Just (h1, (mappend s1' (ListT m2))))
+            return (Just (h1, ((<>) s1' (ListT m2))))
 
-instance Functor m => Functor (ListT m) where
-  fmap f =
-    ListT . (fmap . fmap) (f *** fmap f) . unListT
+instance (Monad m) => Monoid (ListT m a) where
+  mempty =
+    ListT $
+      return Nothing
+  mappend = (<>)
 
+instance (Functor m) => Functor (ListT m) where
+  fmap f = go
+    where
+      go =
+        ListT . (fmap . fmap) (bimapPair' f go) . uncons
+
 instance (Monad m, Functor m) => Applicative (ListT m) where
-  pure = 
-    return
-  (<*>) = 
+  pure a =
+    ListT $ return (Just (a, (ListT (return Nothing))))
+  (<*>) =
     ap
 
+  -- This is just like liftM2, but it uses fmap over the second
+  -- action. liftM2 can't do that, because it has to deal with
+  -- the possibility that someone defines liftA2 = liftM2 and
+  -- fmap f = (pure f <*>) (leaving (<*>) to the default).
+  liftA2 f m1 m2 = do
+    x1 <- m1
+    fmap (f x1) m2
+
 instance (Monad m, Functor m) => Alternative (ListT m) where
-  empty = 
-    inline mzero
-  (<|>) = 
-    inline mplus
+  empty =
+    inline mempty
+  (<|>) =
+    inline mappend
 
-instance Monad m => Monad (ListT m) where
-  return a =
-    ListT $ return (Just (a, (ListT (return Nothing))))
-  (>>=) s1 k2 =
-    ListT $
-      uncons s1 >>=
-        \case
-          Nothing ->
-            return Nothing
-          Just (h1, t1) ->
-            uncons $ k2 h1 <> (t1 >>= k2)
+instance (Monad m) => Monad (ListT m) where
+  return = pure
 
-instance Monad m => MonadPlus (ListT m) where
-  mzero = 
+  -- We use a go function so GHC can inline k2
+  -- if it likes.
+  (>>=) s10 k2 = go s10
+    where
+      go s1 =
+        ListT $
+          uncons s1
+            >>= \case
+              Nothing ->
+                return Nothing
+              Just (h1, t1) ->
+                uncons $ k2 h1 <> go t1
+
+instance (Monad m) => MonadFail (ListT m) where
+  fail _ =
     inline mempty
-  mplus = 
+
+instance (Monad m) => MonadPlus (ListT m) where
+  mzero =
+    inline mempty
+  mplus =
     inline mappend
 
 instance MonadTrans ListT where
   lift =
-    ListT . liftM (\a -> Just (a, mempty))
+    ListT . fmap (\a -> Just (a, mempty))
 
-instance MonadIO m => MonadIO (ListT m) where
+instance (MonadIO m) => MonadIO (ListT m) where
   liftIO =
     lift . liftIO
 
 instance MFunctor ListT where
-  hoist f =
-    ListT . f . (liftM . fmap) (id *** hoist f) . unListT
+  hoist f = go
+    where
+      go (ListT run) =
+        ListT . f $
+          run <&> \case
+            Just (elem, next) -> Just (elem, go next)
+            Nothing -> Nothing
 
 instance MMonad ListT where
   embed f (ListT m) =
@@ -124,140 +190,217 @@
       Nothing -> mzero
       Just (h, t) -> ListT $ return $ Just $ (h, embed f t)
 
-instance MonadBase b m => MonadBase b (ListT m) where
+instance (MonadBase b m) => MonadBase b (ListT m) where
   liftBase =
     lift . liftBase
 
-#if MIN_VERSION_monad_control(1,0,0)
-instance MonadBaseControl b m => MonadBaseControl b (ListT m) where
-  type StM (ListT m) a =
-    StM m (Maybe (a, ListT m a))
+instance (MonadBaseControl b m) => MonadBaseControl b (ListT m) where
+  type
+    StM (ListT m) a =
+      StM m (Maybe (a, ListT m a))
   liftBaseWith runToBase =
-    lift $ liftBaseWith $ \runInner -> 
-      runToBase $ runInner . uncons
+    lift $
+      liftBaseWith $ \runInner ->
+        runToBase $ runInner . uncons
   restoreM inner =
     lift (restoreM inner) >>= \case
       Nothing -> mzero
       Just (h, t) -> cons h t
-#else
-instance MonadBaseControl b m => MonadBaseControl b (ListT m) where
-  newtype StM (ListT m) a =
-    StM (StM m (Maybe (a, ListT m a)))
-  liftBaseWith runToBase =
-    lift $ liftBaseWith $ \runInner -> 
-      runToBase $ liftM StM . runInner . uncons
-  restoreM (StM inner) =
-    lift (restoreM inner) >>= \case
-      Nothing -> mzero
-      Just (h, t) -> cons h t
-#endif
 
-instance MonadError e m => MonadError e (ListT m) where
+instance (MonadError e m) => MonadError e (ListT m) where
   throwError = ListT . throwError
-  catchError m handler = ListT $ catchError (unListT m) $ unListT . handler
+  catchError m handler = ListT $ catchError (uncons m) $ uncons . handler
 
--- * Classes
--------------------------
+instance (MonadReader e m) => MonadReader e (ListT m) where
+  ask = lift ask
+  reader = lift . reader
+  local r = go
+    where
+      go (ListT m) = ListT $ local r (fmap (fmap (secondPair' go)) m)
 
--- |
--- A monad transformer capable of deconstructing like a list.
-class MonadTrans t => MonadTransUncons t where
-  -- |
-  -- Execute in the inner monad,
-  -- getting the head and the tail.
-  -- Returns nothing if it's empty.
-  uncons :: Monad m => t m a -> m (Maybe (a, t m a))
+instance (MonadState e m) => MonadState e (ListT m) where
+  get = lift get
+  put = lift . put
+  state = lift . state
 
-instance MonadTransUncons ListT where
-  {-# INLINE uncons #-}
-  uncons (ListT m) = m
+instance (Monad m) => MonadLogic (ListT m) where
+  msplit (ListT m) = lift m
 
+  interleave m1 m2 =
+    ListT $
+      uncons m1 >>= \case
+        Nothing -> uncons m2
+        Just (a, m1') -> uncons $ cons a (interleave m2 m1')
 
--- |
--- A monad capable of constructing like a list.
-class MonadPlus m => MonadCons m where
-  -- |
-  -- Prepend an element.
-  cons :: a -> m a -> m a
+  m >>- f =
+    ListT $
+      uncons m >>= \case
+        Nothing -> uncons empty
+        Just (a, m') -> uncons $ interleave (f a) (m' >>- f)
 
-instance MonadCons [] where
-  cons a m = a : m
+  ifte t th el =
+    ListT $
+      uncons t >>= \case
+        Nothing -> uncons el
+        Just (a, m) -> uncons $ th a <|> (m >>= th)
 
-instance Monad m => MonadCons (ListT m) where
-  {-# INLINABLE cons #-}
-  cons h t = ListT $ return (Just (h, t))
+  once (ListT m) =
+    ListT $
+      m >>= \case
+        Nothing -> uncons empty
+        Just (a, _) -> uncons (return a)
 
-instance MonadCons m => MonadCons (ReaderT e m) where
-  cons a m = ReaderT $ cons a . runReaderT m
+  lnot (ListT m) =
+    ListT $
+      m >>= \case
+        Nothing -> uncons (return ())
+        Just _ -> uncons empty
 
+instance (MonadZip m) => MonadZip (ListT m) where
+  mzipWith f = go
+    where
+      go (ListT m1) (ListT m2) =
+        ListT $
+          mzipWith
+            ( mzipWith $
+                \(a, as) (b, bs) -> (f a b, go as bs)
+            )
+            m1
+            m2
 
+  munzip (ListT m)
+    | (l, r) <- munzip (fmap go m) =
+        (ListT l, ListT r)
+    where
+      go Nothing = (Nothing, Nothing)
+      go (Just ((a, b), listab)) =
+        (Just (a, la), Just (b, lb))
+        where
+          -- If the underlying munzip is careful not to leak memory, then we
+          -- don't want to defeat it.  We need to be sure that la and lb are
+          -- realized as selector thunks.
+          {-# NOINLINE remains #-}
+          {-# NOINLINE la #-}
+          {-# NOINLINE lb #-}
+          remains = munzip listab
+          (la, lb) = remains
+
 -- * Execution in the inner monad
+
 -------------------------
 
 -- |
+-- Execute in the inner monad,
+-- getting the head and the tail.
+-- Returns nothing if it's empty.
+uncons :: ListT m a -> m (Maybe (a, ListT m a))
+uncons (ListT m) =
+  m
+
+-- |
 -- Execute, getting the head. Returns nothing if it's empty.
-{-# INLINABLE head #-}
-head :: (Monad m, MonadTransUncons t) => t m a -> m (Maybe a)
+{-# INLINEABLE head #-}
+head :: (Monad m) => ListT m a -> m (Maybe a)
 head =
-  liftM (fmap fst) . uncons
+  fmap (fmap fst) . uncons
 
 -- |
 -- Execute, getting the tail. Returns nothing if it's empty.
-{-# INLINABLE tail #-}
-tail :: (Monad m, MonadTransUncons t) => t m a -> m (Maybe (t m a))
+{-# INLINEABLE tail #-}
+tail :: (Monad m) => ListT m a -> m (Maybe (ListT m a))
 tail =
-  liftM (fmap snd) . uncons
+  fmap (fmap snd) . uncons
 
 -- |
 -- Execute, checking whether it's empty.
-{-# INLINABLE null #-}
-null :: (Monad m, MonadTransUncons t) => t m a -> m Bool
+{-# INLINEABLE null #-}
+null :: (Monad m) => ListT m a -> m Bool
 null =
-  liftM (maybe True (const False)) . uncons
+  fmap (maybe True (const False)) . uncons
 
 -- |
--- Execute, applying a left fold.
-{-# INLINABLE fold #-}
-fold :: (Monad m, MonadTransUncons t) => (r -> a -> m r) -> r -> t m a -> m r
-fold s r = 
-  uncons >=> maybe (return r) (\(h, t) -> s r h >>= \r' -> fold s r' t)
+-- Execute in the inner monad,
+-- using its '(<|>)' function on each entry.
+{-# INLINEABLE alternate #-}
+alternate :: (Alternative m, Monad m) => ListT m a -> m a
+alternate (ListT m) =
+  m >>= \case
+    Nothing -> empty
+    Just (a, as) -> pure a <|> alternate as
 
 -- |
+-- Use a monad morphism to convert a 'ListT' to a similar
+-- monad, such as '[]'.
+--
+-- A more efficient alternative to @'alternate' . 'hoist' f@.
+{-# INLINEABLE alternateHoisting #-}
+alternateHoisting :: (Monad n, Alternative n) => (forall a. m a -> n a) -> ListT m a -> n a
+alternateHoisting f = go
+  where
+    go (ListT m) =
+      f m >>= \case
+        Nothing -> empty
+        Just (a, as) -> pure a <|> go as
+
+-- |
+-- Execute, applying a strict left fold.
+{-# INLINEABLE fold #-}
+fold :: (Monad m) => (b -> a -> m b) -> b -> ListT m a -> m b
+fold step = go
+  where
+    go !acc (ListT run) =
+      run >>= \case
+        Just (element, next) -> do
+          acc' <- step acc element
+          go acc' next
+        Nothing ->
+          return acc
+
+-- |
 -- A version of 'fold', which allows early termination.
-{-# INLINABLE foldMaybe #-}
-foldMaybe :: (Monad m, MonadTransUncons t) => (r -> a -> m (Maybe r)) -> r -> t m a -> m r
+{-# INLINEABLE foldMaybe #-}
+foldMaybe :: (Monad m) => (b -> a -> m (Maybe b)) -> b -> ListT m a -> m b
 foldMaybe s r l =
-  liftM (maybe r id) $ runMaybeT $ do
-    (h, t) <- MaybeT $ uncons l
-    r' <- MaybeT $ s r h
-    lift $ foldMaybe s r' t
+  fmap (maybe r id) $
+    runMaybeT $ do
+      (h, t) <- MaybeT $ uncons l
+      r' <- MaybeT $ s r h
+      lift $ foldMaybe s r' t
 
 -- |
+-- Apply the left fold abstraction from the \"foldl\" package.
+applyFoldM :: (Monad m) => FoldM m i o -> ListT m i -> m o
+applyFoldM (FoldM step init extract) lt = do
+  a <- init
+  b <- fold step a lt
+  extract b
+
+-- |
 -- Execute, folding to a list.
-{-# INLINABLE toList #-}
-toList :: (Monad m, MonadTransUncons t) => t m a -> m [a]
+{-# INLINEABLE toList #-}
+toList :: (Monad m) => ListT m a -> m [a]
 toList =
-  liftM ($ []) . fold (\f e -> return $ f . (e :)) id
+  fmap reverse . toReverseList
 
 -- |
--- Execute, folding to a list in a reverse order.
+-- Execute, folding to a list in the reverse order.
 -- Performs more efficiently than 'toList'.
-{-# INLINABLE toReverseList #-}
-toReverseList :: (Monad m, MonadTransUncons t) => t m a -> m [a]
+{-# INLINEABLE toReverseList #-}
+toReverseList :: (Monad m) => ListT m a -> m [a]
 toReverseList =
-  ListT.fold (\l -> return . (:l)) []
+  fold (\list element -> return (element : list)) []
 
 -- |
--- Execute, traversing the stream with a side effect in the inner monad. 
-{-# INLINABLE traverse_ #-}
-traverse_ :: (Monad m, MonadTransUncons t) => (a -> m ()) -> t m a -> m ()
+-- Execute, traversing the stream with a side effect in the inner monad.
+{-# INLINEABLE traverse_ #-}
+traverse_ :: (Monad m) => (a -> m ()) -> ListT m a -> m ()
 traverse_ f =
   fold (const f) ()
 
 -- |
 -- Execute, consuming a list of the specified length and returning the remainder stream.
-{-# INLINABLE splitAt #-}
-splitAt :: (Monad m, MonadTransUncons t, MonadPlus (t m)) => Int -> t m a -> m ([a], t m a)
+{-# INLINEABLE splitAt #-}
+splitAt :: (Monad m) => Int -> ListT m a -> m ([a], ListT m a)
 splitAt =
   \case
     n | n > 0 -> \l ->
@@ -266,85 +409,89 @@
         Just (h, t) -> do
           (r1, r2) <- splitAt (pred n) t
           return (h : r1, r2)
-    _ -> \l -> 
+    _ -> \l ->
       return ([], l)
 
-
 -- * Construction
+
 -------------------------
 
 -- |
+-- Prepend an element.
+cons :: (Monad m) => a -> ListT m a -> ListT m a
+cons h t =
+  ListT $ return (Just (h, t))
+
+-- |
 -- Construct from any foldable.
-{-# INLINABLE fromFoldable #-}
-fromFoldable :: (MonadCons m, Foldable f) => f a -> m a
-fromFoldable = 
+{-# INLINEABLE fromFoldable #-}
+fromFoldable :: (Monad m, Foldable f) => f a -> ListT m a
+fromFoldable =
   foldr cons mzero
 
 -- |
--- Construct from an MVar, interpreting a value of Nothing as an end.
-fromMVar :: (MonadCons m, MonadIO m) => MVar (Maybe a) -> m a
+-- Construct from an MVar, interpreting the value of Nothing as the end.
+fromMVar :: (MonadIO m) => MVar (Maybe a) -> ListT m a
 fromMVar v =
   fix $ \loop -> liftIO (takeMVar v) >>= maybe mzero (flip cons loop)
 
 -- |
 -- Construct by unfolding a pure data structure.
-{-# INLINABLE unfold #-}
-unfold :: (MonadCons m) => (b -> Maybe (a, b)) -> b -> m a
+{-# INLINEABLE unfold #-}
+unfold :: (Monad m) => (b -> Maybe (a, b)) -> b -> ListT m a
 unfold f s =
   maybe mzero (\(h, t) -> cons h (unfold f t)) (f s)
 
 -- |
 -- Construct by unfolding a monadic data structure
 --
--- This is the most memory-efficient way to construct a ListT where
+-- This is the most memory-efficient way to construct ListT where
 -- the length depends on the inner monad.
-{-# INLINABLE unfoldM #-}
+{-# INLINEABLE unfoldM #-}
 unfoldM :: (Monad m) => (b -> m (Maybe (a, b))) -> b -> ListT m a
-unfoldM f = go where
-  go s = ListT $ f s >>= \case
-    Nothing -> return Nothing
-    Just (a,r) -> return (Just (a, go r))
+unfoldM f = go
+  where
+    go s =
+      ListT $
+        f s >>= \case
+          Nothing -> return Nothing
+          Just (a, r) -> return (Just (a, go r))
 
 -- |
 -- Produce an infinite stream.
-{-# INLINABLE repeat #-}
-repeat :: (MonadCons m) => a -> m a
-repeat = 
+{-# INLINEABLE repeat #-}
+repeat :: (Monad m) => a -> ListT m a
+repeat =
   fix . cons
 
-
 -- * Transformation
--------------------------
 
--- |
--- A function, which updates the contents of a list transformer.
--- 
--- Since it's merely just a function,
--- you can run it by passing a list transformer as an argument.
-type Transformation m a b = 
-  forall t. (Monad m, MonadCons (t m), MonadTransUncons t) =>
-  t m a -> t m b
+-------------------------
 
 -- |
 -- A transformation,
 -- which traverses the stream with an action in the inner monad.
-{-# INLINABLE traverse #-}
-traverse :: (a -> m b) -> Transformation m a b
-traverse f s =
-  lift (uncons s) >>= 
-  mapM (\(h, t) -> lift (f h) >>= \h' -> cons h' (traverse f t)) >>=
-  maybe mzero return
+{-# INLINEABLE traverse #-}
+traverse :: (Monad m) => (a -> m b) -> ListT m a -> ListT m b
+traverse f =
+  go
+  where
+    go (ListT run) =
+      ListT $
+        run >>= \case
+          Nothing -> return Nothing
+          Just (a, next) -> f a <&> \b -> Just (b, go next)
 
 -- |
 -- A transformation,
 -- reproducing the behaviour of @Data.List.'Data.List.take'@.
-{-# INLINABLE take #-}
-take :: Int -> Transformation m a a
+{-# INLINEABLE take #-}
+take :: (Monad m) => Int -> ListT m a -> ListT m a
 take =
   \case
     n | n > 0 -> \t ->
-      lift (uncons t) >>= 
-        \case
+      lift (uncons t)
+        >>= \case
           Nothing -> t
           Just (h, t) -> cons h (take (pred n) t)
     _ ->
@@ -353,43 +500,24 @@
 -- |
 -- A transformation,
 -- reproducing the behaviour of @Data.List.'Data.List.drop'@.
-{-# INLINABLE drop #-}
-drop :: Int -> Transformation m a a
+{-# INLINEABLE drop #-}
+drop :: (Monad m) => Int -> ListT m a -> ListT m a
 drop =
   \case
-    n | n > 0 ->
-      lift . uncons >=> maybe mzero (drop (pred n) . snd)
+    n
+      | n > 0 ->
+          lift . uncons >=> maybe mzero (drop (pred n) . snd)
     _ ->
       id
 
 -- |
 -- A transformation,
 -- which slices a list into chunks of the specified length.
-{-# INLINABLE slice #-}
-slice :: Positive Int -> Transformation m a [a]
-slice n l = 
+{-# INLINEABLE slice #-}
+slice :: (Monad m) => Int -> ListT m a -> ListT m [a]
+slice n l =
   do
-    (h, t) <- lift $ splitAt (case n of Positive n -> n) l
+    (h, t) <- lift $ splitAt n l
     case h of
       [] -> mzero
       _ -> cons h (slice n t)
-
-
--- * Positive numbers
--------------------------
-
--- |
--- A newtype wrapper around a number,
--- which ensures that it is greater than zero.
-newtype Positive n = 
-  Positive n
-  deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)
-
--- |
--- A smart constructor for positive numbers.
-positive :: (Ord n, Num n) => n -> Maybe (Positive n)
-positive =
-  \case
-    n | n > 0 -> Just $ Positive n
-    _ -> Nothing
-
diff --git a/library/ListT/Prelude.hs b/library/ListT/Prelude.hs
new file mode 100644
--- /dev/null
+++ b/library/ListT/Prelude.hs
@@ -0,0 +1,93 @@
+{-# OPTIONS_GHC -Wno-dodgy-imports #-}
+
+module ListT.Prelude
+  ( module Exports,
+    bimapPair',
+    secondPair',
+  )
+where
+
+import Control.Applicative as Exports
+import Control.Category as Exports
+import Control.Concurrent as Exports
+import Control.Exception as Exports
+import Control.Foldl as Exports (Fold (..), FoldM (..))
+import Control.Monad as Exports hiding (fail, forM, forM_, mapM, mapM_, msum, sequence, sequence_)
+import Control.Monad.Base as Exports
+import Control.Monad.Error.Class as Exports
+import Control.Monad.Fail as Exports
+import Control.Monad.Fix as Exports hiding (fix)
+import Control.Monad.IO.Class as Exports
+import Control.Monad.Logic.Class as Exports
+import Control.Monad.Morph as Exports hiding (MonadTrans (..))
+import Control.Monad.Reader.Class as Exports
+import Control.Monad.ST as Exports
+import Control.Monad.State.Class as Exports
+import Control.Monad.Trans.Class as Exports
+import Control.Monad.Trans.Control as Exports hiding (embed, embed_)
+import Control.Monad.Trans.Maybe as Exports hiding (liftCallCC, liftCatch)
+import Control.Monad.Zip as Exports
+import Data.Bits as Exports
+import Data.Bool as Exports
+import Data.Char as Exports
+import Data.Coerce as Exports
+import Data.Complex as Exports
+import Data.Data as Exports
+import Data.Dynamic as Exports
+import Data.Either as Exports
+import Data.Fixed as Exports
+import Data.Foldable as Exports
+import Data.Function as Exports hiding (id, (.))
+import Data.Functor as Exports hiding (unzip)
+import Data.Functor.Classes as Exports
+import Data.IORef as Exports
+import Data.Int as Exports
+import Data.Ix as Exports
+import Data.List as Exports hiding (all, and, any, concat, concatMap, elem, find, foldl, foldl', foldl1, foldr, foldr1, isSubsequenceOf, mapAccumL, mapAccumR, maximum, maximumBy, minimum, minimumBy, notElem, or, product, sortOn, sum, uncons)
+import Data.Maybe as Exports
+import Data.Monoid as Exports hiding (First, Last, getFirst, getLast, (<>))
+import Data.Ord as Exports
+import Data.Proxy as Exports
+import Data.Ratio as Exports
+import Data.STRef as Exports
+import Data.Semigroup as Exports
+import Data.String as Exports
+import Data.Traversable as Exports
+import Data.Tuple as Exports
+import Data.Unique as Exports
+import Data.Version as Exports
+import Data.Word as Exports
+import Debug.Trace as Exports
+import Foreign.ForeignPtr as Exports
+import Foreign.Ptr as Exports
+import Foreign.StablePtr as Exports
+import Foreign.Storable as Exports
+import GHC.Conc as Exports hiding (threadWaitRead, threadWaitReadSTM, threadWaitWrite, threadWaitWriteSTM, withMVar)
+import GHC.Exts as Exports (groupWith, inline, lazy, sortWith)
+import GHC.Generics as Exports (Generic)
+import GHC.IO.Exception as Exports
+import Numeric as Exports
+import System.Environment as Exports
+import System.Exit as Exports
+import System.IO as Exports (Handle, hClose)
+import System.IO.Error as Exports
+import System.IO.Unsafe as Exports
+import System.Mem as Exports
+import System.Mem.StableName as Exports
+import System.Timeout as Exports
+import Text.Printf as Exports (hPrintf, printf)
+import Text.Read as Exports (Read (..), readEither, readMaybe)
+import Unsafe.Coerce as Exports
+import Prelude as Exports hiding (all, and, any, concat, concatMap, elem, fail, foldl, foldl1, foldr, foldr1, id, mapM, mapM_, maximum, minimum, notElem, or, product, sequence, sequence_, sum, unzip, (.))
+
+-- |
+-- A slightly stricter version of Data.Bifunctor.bimap.
+-- There's no benefit to producing lazy pairs here.
+bimapPair' :: (a -> b) -> (c -> d) -> (a, c) -> (b, d)
+bimapPair' f g = \(a, c) -> (f a, g c)
+
+-- |
+-- A slightly stricter version of Data.Bifunctor.second
+-- that doesn't produce gratuitous lazy pairs.
+secondPair' :: (b -> c) -> (a, b) -> (a, c)
+secondPair' f = \(a, b) -> (a, f b)
diff --git a/list-t.cabal b/list-t.cabal
--- a/list-t.cabal
+++ b/list-t.cabal
@@ -1,75 +1,91 @@
-name:
-  list-t
-version:
-  0.4.7
-synopsis:
-  ListT done right
+cabal-version: 3.0
+name:          list-t
+version:       1.0.5.7
+synopsis:      ListT done right
 description:
   A correct implementation of the list monad-transformer.
   Useful for basic streaming.
-category:
-  Streaming, Data Structures, Control
-homepage:
-  https://github.com/nikita-volkov/list-t
-bug-reports:
-  https://github.com/nikita-volkov/list-t/issues
-author:
-  Nikita Volkov <nikita.y.volkov@mail.ru>
-maintainer:
-  Nikita Volkov <nikita.y.volkov@mail.ru>
-copyright:
-  (c) 2014, Nikita Volkov
-license:
-  MIT
-license-file:
-  LICENSE
-build-type:
-  Simple
-cabal-version:
-  >=1.10
 
+category:      Streaming, Data Structures, Control
+homepage:      https://github.com/nikita-volkov/list-t
+bug-reports:   https://github.com/nikita-volkov/list-t/issues
+author:        Nikita Volkov <nikita.y.volkov@mail.ru>
+maintainer:    Nikita Volkov <nikita.y.volkov@mail.ru>
+copyright:     (c) 2014, Nikita Volkov
+license:       MIT
+license-file:  LICENSE
 
 source-repository head
-  type:
-    git
-  location:
-    git://github.com/nikita-volkov/list-t.git
+  type:     git
+  location: git://github.com/nikita-volkov/list-t.git
 
+common language-settings
+  default-extensions:
+    NoImplicitPrelude
+    NoMonomorphismRestriction
+    BangPatterns
+    ConstraintKinds
+    DataKinds
+    DefaultSignatures
+    DeriveDataTypeable
+    DeriveFunctor
+    DeriveGeneric
+    DeriveTraversable
+    EmptyDataDecls
+    FlexibleContexts
+    FlexibleInstances
+    FunctionalDependencies
+    GADTs
+    GeneralizedNewtypeDeriving
+    LambdaCase
+    LiberalTypeSynonyms
+    MagicHash
+    MultiParamTypeClasses
+    MultiWayIf
+    OverloadedStrings
+    ParallelListComp
+    PatternGuards
+    PolyKinds
+    QuasiQuotes
+    RankNTypes
+    RecordWildCards
+    ScopedTypeVariables
+    StandaloneDeriving
+    TemplateHaskell
+    TupleSections
+    TypeFamilies
+    TypeOperators
+    UnboxedTuples
+    UndecidableInstances
 
+  default-language:   Haskell2010
+
 library
-  hs-source-dirs:
-    library
-  other-modules:
-  exposed-modules:
-    ListT
+  import:          language-settings
+  hs-source-dirs:  library
+  exposed-modules: ListT
+  other-modules:   ListT.Prelude
   build-depends:
-    mmorph == 1.*,
-    monad-control >= 0.3 && < 2,
-    mtl == 2.*,
-    transformers-base == 0.4.*,
-    transformers >= 0.3 && < 0.6,
-    base-prelude < 2,
-    base < 4.10
-  default-extensions:
-    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, PolyKinds, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
-  default-language:
-    Haskell2010
+    , base >=4.11 && <5
+    , foldl >=1.2 && <2
+    , logict >=0.7 && <0.9
+    , mmorph >=1 && <2
+    , monad-control >=0.3 && <2
+    , mtl >=2 && <3
+    , transformers >=0.3 && <0.7
+    , transformers-base ^>=0.4
 
+  if impl(ghc <8.0)
+    build-depends: semigroups >=0.11 && <0.21
 
-test-suite tests
-  type:
-    exitcode-stdio-1.0
-  hs-source-dirs:
-    tests
-  main-is:
-    Main.hs
+test-suite htf-test
+  import:         language-settings
+  type:           exitcode-stdio-1.0
+  hs-source-dirs: htf-test
+  main-is:        Main.hs
   build-depends:
-    list-t,
-    mmorph,
-    HTF == 0.13.*,
-    mtl-prelude < 3,
-    base-prelude
-  default-extensions:
-    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, PolyKinds, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
-  default-language:
-    Haskell2010
+    , base-prelude
+    , HTF ^>=0.15
+    , list-t
+    , mmorph
+    , mtl-prelude <3
diff --git a/tests/Main.hs b/tests/Main.hs
deleted file mode 100644
--- a/tests/Main.hs
+++ /dev/null
@@ -1,156 +0,0 @@
-{-# OPTIONS_GHC -F -pgmF htfpp #-}
-
-import BasePrelude hiding (toList)
-import MTLPrelude
-import Control.Monad.Morph
-import Test.Framework
-import qualified ListT as L
-
-
-main = htfMain $ htf_thisModulesTests
-
-
--- * MMonad
--------------------------
-
--- embed lift = id
-prop_mmonadLaw1 (l :: [Int]) =
-  let s = L.fromFoldable l
-      in runIdentity $ streamsEqual s (embed lift s)
-
--- embed f (lift m) = f m
-prop_mmonadLaw2 l =
-  let s = (L.fromFoldable :: [Int] -> L.ListT Identity Int) l
-      f = MaybeT . fmap Just
-      run = runIdentity . L.toList . runMaybeT
-      in 
-        run (f s) == 
-        run (embed f (lift s))
-
-
--- * Applicative
--------------------------
-
-prop_applicativeIdentityLaw (l :: [Int]) =
-  runIdentity $ streamsEqual (pure id <*> s) s
-  where
-    s = L.fromFoldable l
-
-prop_applicativeBehavesLikeList =
-  \(ns :: [Int]) ->
-    let a = fs <*> ns
-        b = runIdentity (toList $ L.fromFoldable fs <*> L.fromFoldable ns)
-        in a == b
-  where
-    fs = [(+1), (+3), (+5)]
-
-
--- * Monad
--------------------------
-
-test_monadLaw1 =
-  assertBool =<< streamsEqual (return a >>= k) (k a)
-  where
-    a = 2
-    k a = return $ chr a
-
-test_monadLaw2 =
-  assertBool =<< streamsEqual (m >>= return) m
-  where
-    m = L.fromFoldable ['a'..'z']
-
-test_monadLaw3 =
-  assertBool =<< streamsEqual (m >>= (\x -> k x >>= h)) ((m >>= k) >>= h)
-  where
-    m = L.fromFoldable ['a'..'z']
-    k a = return $ ord a
-    h a = return $ a + 1
-
-test_monadLaw4 =
-  assertBool =<< streamsEqual (fmap f xs) (xs >>= return . f)
-  where
-    f = ord
-    xs = L.fromFoldable ['a'..'z']
-
-
--- * Monoid
--------------------------
-
-test_mappend =
-  assertBool =<< 
-    streamsEqual 
-      (L.fromFoldable [0..7]) 
-      (L.fromFoldable [0..3] <> L.fromFoldable [4..7])
-
-test_mappendAndTake =
-  assertBool =<< 
-    streamsEqual 
-      (L.fromFoldable [0..5]) 
-      (L.take 6 $ L.fromFoldable [0..3] <> L.fromFoldable [4..7])
-
-test_mappendDoesntCauseTraversal =
-  do
-    ref <- newIORef 0
-    (flip runReaderT) ref (toList $ L.take 5 $ stream <> stream)
-    assertEqual 5 =<< readIORef ref
-  where
-    stream =
-      do
-        ref <- lift $ ask
-        x <- L.fromFoldable [0..4]
-        liftIO $ modifyIORef ref (+1)
-        return x
-
-
--- * Other
--------------------------
-
-test_repeat =
-  assertEqual [2,2,2] =<< do
-    toList $ L.take 3 $ L.repeat (2 :: Int)
-
-test_traverseDoesntCauseTraversal =
-  do
-    ref <- newIORef 0
-    (flip runReaderT) ref (toList stream3)
-    assertEqual 3 =<< readIORef ref
-  where
-    stream1 =
-      do
-        ref <- lift $ ask
-        x <- L.fromFoldable ['a'..'z']
-        liftIO $ modifyIORef ref (+1)
-        return x
-    stream2 =
-      L.traverse (return . toUpper) stream1
-    stream3 =
-      L.take 3 stream2
-
-test_takeDoesntCauseTraversal =
-  do
-    ref <- newIORef 0
-    (flip runReaderT) ref (toList $ L.take 3 $ L.take 7 $ stream)
-    assertEqual 3 =<< readIORef ref
-  where
-    stream =
-      do
-        ref <- lift $ ask
-        x <- L.fromFoldable [0..10]
-        liftIO $ modifyIORef ref (+1)
-        return x
-
-test_drop =
-  assertEqual [3, 4] =<< do
-    toList $ L.drop 2 $ L.fromFoldable [1 .. 4]
-    
-test_slice =
-  assertEqual ["abc", "def", "gh"] =<< do
-    toList $ L.slice (fromJust $ L.positive 3) $ L.fromFoldable ("abcdefgh" :: [Char])
-
-
-toList :: Monad m => L.ListT m a -> m [a]
-toList = L.toList
-
-streamsEqual :: (Applicative m, Monad m, Eq a) => L.ListT m a -> L.ListT m a -> m Bool
-streamsEqual a b =
-  (==) <$> L.toList a <*> L.toList b
