1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
{-# LANGUAGE LambdaCase #-}
module NonBaseTH where
import Data.List (sort)
import Language.Haskell.TH
-- | Define a new GADT that is a base-functor-like version of a given existing
-- GADT AST.
--
-- Remember to use 'lookupTypeName' or 'lookupValueName' instead of normal
-- quotes in case of punning of data types and constructors.
defineBaseAST
:: Name -- ^ Name of the (base-functor-like) data type to define
-> Name -- ^ Name of the GADT to process
-> [Name] -- ^ Constructors to exclude (chiefly Var, Let, Lam)
-> Q [Dec]
defineBaseAST basename astname excludes = do
info <- reify astname
(params, constrs) <- case info of
TyConI (DataD [] _ params Nothing constrs _) -> return (params, constrs)
_ -> fail $ "Unsupported datatype: " ++ pprint astname
let recvar = mkName "r"
let detectRec :: BangType -> Q (Maybe Type)
detectRec (_, field) = _
let processConstr con = do
(vars, ctx, names, fields, retty) <- case con of
ForallC vars ctx (GadtC names fields retty) -> return (vars, ctx, names, fields, retty)
GadtC names fields retty -> return ([], [], names, fields, retty)
_ -> fail "Unsupported constructors found"
checkRetty astname (head names) vars retty
_
constrs' <- concat <$> traverse processConstr constrs
_
checkRetty :: Name -> Name -> [TyVarBndr a] -> Type -> Q ()
checkRetty astname consname vars retty = do
case splitApps retty of
(ConT name, args)
| name /= astname -> fail $ "Could not parse return type of constructor " ++ pprint consname
| null args -> fail "Expected GADT to have type parameters"
| Just varnames <- traverse (\case VarT varname -> Just varname ; _ -> Nothing) (init args)
, allDistinct varnames
, all (`elem` map bndrName vars) varnames ->
return ()
| otherwise -> fail $ "All type parameters but the last one must be uniform over all constructors. "
++ "(Return type of constructor " ++ pprint consname ++ ")"
_ -> fail $ "Could not parse return type of constructor " ++ pprint consname
splitApps :: Type -> (Type, [Type])
splitApps = flip go []
where go (AppT t arg) tl = go t (arg : tl)
go t tl = (t, tl)
allDistinct :: Ord a => [a] -> Bool
allDistinct l =
let sorted = sort l
in all (uncurry (/=)) (zip sorted (drop 1 sorted))
bndrName :: TyVarBndr a -> Name
bndrName (PlainTV n _) = n
bndrName (KindedTV n _ _) = n
|