exhaustive is a library that guarantees that when building a parser, or some other computation that produces data, all possible constructors in a data type are considered. You can think of this library as providing a symmetry to GHC's built in -fwarn-incomplete-patterns compile time warning, although this library is stricter in that it produces compile time errors if a constructor is omitted.

Usage of this library is intended to be straightforward, though admittedly the types might have you think the opposite! To understand this library, an example may be helpful.

To begin with, consider a simple data type for a "boolean expressions" language:

import qualified GHC.Generics as GHC data Expr = ETrue | EFalse | EIf Expr Expr Expr deriving ( Eq , GHC. Generic ) instance Generic Expr

Note that we have to make our data type an instance of both GHC.Generics. Generic and Generics.SOP. Generic , though this only requires boiler-plate code.

Next, we would like to build a parser for this language. Let's assume that we have access to a parsec -like library, where we have one basic combinator:

symbol :: String -> Parser String

Ordinarily, we would write our parser as

parseExpr :: Parser Expr parseExpr = msum [ETrue <$ symbol "True" ,EFalse <$ symbol "False" ,EIf <$> symbol "if" *> parseExpr <*> symbol "then" *> parseExpr <*> symbol "else" *> parseExpr ]

However, nothing is making sure that we actually considered all constructors in Expr . We could just as well write

parseExpr :: Parser Expr parseExpr = msum [ETrue <$ symbol "True" ,EFalse <$ symbol "False"]

Although this is significantly less useful!

Using exhaustive , we can get exhaustivity checks that we are at least considering all constructors:

parseExpr :: Parser Expr parseExpr = produceFirst $ construct (\f -> f <$ symbol "True") :* construct (\f -> f <$ symbol "False") :* construct (\f -> f <$ > symbol "if" *> parseExpr <*> symbol "then" *> parseExpr <*> symbol "else" *> parseExpr) :* Nil

As you can hopefully see, exhaustive requires only minimal changes to an existing parser. Specifically, we need to: