diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
--- a/CHANGELOG.markdown
+++ b/CHANGELOG.markdown
@@ -1,3 +1,7 @@
+0.10.9 [2024.10.31]
+-------------------
+* Drop support for pre-8.0 versions of GHC.
+
 0.10.8 [2024.04.20]
 -------------------
 * Allow building with `template-haskell-2.22.*`.
diff --git a/exceptions.cabal b/exceptions.cabal
--- a/exceptions.cabal
+++ b/exceptions.cabal
@@ -1,6 +1,6 @@
 name:          exceptions
 category:      Control, Exceptions, Monad
-version:       0.10.8
+version:       0.10.9
 cabal-version: >= 1.10
 license:       BSD3
 license-file:  LICENSE
@@ -12,11 +12,7 @@
 copyright:     Copyright (C) 2013-2015 Edward A. Kmett
                Copyright (C) 2012 Google Inc.
 build-type:    Simple
-tested-with:   GHC == 7.4.2
-             , GHC == 7.6.3
-             , GHC == 7.8.4
-             , GHC == 7.10.3
-             , GHC == 8.0.2
+tested-with:   GHC == 8.0.2
              , GHC == 8.2.2
              , GHC == 8.4.4
              , GHC == 8.6.5
@@ -25,8 +21,9 @@
              , GHC == 9.0.2
              , GHC == 9.2.8
              , GHC == 9.4.8
-             , GHC == 9.6.4
-             , GHC == 9.8.1
+             , GHC == 9.6.6
+             , GHC == 9.8.2
+             , GHC == 9.10.1
 synopsis:      Extensible optionally-pure exceptions
 description:   Extensible optionally-pure exceptions.
 
@@ -42,33 +39,19 @@
   type: git
   location: https://github.com/ekmett/exceptions.git
 
-flag transformers-0-4
-  description: Use @transformers-0.4@ or later.
-  default:     True
-
 library
   build-depends:
-    base                       >= 4.5      && < 5,
+    base                       >= 4.9      && < 5,
+    mtl                        >= 2.2      && < 2.4,
     stm                        >= 2.2      && < 3,
-    template-haskell           >= 2.7      && < 2.23,
-    mtl                        >= 2.0      && < 2.4
-
-  if !impl(ghc >= 8.0)
-    build-depends: fail        == 4.9.*
-
-  if flag(transformers-0-4)
-    build-depends:
-      transformers             >= 0.4      && < 0.7
-  else
-    build-depends:
-      transformers             >= 0.2      && < 0.4,
-      transformers-compat      >= 0.3      && < 0.8
+    template-haskell           >= 2.11     && < 2.23,
+    transformers               >= 0.5.2.0  && < 0.7
 
   exposed-modules:
     Control.Monad.Catch
     Control.Monad.Catch.Pure
 
-  ghc-options: -Wall -fwarn-tabs -O2
+  ghc-options: -Wall -Wtabs -O2
   hs-source-dirs: src
   default-language: Haskell2010
 
@@ -76,24 +59,17 @@
   main-is: Tests.hs
   other-modules: Control.Monad.Catch.Tests
   hs-source-dirs: tests
-  ghc-options: -Wall -fwarn-tabs
+  ghc-options: -Wall -Wtabs
   default-language: Haskell2010
   type: exitcode-stdio-1.0
   build-depends:
     base,
     exceptions,
+    mtl,
     stm,
     template-haskell,
-    mtl                        >= 2.0,
+    transformers               >= 0.5.2.0  && < 0.7,
     test-framework             >= 0.8      && < 0.9,
     test-framework-hunit       >= 0.3      && < 0.4,
     test-framework-quickcheck2 >= 0.3      && < 0.4,
