For an ADT type, such as,
data Exp = Num Int | App Char Exp
deriving (Show)
In Haskell we have case expressions to deconstruct values, and they have exhaustiveness checking. We have
case e of
Num i -> ..
and then GHC can warn us that we forgot the App
case. Well what about the opposite, to have exhaustiveness checking when we're constructing values of type Exp
?
This gist demonstrates a simple syntactic extension to achieve this. Here it is. We can write out a way to construct the type, case-by-case:
expr = $(onto [|
do [Num i | i <- read . (: []) <$> digit]
[App f x | f <- alphaNum, x <- expr]
|])
Meaning, to construct the Num i
expression, draw i
from the monadic action. If that case fails, try the next one App
.
The Alternative
class's (<|>)
is used for this alternating.
This parser works as expected:
> parse expr "" "fg1"
Right (App 'f' (App 'g' (Num 1)))
Now here's the good bit, if we comment out or forget the App
line,
or we add a new constructor to our data type, we get:
onto-demo.hs:19:5: warning:
The following cases have not been covered: Exp.App
This template haskell extension could be made into a GHC source plugin, or added to GHC as an extension.
The abuse of the do
-notation is coincidental. Some other syntax could be used.
The name comes from the "onto mapping", i.e.
∀y ∈ B,∃x ∈ A,f(x) = y
the function f
has an output for every element of set B, mapped from some value in set A.