Parsec: the parser combinator library that shaped Haskell text parsing
A monadic parser combinator library
At a glance
- What is it?
- Parsec is a monadic parser combinator library by Daan Leijen, designed from scratch as an industrial-strength parser with good error messages, and it is the library most Haskell developers meet first when they need to parse text. It ships on Hackage, it has no GitHub releases, and its own README points you to Megaparsec when you need more than it offers.
- Who is it for?
- Parsec fits a Haskell codebase that needs to parse a configuration format, a DSL or a text protocol and wants a library whose combinators read like the grammar they describe, and it fits a team learning parser combinators, because the API surface is small enough to hold in your head.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 136 days ago.
- What is it written in?
- Mainly Haskell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Combinators, not a grammar, and that is the whole design
Parsec is a monadic parser combinator library, and the word combinator carries the design.
A parser generator takes a grammar file and produces a parser. A combinator library gives you a small set of functions that each consume a bit of input and return either a result or a demand for more input, and you compose them into a parser in Haskell code. There is no grammar file, no separate language to learn, and no code generation step.
The README shows this at its smallest in a REPL session. The library is imported into scope, and a parser for balanced parentheses is defined with a recursive let binding:
Prelude> :m +Text.Parsec
Prelude Text.Parsec> let parenSet = char '(' >> many parenSet >> char ')' :: Parsec String () CharThe type annotation says what the parser consumes and what it produces: a Parsec over String input, using String input, with unit as the user state, and returning a Char. The combinators are visible in the definition. char matches one specific character, many applies a parser zero or more times, and the >> sequence discards the first result.
The second definition shows the alternative combinator, where <|> means try this or that:
Prelude Text.Parsec> let parens = (many parenSet >> eof) <|> eof
Prelude Text.Parsec> parse parens "" "()"
Right ()
Prelude Text.Parsec> parse parens "" "()(())"
Right ()
Prelude Text.Parsec> parse parens "" "("
Left (line 1, column 2):
unexpected end of input
expecting "(" or ")"The Right results indicate successes and the parentheses matched. The Left result indicates a parse failure, and the documentation says it is detailed with an error message.
That error output is the part that matters in practice. A line and a column, the unexpected thing, and what was expected instead. For a user editing a config file, that is the difference between a usable tool and one that says parse error at position 47.
The project is described as simple, safe, well documented, with extensive libraries, good error messages, and fast, and it was designed from scratch as an industrial-strength parser library rather than as an academic exercise.
The REPL is the tutorial, and the README knows it
The Getting started section of the README is unusually short and unusually good, and the reason is that the author decided the best documentation is a live session.
The setup requirement is a working version of cabal and ghci, which are part of any modern installation of Haskell, such as the Haskell Platform. Then the library is installed with a single command:
cabal install parsecAfter that the README does not describe an API in prose. It shows a parser being built and run in the interactive GHC environment, which was started with the ghci command. The session output is included, including the package loading lines, so the reader can see what a first run looks like.
That choice has consequences. A combinator library is not learnable from a table of function signatures, because the way the combinators compose is the point, and a live session shows that composition. It also means the README doubles as a smoke test: if the session in the README still works, the library still does what the documentation says.
The README is also explicit that it is not the reference. It asks readers to refer to the package description on Hackage for more information, and the Parsec link points at the Hackage page, which contains the generated documentation. For a thorough introduction it points at a set of external resources: the 2001 paper by Daan Leijen, which it notes is somewhat outdated and which is only reachable through archive.org and a Microsoft Research mirror, a chapter of Real World Haskell, a blog introduction by Kunigami and another by Wilson.
So the documentation strategy is layered. The README gives you a working hello world. The Hackage page gives you the reference. The paper gives you the theory, with the caveat that it has aged. The book chapter and the blog posts give you the walkthrough.
The README's most useful section is the list of alternatives
One thing this README does that few do is link directly to comparisons with every serious alternative, and the sources for those comparisons are named.
The differences between Parsec and Attoparsec, described as Haskell's other prominent parser library, are explained in an answer on StackExchange. The differences between Parsec and Happy, Haskell's parser generator, are explained in two answers on separate StackExchange questions. And the differences between Parsec and Megaparsec, described as an advanced fork of Parsec, are explained in Megaparsec's own README.
That last one matters most, because the fork's README is where the argument is made, and it is where the project itself points you when you want to know what Parsec does not do. Because it is a fork of Parsec, its README is the natural place to read how the two libraries differ, and the Parsec README sends you straight there.
Parsec's own error message in the README is good, but it is a single line and a column with an expectation list. It does not show the offending line of text, it does not show a caret, and it stops at the first problem. That is exactly the point at which a project like Megaparsec becomes the better answer, and the fact that the Parsec README links there is a candour that helps the reader more than any marketing would.
The alternative is not always Megaparsec, and the README names the other cases too. If you want a grammar in a separate file and the speed of generated code, Happy is the route, and Parsec is the wrong tool because it has no grammar concept at all. If you want a parser tuned for byte and text with a different error model, Attoparsec is the comparison to read.
Reverse dependencies show where Parsec actually ended up
There is a paragraph in the README that answers the question a reviewer usually asks, which is who depends on this thing.
The approach is indirect but honest. The README points at Parsec's reverse dependencies on Hackage, a service that lists the packages which depend on a given one, and uses it to find open source projects that make use of Parsec. The examples given are bibtex, ConfigFile, csv and hjson.
Those four are worth reading as a list, because they cover the range of what a combinator parser is good for. bibtex parses a bibliographic format. csv is a package for tabular data. ConfigFile parses configuration files, and it is a Haskell package with a capital C in its name, so it is a general purpose library rather than an application. hjson is a package for the Hjson format, a JSON variant.
What that list establishes is that Parsec handles real formats in real packages, not just tutorial grammars. It does not establish that Parsec is the right choice for you, and the README does not claim it does. It also does not tell you how many packages depend on Parsec, because the reverse dependency page does that and the README deliberately points at it rather than quoting a number.
The dependency story has a second half, though, and it is worth naming. A parser combinator library is easy to adopt and hard to remove, because a grammar written with combinators is code, and a code generator is not. If you later want to move to a generated parser, you are rewriting the grammar by hand. That asymmetry is one of the reasons the choice between Parsec, Megaparsec, Attoparsec and Happy is worth two minutes of reading before you start, and the README makes those two minutes easy.
A small library with a large footprint in the ecosystem
The repository listing is short, which tells you something about the shape of the project. There is a src directory, a test directory, a.gitignore, a.github directory, a ChangeLog.md, a LICENSE file, a README, a Setup.hs, a cabal.project, a cabal.haskell-ci file, and parsec.cabal.
The presence of cabal.haskell-ci is the interesting one. That file is the configuration for a tool that runs the test suite across a matrix of GHC versions and package environments, and having it checked in means the maintainer is running a broad build matrix rather than a single compiler. For a library that has been around for a long time and that other packages depend on, that is the difference between a library that keeps working across toolchain changes and one that quietly stops compiling on the new GHC.
Setup.hs alongside parsec.cabal shows the older Cabal style build description, which is what a library of this vintage carries, and it is also the style that keeps the package buildable with older Cabal versions.
The test directory is separate from src, so the tests do not ship in the package, which is what you want from a library whose parser combinators are small enough that the interesting part is the composition, not the size.
The other repository fact worth recording is the maintenance state. The last push to the repository was on 2026-06-09, the repository is not archived, and there are no GitHub releases. The absence of releases is normal for a Hackage library, where the version in the cabal file and the version on Hackage are the release, but it does mean the repository history and the ChangeLog.md are where you look for what changed.
The licence is one of the custom ones that GitHub cannot classify, with the terms in a LICENSE file in the repository, and the README simply says to see that file. That is a case where the answer has to come from the file rather than from a summary.
Where Parsec stops being the right answer
The limitations here are well known, and the useful thing is that the project itself points at the alternative.
The first is error reporting. Parsec tells you the line, the column, what it found and what it expected. For a machine readable format where the error must be machine readable, that is enough. For a configuration file that a human is editing, and for a language service that wants to underline the offending range and keep scanning, it is not. Megaparsec exists for that, and the README links the comparison.
The second is the absence of a grammar. Because Parsec is a combinator library, the grammar is embedded in Haskell code, which means there is no grammar file, no separate syntax to highlight, and no way to generate documentation of the accepted language. That is an advantage for a small bespoke format and a disadvantage for a large language with a formal grammar, where a generator such as Happy lets you keep the specification in one declarative place and regenerate the parser when the grammar changes.
The third is version churn for existing code. Parsec has been through a major version with a different API, and there is a large body of older Parsec 2 tutorials and blog posts still in circulation. Code that mixes a Parsec 2 example with a Parsec 3 install will not typecheck, and the error is a type error about a changed signature rather than a message about versions.
There is also a purely practical point. Parsec is a library, not an application. It has no CLI, no configuration, nothing to run. It is a component inside something else, which means the cost of adopting it is the cost of writing the parser yourself, with the library saving you the plumbing and the error handling, and nothing else.
Editorial conclusion
Parsec fits a Haskell codebase that needs to parse a configuration format, a DSL or a text protocol and wants a library whose combinators read like the grammar they describe, and it fits a team learning parser combinators, because the API surface is small enough to hold in your head. It is the wrong choice if you need precise error positions, context in the error message, or a parser that keeps going after an error, because the project itself directs you to Megaparsec for that, and it is the wrong choice if you need a grammar separate from the code. Before adopting it, read the package description on Hackage, which the README points to as the primary documentation, and check the ChangeLog.md in the repository, because the library's public API has changed across major versions and Parsec 3 differs from the Parsec 2 code many older tutorials still use.
Frequently asked questions
What is Parsec and who wrote it?
Parsec is a monadic parser combinator library written by Daan Leijen, designed from scratch as an industrial-strength parser library. The README describes it as simple, safe, well documented, with extensive libraries, good error messages, and fast, and it is published on Hackage.
How do I install Parsec and try it out?
The README says you need a working cabal and ghci, which come with any modern Haskell installation, then run cabal install parsec. It then shows the library being used interactively in a ghci session, building a parentheses parser and running it against sample input.
What is the difference between Parsec and Megaparsec?
The README describes Megaparsec as an advanced fork of Parsec and links to its own README for a detailed comparison. Parsec reports a line, a column, what it found and what it expected, and stops at the first error, while a fork of this kind adds richer error context and the ability to keep parsing after a failure.
Does Parsec use a separate grammar file?
No. Parsec is a combinator library, so a grammar is written directly in Haskell code using functions such as char, many, eof and the alternative operator, as the README's REPL example shows. If you want a declarative grammar kept separately from the code, the README points to Happy, Haskell's parser generator, as the comparison to read.
Which real projects use Parsec?
The README points at Parsec's reverse dependencies on Hackage as the way to find projects that use it, and names bibtex, ConfigFile, csv and hjson as examples of open source packages that make use of Parsec.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/haskell-parsec)Was this useful?