diff --git a/CHANGES.md b/CHANGES.md
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,8 @@
+0.12.0.0 (2020-12-31)
+=====================
+
+-   Port to Megaparsec 9.
+
 0.11.0.0 (2019-10-01)
 =====================
 
diff --git a/language-thrift.cabal b/language-thrift.cabal
--- a/language-thrift.cabal
+++ b/language-thrift.cabal
@@ -1,24 +1,24 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.31.2.
+-- This file has been generated from package.yaml by hpack version 0.34.2.
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 4e717e225f20a4779ba92b55f6212040c7138271b8a10c8e4e0d5a62351472a9
+-- hash: d80a1a764a9011be77762ba5390e28715af67b898b56b31e76b9845e55684958
 
 name:           language-thrift
-version:        0.11.0.0
-bug-reports:    https://github.com/abhinav/language-thrift/issues
-build-type:     Simple
-license:        BSD3
-license-file:   LICENSE
-maintainer:     Abhinav Gupta <mail@abhinavg.net>
-homepage:       https://github.com/abhinav/language-thrift#readme
+version:        0.12.0.0
 synopsis:       Parser and pretty printer for the Thrift IDL format.
 description:    This package provides a parser and pretty printer for the
                 <http://thrift.apache.org/docs/idl Thrift IDL format>.
 category:       Language
+homepage:       https://github.com/abhinav/language-thrift#readme
+bug-reports:    https://github.com/abhinav/language-thrift/issues
 author:         Abhinav Gupta
+maintainer:     Abhinav Gupta <mail@abhinavg.net>
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
 extra-source-files:
     CHANGES.md
     examples/generateHaskellTypes.hs
@@ -28,64 +28,65 @@
     test/data/docstring-2.txt
 
 source-repository head
-    type: git
-    location: https://github.com/abhinav/language-thrift
+  type: git
+  location: https://github.com/abhinav/language-thrift
 
 library
-    hs-source-dirs:
-        src
-    ghc-options: -Wall
-    build-depends:
-        ansi-wl-pprint >=0.6 && <0.7,
-        base >=4.7 && <5,
-        containers >=0.5 && <0.7,
-        megaparsec >=7.0 && <8.0,
-        scientific >=0.3 && <0.4,
-        semigroups >=0.18 && <0.19,
-        text >=1.2,
-        transformers
-    exposed-modules:
-        Language.Thrift.AST
-        Language.Thrift.Parser
-        Language.Thrift.Pretty
-    other-modules:
-        Language.Thrift.Internal.AST
-        Language.Thrift.Internal.Lens
-        Language.Thrift.Internal.Reserved
-        Paths_language_thrift
-    default-language: Haskell2010
+  exposed-modules:
+      Language.Thrift.AST
+      Language.Thrift.Parser
+      Language.Thrift.Pretty
+  other-modules:
+      Language.Thrift.Internal.AST
+      Language.Thrift.Internal.Lens
+      Language.Thrift.Internal.Reserved
+      Paths_language_thrift
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+      ansi-wl-pprint >=0.6 && <0.7
+    , base >=4.7 && <5
+    , containers >=0.5 && <0.7
+    , megaparsec >=9.0 && <10.0
+    , scientific >=0.3 && <0.4
+    , semigroups >=0.18 && <0.20
+    , text >=1.2
+    , transformers
+  default-language: Haskell2010
 
 test-suite spec
