diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Revision history for multi-except
 
+## 1.0.0 -- 2024-01-03
+
+* Split into two libraries, `multi-except`, and `multi-except:semigroupoid-instances`
+* Migrated to custom dlist, removing the dependency
+* Support more GHC versions (tested down to 7.0.4)
+
 ## 0.3.0.0 -- 2021-06-06
 
 * Switch to dlist-nonempty package
diff --git a/Control/Applicative/MultiExcept.hs b/Control/Applicative/MultiExcept.hs
deleted file mode 100644
--- a/Control/Applicative/MultiExcept.hs
+++ /dev/null
@@ -1,94 +0,0 @@
-{-|
-Module      : Control.Applicative.MultiExcept
-Copyright   : (c) Owen Shepherd, 2021
-License     : MIT
-Maintainer  : owen@owen.cafe
-Stability   : stable
-Portability : portable
--}
-
-{-# LANGUAGE ScopedTypeVariables #-}
-
-module Control.Applicative.MultiExcept
-  ( MultiExcept
-  , fromEither
-  , fromEitherPoly
-  , join
-  , runMultiExcept
-  , succeed
-  , throwError
-  , throwErrors
-  ) where
-
-import Data.Bifunctor
-import Data.Functor.Alt
-import Data.DList.NonEmpty (NonEmptyDList)
-
--- | A 'MultiExcept' is a success value, or one or more errors.
-data MultiExcept err a
-  = Success a
-  | Errors (NonEmptyDList err)
-  deriving (Eq, Ord, Read, Show)
-
--- | Run the computation.
-runMultiExcept :: MultiExcept err a -> Either (NonEmptyDList err) a
-runMultiExcept (Errors errs) = Left errs
-runMultiExcept (Success a) = Right a
-
--- | Throw a single error.
-throwError :: forall a err. err -> MultiExcept err a
-throwError = Errors . pure
-
--- | Throw one or more errors.
-throwErrors :: forall a err. NonEmptyDList err -> MultiExcept err a
-throwErrors = Errors
-
--- | Embeds a value into a 'MultiExcept' context.
-succeed :: forall err a. a -> MultiExcept err a
-succeed = Success
-
--- | Convert an 'Either' to a 'MultiExcept'.
-fromEither :: Either err a -> MultiExcept err a
-fromEither (Left err) = throwError err
-fromEither (Right a) = Success a
-
--- | Convert a multi-error 'Either' to a 'MultiExcept'.
-fromEitherPoly :: Either (NonEmptyDList err) a -> MultiExcept err a
-fromEitherPoly (Left errs) = Errors errs
-fromEitherPoly (Right a) = Success a
-
--- | Join nested 'MultiExcept's with the same error type.
---   Note that this doesn't imply a __useful__ 'Monad' instance.
---   The instance defined in terms of join discards errors on the RHS of '>>='.
-join :: MultiExcept err (MultiExcept err a) -> MultiExcept err a
-join (Success a) = a
-join (Errors a) = Errors a
-
-instance Functor (MultiExcept err) where
-  fmap f (Success a) = Success $ f a
-  fmap _ (Errors errs) = Errors errs
-
-instance Bifunctor MultiExcept where
- bimap _ fa (Success a)    = Success $ fa a
- bimap ferr _ (Errors err) = Errors $ fmap ferr err
-
-instance Applicative (MultiExcept err) where
-  pure = Success
-
-  Errors l <*> Errors l' = Errors $ l <> l'
-  Success f <*> Success a = Success $ f a
-  Errors l <*> _ = Errors l
-  _ <*> Errors l = Errors l
-
-instance Alt (MultiExcept err) where
-  Success a <!> _ = Success a
-  _ <!> Success a = Success a
-  Errors l <!> Errors r = Errors (l <> r)
-
-instance Foldable (MultiExcept err) where
-  foldr f acc (Success a) = f a acc
-  foldr _ acc _           = acc
-
-instance Traversable (MultiExcept err) where
-  traverse f (Success a)   = Success <$> f a
-  traverse _ (Errors err) = pure $ Errors err
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,45 @@
+# multi-except
+
+[![Hackage version](https://img.shields.io/hackage/v/multi-except?color=purple)](https://hackage.haskell.org/package/multi-except)
+[![CI Status](https://img.shields.io/github/actions/workflow/status/414owen/multi-except/haskell-ci.yml)](https://github.com/414owen/multi-except/actions)
+
+multi-except - succeed, or return one or more errors
+
+## Adding the dependency
+
+```
+-- in your cabal file
+  -- Add the main package (only depends on base!)
+  , multi-except
+  -- For the Alt instance (depends on semigroupoids)
+  , multi-except:semigroupoid-instances
+```
+
+## Usage
+
+
+```haskell
+{-# LANGUAGE ApplicativeDo #-}
+
+import Control.Applicative.MultiExcept
+
+errors :: MultiExcept String (Int, Int, Int)
+errors = do
+  a <- throwError "no monad instance"
+  b <- pure 12
+  c <- throwError "i am scared"
+  pure (a, b, c)
+
+-- errors: Errors ["no monad instance", "i am scared"]
+```
+
+The use of `ApplicativeDo` is significant and necessary for using
+`MultiExcept` with do notation.
+
+`MultiExcept` is not a `Monad`, only an `Applicative`, so a few constraints
+apply, such as not being able to determine the structure of the rest of the
+computation based on a previously do-bound value. If the previous sentence was
+confusing, then you might want to consider using a writer monad instead.
+
+To compose with other applicative effects, you can use
+[`Data.Functor.Compose`](https://hackage.haskell.org/package/base-4.19.0.0/docs/Data-Functor-Compose.html).
diff --git a/multi-except-semigroupoids/Control/Applicative/MultiExcept/Alt.hs b/multi-except-semigroupoids/Control/Applicative/MultiExcept/Alt.hs
new file mode 100644
--- /dev/null
+++ b/multi-except-semigroupoids/Control/Applicative/MultiExcept/Alt.hs
@@ -0,0 +1,22 @@
+{-|
+Module      : Control.Applicative.MultiExcept.Alt
+Copyright   : (c) Owen Shepherd, 2023
+License     : MIT
+Maintainer  : owen@owen.cafe
+Stability   : stable
+Portability : portable
+-}
+
+{-# LANGUAGE CPP #-}
+#if MIN_VERSION_base(4,9,0)
+{-# OPTIONS_GHC -Wno-orphans #-}
+#endif
+
+module Control.Applicative.MultiExcept.Alt where
+
+import Prelude ()
+import Control.Applicative.MultiExcept
+import Data.Functor.Alt                (Alt(..))
+
+instance Alt (MultiExcept err) where
+  (<!>) = or
diff --git a/multi-except.cabal b/multi-except.cabal
--- a/multi-except.cabal
+++ b/multi-except.cabal
@@ -1,37 +1,75 @@
-cabal-version:       >=1.10
+cabal-version:       3.0
 
 name:                multi-except
-version:             0.3.0.0
+version:             1.0.0
 synopsis:            Multiple Exceptions
-description:         Exception type that supports reporting multiple exceptions
+description:         Report multiple errors, or a single correct value.
 license:             MIT
 license-file:        LICENSE
 author:              Owen Shepherd
 maintainer:          owen@owen.cafe
 category:            Exceptions
 build-type:          Simple
-extra-source-files:  CHANGELOG.md
+extra-doc-files:     CHANGELOG.md
+                   , README.md
 homepage:            https://github.com/414owen/multi-except
 
+tested-with:
+    GHC==9.6.2
+  , GHC==9.4.5
+  , GHC==9.2.8
+  , GHC==9.0.2
+  , GHC==8.10.7
+  , GHC==8.8.4
+  , GHC==8.6.5
+  , GHC==8.4.4
+  , GHC==8.2.2
+  , GHC==8.0.2
+  , GHC==7.10.3
+  , GHC==7.8.4
+  , GHC==7.6.3
+  , GHC==7.4.2
+  , GHC==7.2.2
+  , GHC==7.0.4
+
+source-repository head
+  type:     git
+  location: https://github.com/414owen/multi-except.git
+
+common common-import
+  default-language:    Haskell2010
+  ghc-options:         -Wall -W
+  if impl(ghc >= 8.0)
+    ghc-options:       -Wcompat
+
 library
+  import:              common-import
   exposed-modules:     Control.Applicative.MultiExcept
+  hs-source-dirs:      multi-except
   build-depends:       base >=4.0 && <5
-                     , dlist-nonempty
-                     , semigroupoids
-  default-language:    Haskell2010
 
+library semigroupoid-instances
+  import:              common-import
+  exposed-modules:     Control.Applicative.MultiExcept.Alt
+  hs-source-dirs:      multi-except-semigroupoids
+  visibility:          public
+  build-depends:       base >=4.0 && <5
+                     , multi-except
+                     , semigroupoids >=1 && <7
+
 test-suite unit-tests
+  import:             common-import
   type:               exitcode-stdio-1.0
   hs-source-dirs:     test
   main-is:            Main.hs
-  default-language:   Haskell2010
-  other-modules:      Test.Functor
-                    , Test.Applicative
-                    , Test.Alt
-                    , Test.Bifunctor
-                    , Test.Foldable
-                    , Test.Traversable
+  other-modules:      Test.MultiExcept.Functor
+                    , Test.MultiExcept.Applicative
+                    , Test.MultiExcept.Alt
+                    , Test.MultiExcept.Bifunctor
+                    , Test.MultiExcept.Foldable
+                    , Test.MultiExcept.Traversable
   build-depends:      base
                     , multi-except
-                    , semigroupoids
-                    , hspec
+                    , multi-except:semigroupoid-instances
+                    , hspec >=2 && <3
+                    , semigroupoids >=1 && <7
diff --git a/multi-except/Control/Applicative/MultiExcept.hs b/multi-except/Control/Applicative/MultiExcept.hs
new file mode 100644
--- /dev/null
+++ b/multi-except/Control/Applicative/MultiExcept.hs
@@ -0,0 +1,175 @@
+{-|
+Module      : Control.Applicative.MultiExcept
+Copyright   : (c) Owen Shepherd, 2021
+License     : MIT
+Maintainer  : owen@owen.cafe
+Stability   : stable
+Portability : portable
+-}
+
+{-# LANGUAGE CPP                 #-}
+{-# LANGUAGE NoImplicitPrelude   #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Control.Applicative.MultiExcept
+  ( MultiExcept
+  , fromEither
+  , fromEitherPoly
+  , join
+  , or
+  , runMultiExcept
+  , succeed
+  , throwError
+  , throwErrors
+  , mapMultiExcept
+  ) where
+
+import Prelude (Eq(..), Ord(..), Either(..), (.), ($), id, Show(..), (++))
+
+import Control.Applicative  (Applicative(..))
+#if MIN_VERSION_base(4,8,0)
+import Data.Bifunctor
+#endif
+import Data.Functor         (Functor(..), (<$>))
+import Data.Foldable        (Foldable(..))
+import Data.Traversable     (Traversable(..))
+#if MIN_VERSION_base(4,9,0)
+import Data.List.NonEmpty   (NonEmpty(..))
+#endif
+
+
+-- NonEmptyDList
+
+-- | This is written here because:
+-- * The version in dlist is currently limited to ghc>=8.0
+-- * The version in dlist-nonempty is too heavy on dependencies
+-- * We only need a few trivial features anyway
+
+data NonEmptyDList a = NonEmptyDList !a !([a] -> [a])
+
+{-# INLINE nedlSingleton #-}
+nedlSingleton :: a -> NonEmptyDList a
+nedlSingleton a = NonEmptyDList a id
+
+{-# INLINE runNonEmptyDList #-}
+runNonEmptyDList :: NonEmptyDList a -> (a, [a])
+runNonEmptyDList (NonEmptyDList x xs) = (x, xs [])
+
+instance Eq a => Eq (NonEmptyDList a) where
+  NonEmptyDList x xs == NonEmptyDList y ys = x : xs [] == y : ys []
+
+instance Ord a => Ord (NonEmptyDList a) where
+  NonEmptyDList x xs `compare` NonEmptyDList y ys = (x : xs []) `compare` (y : ys [])
+
+instance Show a => Show (NonEmptyDList a) where
+  show nedl = case runNonEmptyDList nedl of
+    (x, xs) -> show $ x : xs
+
+appendNedl :: NonEmptyDList a -> NonEmptyDList a -> NonEmptyDList a
+appendNedl (NonEmptyDList x xs) (NonEmptyDList y ys) = NonEmptyDList x $ xs . (y:) . ys
+
+-- WARNING: O(n) space
+-- TODO Make this constant space
+instance Functor NonEmptyDList where
+  fmap f nedl = case runNonEmptyDList nedl of
+    (x, xs) -> NonEmptyDList (f x) (fmap f xs ++)
+
+-- | A 'MultiExcept' is a success value, or one or more errors.
+data MultiExcept err a
+  = Success !a
+  | Errors !(NonEmptyDList err)
+  deriving (Eq, Ord, Show)
+
+-- | Run the computation.
+runMultiExcept :: MultiExcept err a -> Either (NonEmptyDList err) a
+runMultiExcept (Errors errs) = Left errs
+runMultiExcept (Success a) = Right a
+
+-- | Throw a single error.
+throwError :: forall a err. err -> MultiExcept err a
+throwError = Errors . nedlSingleton
+
+#if MIN_VERSION_base(4,9,0)
+
+-- | Throw one or more errors.
+throwErrors :: forall a err. NonEmpty err -> MultiExcept err a
+throwErrors (err :| errs) = Errors $ NonEmptyDList err (errs ++)
+
+#else
+
+-- | Throw one or more errors.
+throwErrors :: forall a err. (err, [err]) -> MultiExcept err a
+throwErrors (err, errs) = Errors $ NonEmptyDList err (errs ++)
+
+#endif
+
+-- | Embeds a value into a 'MultiExcept' context.
+succeed :: forall err a. a -> MultiExcept err a
+succeed = Success
+
+-- | Convert an 'Either' to a 'MultiExcept'.
+fromEither :: Either err a -> MultiExcept err a
+fromEither (Left err) = throwError err
+fromEither (Right a) = Success a
+
+#if MIN_VERSION_base(4,9,0)
+
+-- | Convert a multi-error 'Either' to a 'MultiExcept'.
+fromEitherPoly :: Either (NonEmpty err) a -> MultiExcept err a
+fromEitherPoly (Left errs) = throwErrors errs
+fromEitherPoly (Right a) = Success a
+
+#else
+
+-- | Convert a multi-error 'Either' to a 'MultiExcept'.
+fromEitherPoly :: Either (err, [err]) a -> MultiExcept err a
+fromEitherPoly (Left errs) = throwErrors errs
+fromEitherPoly (Right a) = Success a
+
+#endif
+
+-- | Join nested 'MultiExcept's with the same error type.
+--   Note that this doesn't imply a __useful__ 'Monad' instance.
+--   The instance defined in terms of join discards errors on the RHS of '>>='.
+join :: MultiExcept err (MultiExcept err a) -> MultiExcept err a
+join (Success a) = a
+join (Errors a) = Errors a
+
+instance Functor (MultiExcept err) where
+  fmap f (Success a) = Success $ f a
+  fmap _ (Errors errs) = Errors errs
+
+mapMultiExcept:: (err -> err') -> (a -> a') -> MultiExcept err a -> MultiExcept err' a'
+mapMultiExcept _ fa (Success a)    = Success $ fa a
+mapMultiExcept ferr _ (Errors err) = Errors $ fmap ferr err
+
+
+#if MIN_VERSION_base(4,8,0)
+
+-- | WARNING: O(n) space and time in the length of the amount of errors
+-- this could be fixed by changing the difference list Functor instance.
+instance Bifunctor MultiExcept where
+  bimap = mapMultiExcept
+
+#endif
+
+instance Applicative (MultiExcept err) where
+  pure = Success
+
+  Errors l <*> Errors l' = Errors $ appendNedl l l'
+  Success f <*> Success a = Success $ f a
+  Errors l <*> _ = Errors l
+  _ <*> Errors l = Errors l
+
+or :: MultiExcept err a -> MultiExcept err a -> MultiExcept err a
+Success a `or` _ = Success a
+_ `or` Success a = Success a
+Errors l `or` Errors r = Errors $ appendNedl l r
+
+instance Foldable (MultiExcept err) where
+  foldr f acc (Success a) = f a acc
+  foldr _ acc _           = acc
+
+instance Traversable (MultiExcept err) where
+  traverse f (Success a)   = Success <$> f a
+  traverse _ (Errors err) = pure $ Errors err
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -2,21 +2,21 @@
 
 import Test.Hspec
 
-import qualified Test.Applicative
-import qualified Test.Alt
-import qualified Test.Functor
-import qualified Test.Bifunctor
-import qualified Test.Foldable
-import qualified Test.Traversable
+import qualified Test.MultiExcept.Applicative
+import qualified Test.MultiExcept.Alt
+import qualified Test.MultiExcept.Functor
+import qualified Test.MultiExcept.Bifunctor
+import qualified Test.MultiExcept.Foldable
+import qualified Test.MultiExcept.Traversable
 
 main :: IO ()
 main = hspec spec
 
 spec :: Spec
 spec = do
-  Test.Functor.spec
-  Test.Applicative.spec
-  Test.Alt.spec
-  Test.Bifunctor.spec
-  Test.Foldable.spec
-  Test.Traversable.spec
+  Test.MultiExcept.Functor.spec
+  Test.MultiExcept.Applicative.spec
+  Test.MultiExcept.Alt.spec
+  Test.MultiExcept.Bifunctor.spec
+  Test.MultiExcept.Foldable.spec
+  Test.MultiExcept.Traversable.spec
diff --git a/test/Test/Alt.hs b/test/Test/Alt.hs
deleted file mode 100644
--- a/test/Test/Alt.hs
+++ /dev/null
@@ -1,22 +0,0 @@
-{-# LANGUAGE OverloadedLists #-}
-{-# LANGUAGE TypeApplications #-}
-
-module Test.Alt
-  ( spec
-  ) where
-
-import Test.Hspec
-
-import Data.Functor.Alt
-import Control.Applicative.MultiExcept
-
-spec :: Spec
-spec = describe "Alt instance" $ do
-  it "propagates left succeed" $
-    succeed 1 <!> throwError 3 `shouldBe` succeed 1
-  it "propagates right succeed" $
-    throwError () <!> succeed 1 `shouldBe` succeed 1
-  it "prioritizes left succeed" $
-    succeed 1 <!> succeed 2 `shouldBe` succeed @() 1
-  it "propagates both errors" $ do
-    throwError 1 <!> throwError 2`shouldBe` throwErrors @() [1, 2]
diff --git a/test/Test/Applicative.hs b/test/Test/Applicative.hs
deleted file mode 100644
--- a/test/Test/Applicative.hs
+++ /dev/null
@@ -1,28 +0,0 @@
-{-# LANGUAGE OverloadedLists #-}
-{-# LANGUAGE TypeApplications #-}
-{-# LANGUAGE TupleSections #-}
-
-module Test.Applicative
-  ( spec
-  ) where
-
-import Test.Hspec
-
-import Control.Applicative.MultiExcept
-
-testErrors :: MultiExcept Int Int
-testErrors = fromEitherPoly $ Left [2, 3]
-
-spec :: Spec
-spec = describe "Applicative instance" $ do
-  describe "pure" $
-    it "succeeds" $
-      pure 5 `shouldBe` (succeed 5 :: MultiExcept () Int)
-  describe "<*>" $ do
-    it "accumulates errors" $
-      throwError 2 <*> throwError 3 `shouldBe` testErrors
-    it "propagates successes" $
-      pure (3,) <*> pure 4 `shouldBe` succeed @() (3, 4)
-    it "errors when only one side is successful" $ do
-      succeed (+ 1) <*> testErrors `shouldBe` testErrors
-      throwError 2 <*> succeed () `shouldBe` throwError @Int 2
diff --git a/test/Test/Bifunctor.hs b/test/Test/Bifunctor.hs
deleted file mode 100644
--- a/test/Test/Bifunctor.hs
+++ /dev/null
@@ -1,18 +0,0 @@
-{-# LANGUAGE OverloadedLists #-}
-{-# LANGUAGE TypeApplications #-}
-
-module Test.Bifunctor
-  ( spec
-  ) where
-
-import Test.Hspec
-
-import Data.Bifunctor
-import Control.Applicative.MultiExcept
-
-spec :: Spec
-spec = describe "Bifunctor instance" $ do
-  it "maps errors with first" $
-    first (+ 1) (throwErrors [3, 4]) `shouldBe` throwErrors @() [4, 5]
-  it "maps successes with second" $
-    second (+ 1) (succeed 3) `shouldBe` succeed @() 4
diff --git a/test/Test/Foldable.hs b/test/Test/Foldable.hs
deleted file mode 100644
--- a/test/Test/Foldable.hs
+++ /dev/null
@@ -1,17 +0,0 @@
-{-# LANGUAGE OverloadedLists #-}
-{-# LANGUAGE TypeApplications #-}
-
-module Test.Foldable
-  ( spec
-  ) where
-
-import Test.Hspec
-
-import Control.Applicative.MultiExcept
-
-spec :: Spec
-spec = describe "Foldable instance" $ do
-  it "f is called for success" $
-    foldr (+) 2 (succeed 1)`shouldBe` 3
-  it "acc is returned for errors" $
-    foldr (+) 2 (throwError 1)`shouldBe` 2
diff --git a/test/Test/Functor.hs b/test/Test/Functor.hs
deleted file mode 100644
--- a/test/Test/Functor.hs
+++ /dev/null
@@ -1,20 +0,0 @@
-{-# LANGUAGE OverloadedLists #-}
-{-# LANGUAGE TypeApplications #-}
-
-module Test.Functor
-  ( spec
-  ) where
-
-import Test.Hspec
-
-import Control.Applicative.MultiExcept
-
-testErrors :: MultiExcept Int Int
-testErrors = fromEitherPoly (Left [2, 3])
-
-spec :: Spec
-spec = describe "Functor instance" $ do
-  it "transforms success value" $
-    ((+ 3) <$> succeed 2) `shouldBe` succeed @() 5
-  it "doesn't affect errors" $ do
-    fmap (+ 3) testErrors `shouldBe` testErrors
diff --git a/test/Test/MultiExcept/Alt.hs b/test/Test/MultiExcept/Alt.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/MultiExcept/Alt.hs
@@ -0,0 +1,22 @@
+module Test.MultiExcept.Alt
+  ( spec
+  ) where
+
+import Test.Hspec
+
+import Prelude (Int, ($))
+import Control.Applicative (Applicative(..))
+import Data.Functor.Alt
+import Control.Applicative.MultiExcept
+import Control.Applicative.MultiExcept.Alt ()
+
+spec :: Spec
+spec = describe "Alt instance" $ do
+  it "propagates left succeed" $ do
+    succeed 1 <!> throwError () `shouldBe` succeed (1 :: Int)
+  it "propagates right succeed" $
+    throwError () <!> succeed 1 `shouldBe` succeed (1 :: Int)
+  it "prioritizes left succeed" $
+    succeed 1 <!> succeed 2 `shouldBe` (succeed 1 :: MultiExcept () Int)
+  it "propagates both errors" $ do
+    throwError 1 <!> throwError 2 `shouldBe` (throwError 1 *> throwError 2 :: MultiExcept Int ())
diff --git a/test/Test/MultiExcept/Applicative.hs b/test/Test/MultiExcept/Applicative.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/MultiExcept/Applicative.hs
@@ -0,0 +1,27 @@
+module Test.MultiExcept.Applicative
+  ( spec
+  ) where
+
+import Test.Hspec
+
+import Prelude (Int, ($), Num(..))
+
+import Control.Applicative (Applicative(..))
+import Control.Applicative.MultiExcept
+
+testErrors :: MultiExcept Int Int
+testErrors = throwError 2 *> throwError 3
+
+spec :: Spec
+spec = describe "Applicative instance" $ do
+  describe "pure" $
+    it "succeeds" $
+      pure 5 `shouldBe` (succeed 5 :: MultiExcept () Int)
+  describe "<*>" $ do
+    it "accumulates errors" $
+      throwError 2 <*> throwError 3 `shouldBe` testErrors
+    it "propagates successes" $
+      pure (3,4) `shouldBe` (succeed (3, 4) :: MultiExcept () (Int, Int))
+    it "errors when only one side is successful" $ do
+      succeed (+ 1) <*> testErrors `shouldBe` testErrors
+      throwError 2 <*> succeed () `shouldBe` (throwError 2 :: MultiExcept Int Int)
diff --git a/test/Test/MultiExcept/Bifunctor.hs b/test/Test/MultiExcept/Bifunctor.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/MultiExcept/Bifunctor.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE CPP             #-}
+
+module Test.MultiExcept.Bifunctor
+  ( spec
+  ) where
+
+import Test.Hspec
+
+#if MIN_VERSION_base(4,8,0)
+
+import Data.Bifunctor
+import Control.Applicative.MultiExcept
+
+spec :: Spec
+spec = describe "Bifunctor instance" $ do
+  it "maps errors with first" $
+    first (+ 1) (throwError 3 *> throwError 4) `shouldBe` (throwError 4 *> throwError 5 :: MultiExcept Int ())
+  it "maps successes with second" $
+    second (+ 1) (succeed 3) `shouldBe` (succeed 4 :: MultiExcept Int Int)
+
+#else
+
+import Control.Applicative
+
+spec :: Spec
+spec = pure ()
+
+#endif
diff --git a/test/Test/MultiExcept/Foldable.hs b/test/Test/MultiExcept/Foldable.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/MultiExcept/Foldable.hs
@@ -0,0 +1,15 @@
+module Test.MultiExcept.Foldable
+  ( spec
+  ) where
+
+import Test.Hspec
+
+import qualified Data.Foldable as Foldable
+import Control.Applicative.MultiExcept
+
+spec :: Spec
+spec = describe "Foldable instance" $ do
+  it "f is called for success" $
+    Foldable.foldr (+) 2 (succeed 1) `shouldBe` (3 :: Int)
+  it "acc is returned for errors" $
+    Foldable.foldr (+) 2 (throwError (1 :: Int)) `shouldBe` (2 :: Int)
diff --git a/test/Test/MultiExcept/Functor.hs b/test/Test/MultiExcept/Functor.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/MultiExcept/Functor.hs
@@ -0,0 +1,20 @@
+module Test.MultiExcept.Functor
+  ( spec
+  ) where
+
+import Test.Hspec
+
+import Prelude (Int, ($), Num(..))
+import Data.Functor ((<$>), fmap)
+import Control.Applicative (Applicative(..))
+import Control.Applicative.MultiExcept
+
+testErrors :: MultiExcept Int Int
+testErrors = throwError 2 *> throwError 3
+
+spec :: Spec
+spec = describe "Functor instance" $ do
+  it "transforms success value" $
+    ((+ 3) <$> succeed 2) `shouldBe` (succeed 5 :: MultiExcept () Int)
+  it "doesn't affect errors" $ do
+    fmap (+ 3) testErrors `shouldBe` testErrors
diff --git a/test/Test/MultiExcept/Traversable.hs b/test/Test/MultiExcept/Traversable.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/MultiExcept/Traversable.hs
@@ -0,0 +1,18 @@
+module Test.MultiExcept.Traversable
+  ( spec
+  ) where
+
+import Prelude   (Int, Maybe(..), ($), const)
+
+import Data.Traversable
+import Test.Hspec
+import Control.Applicative.MultiExcept
+
+spec :: Spec
+spec = describe "Traversable instance" $ do
+  it "Just works™" $
+    traverse Just (succeed 3) `shouldBe` Just (succeed 3 :: MultiExcept () Int)
+  it "leaves errors" $
+    traverse Just (throwError 3) `shouldBe` Just (throwError 3 :: MultiExcept Int ())
+  it "Nothing works™" $
+    traverse (const Nothing :: a -> Maybe a) (succeed 3 :: MultiExcept () Int) `shouldBe` Nothing
diff --git a/test/Test/Traversable.hs b/test/Test/Traversable.hs
deleted file mode 100644
--- a/test/Test/Traversable.hs
+++ /dev/null
@@ -1,19 +0,0 @@
-{-# LANGUAGE OverloadedLists #-}
-{-# LANGUAGE TypeApplications #-}
-
-module Test.Traversable
-  ( spec
-  ) where
-
-import Test.Hspec
-
-import Control.Applicative.MultiExcept
-
-spec :: Spec
-spec = describe "Traversable instance" $ do
-  it "Just works™" $
-    traverse Just (succeed 3) `shouldBe` Just (succeed @() 3)
-  it "leaves errors" $
-    traverse Just (throwError 3) `shouldBe` Just (throwError @() 3)
-  it "Nothing works™" $
-    traverse (const Nothing :: a -> Maybe a) (succeed @() 3) `shouldBe` Nothing