-    QuickCheck                 >= 2.5      && < 2.15
-
-  if flag(transformers-0-4)
-    build-depends:
-      transformers             >= 0.4      && < 0.7
-  else
-    build-depends:
-      transformers             >= 0.2      && < 0.4,
-      transformers-compat      >= 0.3      && < 0.8
+    QuickCheck                 >= 2.5      && < 2.16
diff --git a/src/Control/Monad/Catch.hs b/src/Control/Monad/Catch.hs
--- a/src/Control/Monad/Catch.hs
+++ b/src/Control/Monad/Catch.hs
@@ -7,17 +7,10 @@
 {-# LANGUAGE UndecidableInstances #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE TypeOperators #-}
-
-#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 702
 {-# LANGUAGE Trustworthy #-}
-#endif
 
-#ifndef MIN_VERSION_transformers
-#define MIN_VERSION_transformers(x,y,z) 1
-#endif
-
 #if !(MIN_VERSION_transformers(0,6,0))
-{-# OPTIONS_GHC -fno-warn-deprecations #-}
+{-# OPTIONS_GHC -Wno-deprecations #-}
 #endif
 --------------------------------------------------------------------
 -- |
@@ -35,7 +28,7 @@
 -- with them, though doesn't mimic the module structure or offer the complete
 -- range of features in those packages.
 --
--- This is very similar to 'ErrorT' and 'MonadError', but based on features of
+-- This is very similar to 'ExceptT' and 'MonadError', but based on features of
 -- "Control.Exception". In particular, it handles the complex case of
 -- asynchronous exceptions by including 'mask' in the typeclass. Note that the
 -- extensible exceptions feature relies on the RankNTypes language extension.
@@ -87,6 +80,7 @@
 import qualified Control.Monad.Trans.Writer.Lazy as LazyW
 import qualified Control.Monad.Trans.Writer.Strict as StrictW
 import Control.Monad.ST (ST)
+import Control.Monad.ST.Unsafe (unsafeIOToST)
 import Control.Monad.STM (STM)
 import Control.Monad.Trans.Class (MonadTrans(..))
 import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)
@@ -95,68 +89,15 @@
 import Control.Monad.Trans.Identity
 import Control.Monad.Trans.Reader (ReaderT(..), runReaderT)
 
-import Language.Haskell.TH.Syntax (Q)
-
-#if MIN_VERSION_base(4,4,0)
-import Control.Monad.ST.Unsafe (unsafeIOToST)
-#else
-import Control.Monad.ST (unsafeIOToST)
-#endif
-
-#if __GLASGOW_HASKELL__ < 706
-import Prelude hiding (catch, foldr)
-import Data.Foldable
-import Data.Monoid
-#elif __GLASGOW_HASKELL__ < 710
-import Prelude hiding (foldr)
-import Data.Foldable
-import Data.Monoid
-#endif
-
-#if __GLASGOW_HASKELL__ < 710
-import Control.Applicative
-#endif
-
-#if __GLASGOW_HASKELL__ >= 800
 import GHC.Stack (HasCallStack, withFrozenCallStack)
-#endif
 
+import Language.Haskell.TH.Syntax (Q)
+
 #if !(MIN_VERSION_transformers(0,6,0))
 import Control.Monad.Trans.Error (ErrorT(..), Error, runErrorT)
 import Control.Monad.Trans.List (ListT(..), runListT)
 #endif
 
--- We use the following bit of CPP to enable the use of HasCallStack
--- constraints without breaking the build for pre-8.0 GHCs, which did not
--- provide GHC.Stack. We are careful to always write constraints like this:
---
---   HAS_CALL_STACK => MonadThrow m => ...
---
--- Instead of like this:
---
---   (HAS_CALL_STACK, MonadThrow e) => ...
---
--- The latter is equivalent to (() :: Constraint, MonadThrow e) => ..., which
--- requires ConstraintKinds. More importantly, it's slightly less efficient,
--- since it requires passing an empty constraint tuple dictionary around.
---
--- Note that we do /not/ depend on the call-stack compatibility library to
--- provide HasCallStack on older GHCs. We tried this at one point, but we
--- discovered that downstream libraries failed to build because combining
--- call-stack with GeneralizedNewtypeDeriving on older GHCs would require the
--- use of ConstraintKinds/FlexibleContexts, which downstream libraries did not
--- enable. (See #91.) The CPP approach that we use now, while somewhat clunky,
--- avoids these issues by not requiring any additional language extensions for
--- downstream users.
-#if __GLASGOW_HASKELL__ >= 800
-# define HAS_CALL_STACK HasCallStack
-#else
-# define HAS_CALL_STACK ()
-
-withFrozenCallStack :: a -> a
-withFrozenCallStack a = a
-#endif
-
 ------------------------------------------------------------------------------
 -- $mtl
 -- The mtl style typeclass
@@ -178,7 +119,7 @@
   -- Should satisfy the law:
   --
   -- > throwM e >> f = throwM e
-  throwM :: HAS_CALL_STACK => Exception e => e -> m a
+  throwM :: (HasCallStack, Exception e) => e -> m a
 
 -- | A class for monads which allow exceptions to be caught, in particular
 -- exceptions which were thrown by 'throwM'.
@@ -198,7 +139,7 @@
   -- action. Note that type of the type of the argument to the handler will
   -- constrain which exceptions are caught. See "Control.Exception"'s
   -- 'ControlException.catch'.
-  catch :: HAS_CALL_STACK => Exception e => m a -> (e -> m a) -> m a
+  catch :: (HasCallStack, Exception e) => m a -> (e -> m a) -> m a
 
 -- | A class for monads which provide for the ability to account for
 -- all possible exit points from a computation, and to mask
@@ -239,7 +180,7 @@
   -- | Runs an action with asynchronous exceptions disabled. The action is
   -- provided a method for restoring the async. environment to what it was
   -- at the 'mask' call. See "Control.Exception"'s 'ControlException.mask'.
-  mask :: HAS_CALL_STACK => ((forall a. m a -> m a) -> m b) -> m b
+  mask :: HasCallStack => ((forall a. m a -> m a) -> m b) -> m b
 
   -- | Like 'mask', but the masked computation is not interruptible (see
   -- "Control.Exception"'s 'ControlException.uninterruptibleMask'. WARNING:
@@ -247,7 +188,7 @@
   -- AND you can guarantee the interruptible operation will only block for a
   -- short period of time. Otherwise you render the program/thread unresponsive
   -- and/or unkillable.
-  uninterruptibleMask :: HAS_CALL_STACK => ((forall a. m a -> m a) -> m b) -> m b
+  uninterruptibleMask :: HasCallStack => ((forall a. m a -> m a) -> m b) -> m b
 
   -- | A generalized version of 'bracket' which uses 'ExitCase' to distinguish
   -- the different exit cases, and returns the values of both the 'use' and
@@ -336,7 +277,7 @@
   --
   -- @since 0.9.0
   generalBracket
-    :: HAS_CALL_STACK
+    :: HasCallStack
     => m a
     -- ^ acquire some resource
     -> (a -> ExitCase b -> m c)
@@ -765,12 +706,12 @@
 ------------------------------------------------------------------------------
 
 -- | Like 'mask', but does not pass a @restore@ action to the argument.
-mask_ :: HAS_CALL_STACK => MonadMask m => m a -> m a
+mask_ :: (HasCallStack, MonadMask m) => m a -> m a
 mask_ io = withFrozenCallStack (\f -> mask (\x -> f x)) (\_ -> io)
 
 -- | Like 'uninterruptibleMask', but does not pass a @restore@ action to the
 -- argument.
-uninterruptibleMask_ :: HAS_CALL_STACK => MonadMask m => m a -> m a
+uninterruptibleMask_ :: (HasCallStack, MonadMask m) => m a -> m a
 uninterruptibleMask_ io = withFrozenCallStack (\f -> uninterruptibleMask (\x -> f x)) (\_ -> io)
 
 -- | Catches all exceptions, and somewhat defeats the purpose of the extensible
@@ -778,57 +719,57 @@
 --
 -- /NOTE/ This catches all /exceptions/, but if the monad supports other ways of
 -- aborting the computation, those other kinds of errors will not be caught.
-catchAll :: HAS_CALL_STACK => MonadCatch m => m a -> (SomeException -> m a) -> m a
+catchAll :: (HasCallStack, MonadCatch m) => m a -> (SomeException -> m a) -> m a
 catchAll = withFrozenCallStack catch
 
 -- | Catch all 'IOError' (eqv. 'IOException') exceptions. Still somewhat too
 -- general, but better than using 'catchAll'. See 'catchIf' for an easy way
 -- of catching specific 'IOError's based on the predicates in "System.IO.Error".
-catchIOError :: HAS_CALL_STACK => MonadCatch m => m a -> (IOError -> m a) -> m a
+catchIOError :: (HasCallStack, MonadCatch m) => m a -> (IOError -> m a) -> m a
 catchIOError = withFrozenCallStack catch
 
 -- | Catch exceptions only if they pass some predicate. Often useful with the
 -- predicates for testing 'IOError' values in "System.IO.Error".
-catchIf :: HAS_CALL_STACK => (MonadCatch m, Exception e) =>
+catchIf :: (HasCallStack, MonadCatch m, Exception e) =>
     (e -> Bool) -> m a -> (e -> m a) -> m a
 catchIf f a b = withFrozenCallStack catch a (\e -> if f e then b e else throwM e)
 
 -- | A more generalized way of determining which exceptions to catch at
 -- run time.
-catchJust :: HAS_CALL_STACK => (MonadCatch m, Exception e) =>
+catchJust :: (HasCallStack, MonadCatch m, Exception e) =>
     (e -> Maybe b) -> m a -> (b -> m a) -> m a
 catchJust f a b = withFrozenCallStack catch a (\e -> maybe (throwM e) b $ f e)
 
 -- | Flipped 'catch'. See "Control.Exception"'s 'ControlException.handle'.
-handle :: HAS_CALL_STACK => (MonadCatch m, Exception e) => (e -> m a) -> m a -> m a
+handle :: (HasCallStack, MonadCatch m, Exception e) => (e -> m a) -> m a -> m a
 handle = flip (withFrozenCallStack catch)
 {-# INLINE handle #-}
 
 -- | Flipped 'catchIOError'
-handleIOError :: HAS_CALL_STACK => MonadCatch m => (IOError -> m a) -> m a -> m a
+handleIOError :: (HasCallStack, MonadCatch m) => (IOError -> m a) -> m a -> m a
 handleIOError = withFrozenCallStack handle
 
 -- | Flipped 'catchAll'
-handleAll :: HAS_CALL_STACK => MonadCatch m => (SomeException -> m a) -> m a -> m a
+handleAll :: (HasCallStack, MonadCatch m) => (SomeException -> m a) -> m a -> m a
 handleAll = withFrozenCallStack handle
 
 -- | Flipped 'catchIf'
-handleIf :: HAS_CALL_STACK => (MonadCatch m, Exception e) => (e -> Bool) -> (e -> m a) -> m a -> m a
+handleIf :: (HasCallStack, MonadCatch m, Exception e) => (e -> Bool) -> (e -> m a) -> m a -> m a
 handleIf f = flip (withFrozenCallStack catchIf f)
 
 -- | Flipped 'catchJust'. See "Control.Exception"'s 'ControlException.handleJust'.
-handleJust :: HAS_CALL_STACK => (MonadCatch m, Exception e) => (e -> Maybe b) -> (b -> m a) -> m a -> m a
+handleJust :: (HasCallStack, MonadCatch m, Exception e) => (e -> Maybe b) -> (b -> m a) -> m a -> m a
 handleJust f = flip (withFrozenCallStack catchJust f)
 {-# INLINE handleJust #-}
 
 -- | Similar to 'catch', but returns an 'Either' result. See "Control.Exception"'s
 -- 'Control.Exception.try'.
-try :: HAS_CALL_STACK => (MonadCatch m, Exception e) => m a -> m (Either e a)
+try :: (HasCallStack, MonadCatch m, Exception e) => m a -> m (Either e a)
 try a = withFrozenCallStack catch (Right `liftM` a) (return . Left)
 
 -- | A variant of 'try' that takes an exception predicate to select
 -- which exceptions are caught. See "Control.Exception"'s 'ControlException.tryJust'
-tryJust :: HAS_CALL_STACK => (MonadCatch m, Exception e) =>
+tryJust :: (HasCallStack, MonadCatch m, Exception e) =>
     (e -> Maybe b) -> m a -> m (Either b a)
 tryJust f a = withFrozenCallStack catch (Right `liftM` a) (\e -> maybe (throwM e) (return . Left) (f e))
 
@@ -839,7 +780,7 @@
   fmap f (Handler h) = Handler (liftM f . h)
 
 -- | Catches different sorts of exceptions. See "Control.Exception"'s 'ControlException.catches'
-catches :: HAS_CALL_STACK => (Foldable f, MonadCatch m) => m a -> f (Handler m a) -> m a
+catches :: (HasCallStack, Foldable f, MonadCatch m) => m a -> f (Handler m a) -> m a
 catches a hs = withFrozenCallStack catch a handler
   where
     handler e = foldr probe (throwM e) hs
@@ -852,7 +793,7 @@
 -- /NOTE/ The action is only run if an /exception/ is thrown. If the monad
 -- supports other ways of aborting the computation, the action won't run if
 -- those other kinds of errors are thrown. See 'onError'.
-onException :: HAS_CALL_STACK => MonadCatch m => m a -> m b -> m a
+onException :: (HasCallStack, MonadCatch m) => m a -> m b -> m a
 onException action handler = withFrozenCallStack catchAll action (\e -> handler >> throwM e)
 
 -- | Run an action only if an error is thrown in the main action. Unlike
@@ -866,7 +807,7 @@
 -- except that 'onError' has a more constrained type.
 --
 -- @since 0.10.0
-onError :: HAS_CALL_STACK => MonadMask m => m a -> m b -> m a
+onError :: (HasCallStack, MonadMask m) => m a -> m b -> m a
 onError action handler = withFrozenCallStack bracketOnError (return ()) (const handler) (const action)
 
 -- | Generalized abstracted pattern of safe resource acquisition and release
@@ -882,24 +823,24 @@
 -- signature from "Control.Exception"), and is often more convenient to use. By
 -- contrast, 'generalBracket' is more expressive, allowing us to implement
 -- other functions like 'bracketOnError'.
-bracket :: HAS_CALL_STACK => MonadMask m => m a -> (a -> m c) -> (a -> m b) -> m b
+bracket :: (HasCallStack, MonadMask m) => m a -> (a -> m c) -> (a -> m b) -> m b
 bracket acquire release = liftM fst . withFrozenCallStack generalBracket
   acquire
   (\a _exitCase -> release a)
 
 -- | Version of 'bracket' without any value being passed to the second and
 -- third actions.
-bracket_ :: HAS_CALL_STACK => MonadMask m => m a -> m c -> m b -> m b
+bracket_ :: (HasCallStack, MonadMask m) => m a -> m c -> m b -> m b
 bracket_ before after action = withFrozenCallStack bracket before (const after) (const action)
 
 -- | Perform an action with a finalizer action that is run, even if an
 -- error occurs.
-finally :: HAS_CALL_STACK => MonadMask m => m a -> m b -> m a
+finally :: (HasCallStack, MonadMask m) => m a -> m b -> m a
 finally action finalizer = withFrozenCallStack bracket_ (return ()) finalizer action
 
 -- | Like 'bracket', but only performs the final action if an error is
 -- thrown by the in-between computation.
-bracketOnError :: HAS_CALL_STACK => MonadMask m => m a -> (a -> m c) -> (a -> m b) -> m b
+bracketOnError :: (HasCallStack, MonadMask m) => m a -> (a -> m c) -> (a -> m b) -> m b
 bracketOnError acquire release = liftM fst . withFrozenCallStack generalBracket
   acquire
   (\a exitCase -> case exitCase of
diff --git a/src/Control/Monad/Catch/Pure.hs b/src/Control/Monad/Catch/Pure.hs
--- a/src/Control/Monad/Catch/Pure.hs
+++ b/src/Control/Monad/Catch/Pure.hs
@@ -5,19 +5,8 @@
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE UndecidableInstances #-}
-
-#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 702
 {-# LANGUAGE Trustworthy #-}
-#endif
 
-#ifndef MIN_VERSION_transformers
-#define MIN_VERSION_transformers(x,y,z) 1
-#endif
-
-#ifndef MIN_VERSION_mtl
-#define MIN_VERSION_mtl(x,y,z) 1
-#endif
-
 --------------------------------------------------------------------
 -- |
 -- Copyright   :  (C) Edward Kmett 2013-2015, (c) Google Inc. 2012
@@ -46,11 +35,7 @@
   , module Control.Monad.Catch
   ) where
 
-#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ >= 706)
 import Prelude hiding (foldr)
-#else
-import Prelude hiding (catch, foldr)
-#endif
 
 import Control.Applicative
 import Control.Monad.Catch
@@ -63,10 +48,6 @@
 import Control.Monad.State (MonadState(..))
 import Control.Monad.Trans.Class (MonadTrans(..))
 import Control.Monad.Writer (MonadWriter(..))
-#if __GLASGOW_HASKELL__ < 710
-import Data.Foldable
-import Data.Monoid (Monoid(..))
-#endif
 import Data.Functor.Identity
 import Data.Traversable as Traversable
 
@@ -192,9 +173,7 @@
 instance MonadState s m => MonadState s (CatchT m) where
   get = lift get
   put = lift . put
-#if MIN_VERSION_mtl(2,1,0)
   state = lift . state
-#endif
 
 instance MonadReader e m => MonadReader e (CatchT m) where
   ask = lift ask
@@ -210,9 +189,7 @@
     return $! case a of
         Left  l      -> (Left  l, id)
         Right (r, f) -> (Right r, f)
-#if MIN_VERSION_mtl(2,1,0)
   writer aw = CatchT (Right `liftM` writer aw)
-#endif
 
 instance MonadRWS r w s m => MonadRWS r w s (CatchT m)
 
diff --git a/tests/Control/Monad/Catch/Tests.hs b/tests/Control/Monad/Catch/Tests.hs
--- a/tests/Control/Monad/Catch/Tests.hs
+++ b/tests/Control/Monad/Catch/Tests.hs
@@ -5,20 +5,13 @@
 {-# LANGUAGE CPP #-}
 
 #if !(MIN_VERSION_transformers(0,6,0))
-{-# OPTIONS_GHC -fno-warn-deprecations #-}
+{-# OPTIONS_GHC -Wno-deprecations #-}
 #endif
 
 module Control.Monad.Catch.Tests (tests) where
 
-#if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ < 706)
-import Prelude hiding (catch)
-#endif
-
-#if !(MIN_VERSION_base(4,8,0))
-import Control.Applicative ((<*>))
-#endif
 import Control.Monad (unless)
-import Data.Data (Data, Typeable)
+import Data.Data (Data)
 import Data.IORef (newIORef, writeIORef, readIORef)
 
 import Control.Monad.Trans.Class (lift)
@@ -48,7 +41,7 @@
 import Control.Monad.Catch.Pure
 
 data TestException = TestException String
-    deriving (Show, Eq, Data, Typeable)
+    deriving (Show, Eq, Data)
 
 instance Exception TestException
 