-    type: exitcode-stdio-1.0
-    main-is: Main.hs
-    hs-source-dirs:
-        src
-        test
-    ghc-options: -Wall
-    build-depends:
-        QuickCheck >=2.5,
-        ansi-wl-pprint >=0.6 && <0.7,
-        base >=4.7 && <5,
-        containers >=0.5 && <0.7,
-        hspec >=2.0,
-        hspec-discover >=2.1,
-        language-thrift,
-        megaparsec >=7.0 && <8.0,
-        scientific >=0.3 && <0.4,
-        semigroups >=0.18 && <0.19,
-        text >=1.2,
-        transformers
-    other-modules:
-        Language.Thrift.AST
-        Language.Thrift.Internal.AST
-        Language.Thrift.Internal.Lens
-        Language.Thrift.Internal.Reserved
-        Language.Thrift.Parser
-        Language.Thrift.Pretty
-        Language.Thrift.Arbitrary
-        Language.Thrift.ASTSpec
-        Language.Thrift.ParserSpec
-        Spec
-        TestUtils
-        Paths_language_thrift
-    default-language: Haskell2010
+  type: exitcode-stdio-1.0
+  main-is: Main.hs
+  hs-source-dirs:
+      src
+      test
+  ghc-options: -Wall
+  build-tool-depends:
+      hspec-discover:hspec-discover >=2.1
+  build-depends:
+      QuickCheck >=2.5
+    , ansi-wl-pprint >=0.6 && <0.7
+    , base >=4.7 && <5
+    , containers >=0.5 && <0.7
+    , hspec >=2.0
+    , language-thrift
+    , megaparsec >=9.0 && <10.0
+    , scientific >=0.3 && <0.4
+    , semigroups >=0.18 && <0.20
+    , text >=1.2
+    , transformers
+  other-modules:
+      Language.Thrift.AST
+      Language.Thrift.Internal.AST
+      Language.Thrift.Internal.Lens
+      Language.Thrift.Internal.Reserved
+      Language.Thrift.Parser
+      Language.Thrift.Pretty
+      Language.Thrift.Arbitrary
+      Language.Thrift.ASTSpec
+      Language.Thrift.ParserSpec
+      Spec
+      TestUtils
+      Paths_language_thrift
+  default-language: Haskell2010
diff --git a/src/Language/Thrift/Parser.hs b/src/Language/Thrift/Parser.hs
--- a/src/Language/Thrift/Parser.hs
+++ b/src/Language/Thrift/Parser.hs
@@ -107,14 +107,14 @@
 -- | @parse name contents@ parses the contents of a Thrift document with name
 -- @name@ held in @contents@.
 parse
-    :: (P.Stream s, P.Token s ~ Char)
+    :: (P.TraversableStream s, P.Token s ~ Char)
     => FilePath
     -> s -> Either (P.ParseErrorBundle s Void) (T.Program P.SourcePos)
 parse = P.parse thriftIDL
 
 -- | Megaparsec parser that is able to parse full Thrift documents.
 thriftIDL
-    :: (P.Stream s, P.Token s ~ Char)
+    :: (P.TraversableStream s, P.Token s ~ Char)
     => P.Parsec Void s (T.Program P.SourcePos)
 thriftIDL = runParser program
 
@@ -131,11 +131,11 @@
     return s
 
 -- | Optional whitespace.
-whiteSpace :: (P.Stream s, P.Token s ~ Char) => Parser s ()
+whiteSpace :: (P.TraversableStream s, P.Token s ~ Char) => Parser s ()
 whiteSpace = someSpace <|> pure ()
 
 -- | Required whitespace.
-someSpace :: (P.Stream s, P.Token s ~ Char) => Parser s ()
+someSpace :: (P.TraversableStream s, P.Token s ~ Char) => Parser s ()
 someSpace = P.skipSome $ readDocstring <|> skipComments <|> skipSpace
   where
     readDocstring = do
@@ -194,7 +194,7 @@
 -- This parses attempts to preserve indentation inside the docstring while
 -- getting rid of the aligned @*@s (if any) and any other preceding space.
 --
-docstring :: (P.Stream s, P.Token s ~ Char) => Parser s Text
+docstring :: (P.TraversableStream s, P.Token s ~ Char) => Parser s Text
 docstring = do
     P.try (string "/**") >> P.skipMany hspace
     indent <- fromIntegral . P.unPos <$> PL.indentLevel
@@ -242,20 +242,20 @@
             loop True maxDedent (line:chunks)
 
 
-symbolic :: forall s. (P.Stream s, P.Token s ~ Char) => Char -> Parser s ()
+symbolic :: forall s. (P.TraversableStream s, P.Token s ~ Char) => Char -> Parser s ()
 symbolic c = void $ PL.symbol whiteSpace (P.tokenToChunk (Proxy :: Proxy s) c)
 
-token :: (P.Stream s, P.Token s ~ Char) => Parser s a -> Parser s a
+token :: (P.TraversableStream s, P.Token s ~ Char) => Parser s a -> Parser s a
 token = PL.lexeme whiteSpace
 
 braces, angles, parens
