How can I write COMPLETE pragmas for types with many constructors?

131 Views Asked by At

Suppose I have a type with many constructors and a few pattern synonyms. I'd like to use pattern synonyms to replace a few of the constructors. How can I write the necessary COMPLETE pragma(s) without having to write out all the constructors by hand and risk falling behind if more are added?

1

There are 1 best solutions below

0
dfeuer On

Using the th-abstraction package, this is quite simple. Some throat clearing:

import Language.Haskell.TH.Datatype (DatatypeInfo (..), ConstructorInfo(..), reifyDatatype)
import Language.Haskell.TH (Q, Dec, Name, pragCompleteD)
import Data.List ((\\))

Using reifyDatatype, we can get info about a type, and extract a list of the names of its constructors. Then we simply need to add on the patterns we want and remove the constructors we don't want.

-- | Produce a @COMPLETE@ pragma for a type with many constructors,
-- without having to list them all out.
--
-- @completeWithButWithout ''T ['P] ['C1, 'C2]@ produces a @COMPLETE@
-- pragma stating that pattern matching on the type @T@ is complete with
-- with the pattern @P@ and with all the constructors of @T@ other than
-- @C1@ and @C2@.
completeWithButWithout :: Name -> [Name] -> [Name] -> Q [Dec]
completeWithButWithout ty extra_patterns excl_constrs = do
  di <- reifyDatatype ty
  let constrs = map constructorName (datatypeCons di)
  (:[]) <$> pragCompleteD (extra_patterns ++ (constrs \\ excl_constrs))
                          (Just ty)

Now the module defining the datatype just needs to import this one, and say

data Foo = Bar' Int | Baz | Quux | ...
pattern Bar :: Char -> Foo

$(completeWithButWithout ''Foo ['Bar] ['Bar'])

I recommend invoking completeWithButWithout at the very end of the module, to prevent the splice from splitting the module.