diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+### 0.9.0.9
+
+* Support GHC 9.10, GHC 9.12, and GHC 9.14
+* Remove noncanonical monad instance definitions for future GHC compatibility
+
 ### 0.9.0.8
 
 * Support GHC 9.8
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -11,54 +11,56 @@
 
 ## Example
 
-    {-# LANGUAGE LambdaCase, ScopedTypeVariables, TypeApplications #-}
-    import Control.Exception (throwIO)
-    import Control.Monad (when)
-    import Control.Monad.Trans.Class (lift)
-    import Control.Monad.Trans.Writer (execWriterT, tell)
-    import Data.Foldable (for_)
-    import Data.List (isPrefixOf)
-    import Data.Typeable (Typeable)
-    import qualified Language.Haskell.Interpreter as Hint
+```haskell
+{-# LANGUAGE LambdaCase, ScopedTypeVariables, TypeApplications #-}
+import Control.Exception (throwIO)
+import Control.Monad (when)
+import Control.Monad.Trans.Class (lift)
+import Control.Monad.Trans.Writer (execWriterT, tell)
+import Data.Foldable (for_)
+import Data.List (isPrefixOf)
+import Data.Typeable (Typeable)
+import qualified Language.Haskell.Interpreter as Hint
 
-    -- |
-    -- Interpret expressions into values:
-    --
-    -- >>> eval @[Int] "[1,2] ++ [3]"
-    -- Right [1,2,3]
-    --
-    -- Send values from your compiled program to your interpreted program by
-    -- interpreting a function:
-    --
-    -- >>> Right f <- eval @(Int -> [Int]) "\\x -> [1..x]"
-    -- >>> f 5
-    -- [1,2,3,4,5]
-    eval :: forall t. Typeable t
-         => String -> IO (Either Hint.InterpreterError t)
-    eval s = Hint.runInterpreter $ do
-      Hint.setImports ["Prelude"]
-      Hint.interpret s (Hint.as :: t)
+-- |
+-- Interpret expressions into values:
+--
+-- >>> eval @[Int] "[1,2] ++ [3]"
+-- Right [1,2,3]
+--
+-- Send values from your compiled program to your interpreted program by
+-- interpreting a function:
+--
+-- >>> Right f <- eval @(Int -> [Int]) "\\x -> [1..x]"
+-- >>> f 5
+-- [1,2,3,4,5]
+eval :: forall t. Typeable t
+     => String -> IO (Either Hint.InterpreterError t)
+eval s = Hint.runInterpreter $ do
+  Hint.setImports ["Prelude"]
+  Hint.interpret s (Hint.as :: t)
 
-    -- |
-    -- >>> :{
-    -- do Right contents <- browse "Prelude"
-    --    for_ contents $ \(identifier, tp) -> do
-    --      when ("put" `isPrefixOf` identifier) $ do
-    --        putStrLn $ identifier ++ " :: " ++ tp
-    -- :}
-    -- putChar :: Char -> IO ()
-    -- putStr :: String -> IO ()
-    -- putStrLn :: String -> IO ()
-    browse :: Hint.ModuleName -> IO (Either Hint.InterpreterError [(String, String)])
-    browse moduleName = Hint.runInterpreter $ do
-      Hint.setImports ["Prelude", "Data.Typeable", moduleName]
-      exports <- Hint.getModuleExports moduleName
-      execWriterT $ do
-        for_ exports $ \case
-          Hint.Fun identifier -> do
-            tp <- lift $ Hint.typeOf identifier
-            tell [(identifier, tp)]
-          _ -> pure ()  -- skip datatypes and typeclasses
+-- |
+-- >>> :{
+-- do Right contents <- browse "Prelude"
+--    for_ contents $ \(identifier, tp) -> do
+--      when ("put" `isPrefixOf` identifier) $ do
+--        putStrLn $ identifier ++ " :: " ++ tp
+-- :}
+-- putChar :: Char -> IO ()
+-- putStr :: String -> IO ()
+-- putStrLn :: String -> IO ()
+browse :: Hint.ModuleName -> IO (Either Hint.InterpreterError [(String, String)])
+browse moduleName = Hint.runInterpreter $ do
+  Hint.setImports ["Prelude", "Data.Typeable", moduleName]
+  exports <- Hint.getModuleExports moduleName
+  execWriterT $ do
+    for_ exports $ \case
+      Hint.Fun identifier -> do
+        tp <- lift $ Hint.typeOf identifier
+        tell [(identifier, tp)]
+      _ -> pure ()  -- skip datatypes and typeclasses
+```
 
 Check [example.hs](examples/example.hs) for a longer example (it must be run
 from hint's base directory).
diff --git a/hint.cabal b/hint.cabal
--- a/hint.cabal
+++ b/hint.cabal
@@ -1,5 +1,5 @@
 name:         hint
-version:      0.9.0.8
+version:      0.9.0.9
 description:
         This library defines an Interpreter monad. It allows to load Haskell
         modules, browse them, type-check and evaluate strings with Haskell
@@ -20,10 +20,13 @@
             , ghc == 8.6.5
             , ghc == 8.8.4
             , ghc == 9.0.2
-            , ghc == 9.2.7
-            , ghc == 9.4.5
-            , ghc == 9.6.1
-            , ghc == 9.8.1
+            , ghc == 9.2.8
+            , ghc == 9.4.8
+            , ghc == 9.6.6
+            , ghc == 9.8.4
+            , ghc == 9.10.3
+            , ghc == 9.12.3
+            , ghc == 9.14.1
 
 cabal-version: >= 1.10
 build-type:    Simple
@@ -72,7 +75,7 @@
   default-language: Haskell2010
   build-depends: base == 4.*,
                  containers,
-                 ghc >= 8.4 && < 9.9,
+                 ghc >= 8.4 && < 9.15,
                  ghc-paths,
                  ghc-boot,
                  transformers,
diff --git a/src/Control/Monad/Ghc.hs b/src/Control/Monad/Ghc.hs
--- a/src/Control/Monad/Ghc.hs
+++ b/src/Control/Monad/Ghc.hs
@@ -35,7 +35,7 @@
                  deriving (Functor, Monad, GHC.HasDynFlags)
 
 instance (Functor m, Monad m) => Applicative (GhcT m) where
-  pure  = return
+  pure  = GhcT . pure
   (<*>) = ap
 
 -- adapted from https://github.com/ghc/ghc/blob/ghc-8.2/compiler/main/GHC.hs#L450-L459
diff --git a/src/Hint/Context.hs b/src/Hint/Context.hs
--- a/src/Hint/Context.hs
+++ b/src/Hint/Context.hs
@@ -85,10 +85,11 @@
          m ([GHC.Module], [GHC.ImportDecl GHC.GhcPs])
     f (ns, ds) i = case i of
       (GHC.IIDecl d)     -> return (ns, d : ds)
-      (GHC.IIModule m) -> do n <- GHC.findModule m Nothing; return (n : ns, ds)
-
-modToIIMod :: GHC.Module -> GHC.InteractiveImport
-modToIIMod = GHC.IIModule . GHC.moduleName
+      _ -> do
+        mMod <- GHC.interactiveImportToModule i
+        case mMod of
+          Just n -> return (n : ns, ds)
+          Nothing -> return (ns, ds)
 
 getContextNames :: GHC.GhcMonad m => m([String], [String])
 getContextNames = fmap (map name *** map decl) getContext
@@ -97,7 +98,7 @@
 
 setContext :: GHC.GhcMonad m => [GHC.Module] -> [GHC.ImportDecl GHC.GhcPs] -> m ()
 setContext ms ds =
-  let ms' = map modToIIMod ms
+  let ms' = map GHC.moduleToInteractiveImport ms
       ds' = map GHC.IIDecl ds
       is = ms' ++ ds'
   in GHC.setContext is
diff --git a/src/Hint/GHC.hs b/src/Hint/GHC.hs
--- a/src/Hint/GHC.hs
+++ b/src/Hint/GHC.hs
@@ -28,6 +28,8 @@
     errMsgSpan,
     fileTarget,
     guessTarget,
+    moduleToInteractiveImport,
+    interactiveImportToModule,
 #if MIN_VERSION_ghc(9,6,0)
     getPrintUnqual,
 #endif
@@ -668,6 +670,22 @@
 guessTarget t pM = GHC.guessTarget t Nothing pM
 #else
 guessTarget = GHC.guessTarget
+#endif
+
+-- moduleToInteractiveImport / interactiveImportToModule
+-- In GHC 9.14+, IIModule takes a Module directly instead of ModuleName
+moduleToInteractiveImport :: Module -> InteractiveImport
+interactiveImportToModule :: InteractiveImport -> GhcMonad m => m (Maybe Module)
+#if MIN_VERSION_ghc(9,14,0)
+moduleToInteractiveImport = IIModule
+interactiveImportToModule (IIModule m) = return $ Just m
+interactiveImportToModule _ = return Nothing
+#else
+moduleToInteractiveImport = IIModule . moduleName
+interactiveImportToModule (IIModule mn) = do
+  m <- findModule mn Nothing
+  return $ Just m
+interactiveImportToModule _ = return Nothing
 #endif
 
 -- getPrintUnqual
diff --git a/src/Hint/InterpreterT.hs b/src/Hint/InterpreterT.hs
--- a/src/Hint/InterpreterT.hs
+++ b/src/Hint/InterpreterT.hs
@@ -196,5 +196,5 @@
     runGhc = runGhcImpl
 
 instance (Monad m) => Applicative (InterpreterT m) where
-    pure  = return
+    pure  = InterpreterT . pure
     (<*>) = ap