-    :: (P.Stream s, P.Token s ~ Char) => Parser s a -> Parser s a
+    :: (P.TraversableStream s, P.Token s ~ Char) => Parser s a -> Parser s a
 
 braces = P.between (symbolic '{') (symbolic '}')
 angles = P.between (symbolic '<') (symbolic '>')
 parens = P.between (symbolic '(') (symbolic ')')
 
-comma, semi, colon, equals :: (P.Stream s, P.Token s ~ Char) => Parser s ()
+comma, semi, colon, equals :: (P.TraversableStream s, P.Token s ~ Char) => Parser s ()
 
 comma  = symbolic ','
 semi   = symbolic ';'
@@ -271,7 +271,7 @@
 
 -- | Parses a reserved identifier and adds it to the collection of known
 -- reserved keywords.
-reserved :: (P.Stream s, P.Token s ~ Char) => String -> Parser s ()
+reserved :: (P.TraversableStream s, P.Token s ~ Char) => String -> Parser s ()
 reserved name =
     errorUnlessReserved name >>
     P.label name $ token $ P.try $ do
@@ -280,7 +280,7 @@
 
 
 -- | A string literal. @"hello"@
-literal :: (P.Stream s, P.Token s ~ Char) => Parser s Text
+literal :: (P.TraversableStream s, P.Token s ~ Char) => Parser s Text
 literal = P.label "string literal" $ token $
     stringLiteral '"' <|> stringLiteral '\''
 
@@ -289,12 +289,12 @@
     PC.char q >> P.manyTill PL.charLiteral (PC.char q)
 
 
-integer :: (P.Stream s, P.Token s ~ Char) => Parser s Integer
+integer :: (P.TraversableStream s, P.Token s ~ Char) => Parser s Integer
 integer = token PL.decimal
 
 
 -- | An identifier in a Thrift file.
-identifier :: (P.Stream s, P.Token s ~ Char) => Parser s Text
+identifier :: (P.TraversableStream s, P.Token s ~ Char) => Parser s Text
 identifier = P.label "identifier" $ token $ do
     name <- (:)
         <$> (PC.letterChar <|> PC.char '_')
@@ -305,7 +305,7 @@
 
 
 -- | Top-level parser to parse complete Thrift documents.
-program :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Program P.SourcePos)
+program :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Program P.SourcePos)
 program = whiteSpace >>
     T.Program
         <$> many (header     <* optionalSep)
@@ -313,7 +313,7 @@
         <*  P.eof
 
 -- | Headers defined for the IDL.
-header :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Header P.SourcePos)
+header :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Header P.SourcePos)
 header = P.choice
   [ T.HeaderInclude   <$> include
   , T.HeaderNamespace <$> namespace
@@ -326,7 +326,7 @@
 -- >
 -- > typedef common.Foo Bar
 --
-include :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Include P.SourcePos)
+include :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Include P.SourcePos)
 include = reserved "include" >> withPosition (T.Include <$> literal)
 
 
@@ -335,7 +335,7 @@
 --
 -- > namespace py my_service.generated
 namespace
-    :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Namespace P.SourcePos)
+    :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Namespace P.SourcePos)
 namespace = P.choice
   [ reserved "namespace" >>
     withPosition (T.Namespace <$> (star <|> identifier) <*> identifier)
@@ -363,7 +363,7 @@
 -- | Convenience wrapper for parsers expecting a position.
 --
 -- The position will be retrieved BEFORE the parser itself is executed.
-withPosition :: P.Stream s => Parser s (P.SourcePos -> a) -> Parser s a
+withPosition :: P.TraversableStream s => Parser s (P.SourcePos -> a) -> Parser s a
 withPosition p = P.getSourcePos >>= \pos -> p <*> pure pos
 
 
@@ -372,7 +372,7 @@
 -- > data Foo = Foo { bar :: Bar, doc :: Docstring, pos :: Delta }
 -- >
 -- > parseFoo = withDocstring $ Foo <$> parseBar
-withDocstring :: P.Stream s => Parser s (T.Docstring -> P.SourcePos -> a) -> Parser s a
+withDocstring :: P.TraversableStream s => Parser s (T.Docstring -> P.SourcePos -> a) -> Parser s a
 withDocstring p = lastDocstring >>= \s -> do
     pos <- P.getSourcePos
     p <*> pure s <*> pure pos
@@ -380,7 +380,7 @@
 
 -- | A constant, type, or service definition.
 definition
-    :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Definition P.SourcePos)
+    :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Definition P.SourcePos)
 definition = whiteSpace >> P.choice
     [ T.ConstDefinition   <$> constant
     , T.TypeDefinition    <$> typeDefinition
@@ -390,7 +390,7 @@
 
 -- | A type definition.
 typeDefinition
-    :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Type P.SourcePos)
+    :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Type P.SourcePos)
 typeDefinition = P.choice
     [ T.TypedefType <$> typedef
     , T.EnumType    <$> enum
@@ -402,7 +402,7 @@
 -- | A typedef is just an alias for another type.
 --
 -- > typedef common.Foo Bar
-typedef :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Typedef P.SourcePos)
+typedef :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Typedef P.SourcePos)
 typedef = reserved "typedef" >> withDocstring
     (T.Typedef <$> typeReference <*> identifier <*> typeAnnotations)
 
@@ -412,7 +412,7 @@
 -- > enum Role {
 -- >     User = 1, Admin
 -- >
-enum :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Enum P.SourcePos)
+enum :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Enum P.SourcePos)
 enum = reserved "enum" >> withDocstring
     ( T.Enum
         <$> identifier
@@ -437,7 +437,7 @@
 -- >     1: optional string message
 -- >     2: required string username
 -- > }
-struct :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Struct P.SourcePos)
+struct :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Struct P.SourcePos)
 struct = kind >>= \k -> withDocstring
     ( T.Struct k
         <$> identifier
@@ -453,19 +453,19 @@
 
 
 -- | A @union@ of types.
-union :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Struct P.SourcePos)
+union :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Struct P.SourcePos)
 union = struct
 {-# DEPRECATED union "Use struct." #-}
 
 -- | An @exception@ that can be raised by service methods.
-exception :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Struct P.SourcePos)
+exception :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Struct P.SourcePos)
 exception = struct
 {-# DEPRECATED exception"Use struct." #-}
 
 
 -- | Whether a field is @required@ or @optional@.
 fieldRequiredness
-    :: (P.Stream s, P.Token s ~ Char) => Parser s T.FieldRequiredness
+    :: (P.TraversableStream s, P.Token s ~ Char) => Parser s T.FieldRequiredness
 fieldRequiredness = P.choice
   [ reserved "required" $> T.Required
   , reserved "optional" $> T.Optional
@@ -473,7 +473,7 @@
 
 
 -- | A struct field.
-field :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Field P.SourcePos)
+field :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Field P.SourcePos)
 field = withDocstring $
   T.Field
     <$> optional (integer <* colon)
@@ -486,7 +486,7 @@
 
 
 -- | A value defined inside an @enum@.
-enumDef :: (P.Stream s, P.Token s ~ Char) => Parser s (T.EnumDef P.SourcePos)
+enumDef :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.EnumDef P.SourcePos)
 enumDef = withDocstring $
   T.EnumDef
     <$> identifier
@@ -497,7 +497,7 @@
 
 -- | An string-only enum. These are a deprecated feature of Thrift and shouldn't
 -- be used.
-senum :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Senum P.SourcePos)
+senum :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Senum P.SourcePos)
 senum = reserved "senum" >> withDocstring
     ( T.Senum
         <$> identifier
@@ -509,7 +509,7 @@
 -- | A 'const' definition.
 --
 -- > const i32 code = 1;
-constant :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Const P.SourcePos)
+constant :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Const P.SourcePos)
 constant = do
   reserved "const"
   withDocstring $
@@ -522,7 +522,7 @@
 
 -- | A constant value literal.
 constantValue
-    :: (P.Stream s, P.Token s ~ Char) => Parser s (T.ConstValue P.SourcePos)
+    :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.ConstValue P.SourcePos)
 constantValue = withPosition $ P.choice
   [ P.try (string "0x") >> T.ConstInt <$> token PL.hexadecimal
   , either T.ConstFloat T.ConstInt
@@ -537,7 +537,7 @@
 
 
 constList
-    :: (P.Stream s, P.Token s ~ Char) => Parser s [T.ConstValue P.SourcePos]
+    :: (P.TraversableStream s, P.Token s ~ Char) => Parser s [T.ConstValue P.SourcePos]
 constList = symbolic '[' *> loop []
   where
     loop xs = P.choice
@@ -549,7 +549,7 @@
 
 
 constMap
-    :: (P.Stream s, P.Token s ~ Char)
+    :: (P.TraversableStream s, P.Token s ~ Char)
     => Parser s [(T.ConstValue P.SourcePos, T.ConstValue P.SourcePos)]
 constMap = symbolic '{' *> loop []
   where
@@ -562,7 +562,7 @@
 
 
 constantValuePair
-    :: (P.Stream s, P.Token s ~ Char)
+    :: (P.TraversableStream s, P.Token s ~ Char)
     => Parser s (T.ConstValue P.SourcePos, T.ConstValue P.SourcePos)
 constantValuePair =
     (,) <$> (constantValue <* colon)
@@ -571,7 +571,7 @@
 
 -- | A reference to a built-in or defined field.
 typeReference
-    :: (P.Stream s, P.Token s ~ Char)
+    :: (P.TraversableStream s, P.Token s ~ Char)
     => Parser s (T.TypeReference P.SourcePos)
 typeReference = P.choice
   [ baseType
@@ -581,7 +581,7 @@
 
 
 baseType
-    :: (P.Stream s, P.Token s ~ Char)
+    :: (P.TraversableStream s, P.Token s ~ Char)
     => Parser s (T.TypeReference P.SourcePos)
 baseType = withPosition $
     P.choice [reserved s *> (v <$> typeAnnotations) | (s, v) <- bases]
@@ -601,7 +601,7 @@
 
 
 containerType
-    :: (P.Stream s, P.Token s ~ Char)
+    :: (P.TraversableStream s, P.Token s ~ Char)
     => Parser s (T.TypeReference P.SourcePos)
 containerType = withPosition $
     P.choice [mapType, setType, listType] <*> typeAnnotations
@@ -617,7 +617,7 @@
 -- > service MyService {
 -- >     // ...
 -- > }
-service :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Service P.SourcePos)
+service :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Service P.SourcePos)
 service = do
   reserved "service"
   withDocstring $
@@ -633,7 +633,7 @@
 -- > Foo getFoo() throws (1: FooDoesNotExist doesNotExist);
 -- > oneway void putBar(1: Bar bar);
 function
-    :: (P.Stream s, P.Token s ~ Char) => Parser s (T.Function P.SourcePos)
+    :: (P.TraversableStream s, P.Token s ~ Char) => Parser s (T.Function P.SourcePos)
 function = withDocstring $
     T.Function
         <$> ((reserved "oneway" $> True) <|> pure False)
@@ -652,18 +652,18 @@
 -- These do not usually affect code generation but allow for custom logic if
 -- writing your own code generator.
 typeAnnotations
-    :: (P.Stream s, P.Token s ~ Char) => Parser s [T.TypeAnnotation]
+    :: (P.TraversableStream s, P.Token s ~ Char) => Parser s [T.TypeAnnotation]
 typeAnnotations = parens (many typeAnnotation) <|> pure []
 
 
-typeAnnotation :: (P.Stream s, P.Token s ~ Char) => Parser s T.TypeAnnotation
+typeAnnotation :: (P.TraversableStream s, P.Token s ~ Char) => Parser s T.TypeAnnotation
 typeAnnotation =
     T.TypeAnnotation
         <$> identifier
         <*> (optional (equals *> literal) <* optionalSep)
 
 
-optionalSep :: (P.Stream s, P.Token s ~ Char) => Parser s ()
+optionalSep :: (P.TraversableStream s, P.Token s ~ Char) => Parser s ()
 optionalSep = void $ optional (comma <|> semi)
 
 string :: forall s. (P.Stream s, P.Token s ~ Char) => String -> Parser s (P.Tokens s)
