-
Notifications
You must be signed in to change notification settings - Fork 37
/
CustomStar.hs
158 lines (151 loc) · 7.11 KB
/
CustomStar.hs
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
{-# LANGUAGE TemplateHaskellQuotes #-}
-----------------------------------------------------------------------------
-- |
-- Module : Data.Singletons.TH.CustomStar
-- Copyright : (C) 2013 Richard Eisenberg
-- License : BSD-style (see LICENSE)
-- Maintainer : Ryan Scott
-- Stability : experimental
-- Portability : non-portable
--
-- This file implements 'singletonStar', which generates a datatype @Rep@ and associated
-- singleton from a list of types. The promoted version of @Rep@ is kind @*@ and the
-- Haskell types themselves. This is still very experimental, so expect unusual
-- results!
--
-- See also @Data.Singletons.Base.CustomStar@ from @singletons-base@, a
-- variant of this module that also re-exports related definitions from
-- @Prelude.Singletons@.
--
----------------------------------------------------------------------------
module Data.Singletons.TH.CustomStar (
singletonStar,
module Data.Singletons.TH
) where
import Language.Haskell.TH
import Data.Singletons.TH
import Data.Singletons.TH.Deriving.Eq
import Data.Singletons.TH.Deriving.Infer
import Data.Singletons.TH.Deriving.Ord
import Data.Singletons.TH.Deriving.Show
import Data.Singletons.TH.Promote
import Data.Singletons.TH.Promote.Monad
import Data.Singletons.TH.Names
import Data.Singletons.TH.Options
import Data.Singletons.TH.Single
import Data.Singletons.TH.Single.Data
import Data.Singletons.TH.Single.Monad
import Data.Singletons.TH.Syntax
import Data.Singletons.TH.Util
import Control.Monad
import Data.Maybe
import Language.Haskell.TH.Desugar
-- | Produce a representation and singleton for the collection of types given.
--
-- A datatype @Rep@ is created, with one constructor per type in the declared
-- universe. When this type is promoted by the @singletons-th@ library, the
-- constructors become full types in @*@, not just promoted data constructors.
--
-- For example,
--
-- > $(singletonStar [''Nat, ''Bool, ''Maybe])
--
-- generates the following:
--
-- > data Rep = Nat | Bool | Maybe Rep deriving (Eq, Ord, Read, Show)
--
-- and its singleton. However, because @Rep@ is promoted to @*@, the singleton
-- is perhaps slightly unexpected:
--
-- > data SRep (a :: *) where
-- > SNat :: Sing Nat
-- > SBool :: Sing Bool
-- > SMaybe :: Sing a -> Sing (Maybe a)
-- > type instance Sing = SRep
--
-- The unexpected part is that @Nat@, @Bool@, and @Maybe@ above are the real @Nat@,
-- @Bool@, and @Maybe@, not just promoted data constructors.
--
-- Please note that this function is /very/ experimental. Use at your own risk.
singletonStar :: OptionsMonad q
=> [Name] -- ^ A list of Template Haskell @Name@s for types
-> q [Dec]
singletonStar names = do
kinds <- mapM getKind names
ctors <- zipWithM (mkCtor True) names kinds
let repDecl = DDataD Data [] repName [] (Just (DConT typeKindName)) ctors
[DDerivClause Nothing (map DConT [''Eq, ''Ord, ''Read, ''Show])]
fakeCtors <- zipWithM (mkCtor False) names kinds
let dataDecl = DataDecl Data repName [] fakeCtors
-- Why do we need withLocalDeclarations here? It's because we end up
-- expanding type synonyms when deriving instances for Rep, which requires
-- reifying Rep itself. Since Rep hasn't been spliced in yet, we must put it
-- into the local declarations.
withLocalDeclarations [decToTH repDecl] $ do
-- We opt to infer the constraints for the Eq instance here so that when it's
-- promoted, Rep will be promoted to Type.
dataDeclEqCxt <- inferConstraints (DConT ''Eq) (DConT repName) fakeCtors
let dataDeclEqInst = DerivedDecl (Just dataDeclEqCxt) (DConT repName) repName dataDecl
eqInst <- mkEqInstance Nothing (DConT repName) dataDecl
ordInst <- mkOrdInstance Nothing (DConT repName) dataDecl
showInst <- mkShowInstance Nothing (DConT repName) dataDecl
(pInsts, promDecls) <- promoteM [] $ do _ <- promoteDataDec dataDecl
traverse (promoteInstanceDec mempty mempty)
[eqInst, ordInst, showInst]
singletonDecls <- singDecsM [] $ do decs1 <- singDataD dataDecl
decs2 <- singDerivedEqDecs dataDeclEqInst
decs3 <- traverse singInstD pInsts
return (decs1 ++ decs2 ++ decs3)
return $ decsToTH $ repDecl :
promDecls ++
singletonDecls
where -- get the kinds of the arguments to the tycon with the given name
getKind :: DsMonad q => Name -> q [DKind]
getKind name = do
info <- reifyWithLocals name
dinfo <- dsInfo info
case dinfo of
DTyConI (DDataD _ (_:_) _ _ _ _ _) _ ->
fail "Cannot make a representation of a constrained data type"
DTyConI (DDataD _ [] _ tvbs mk _ _) _ -> do
all_tvbs <- buildDataDTvbs tvbs mk
return $ map (fromMaybe (DConT typeKindName) . extractTvbKind) all_tvbs
DTyConI (DTySynD _ tvbs _) _ ->
return $ map (fromMaybe (DConT typeKindName) . extractTvbKind) tvbs
DPrimTyConI _ n _ ->
return $ replicate n $ DConT typeKindName
_ -> fail $ "Invalid thing for representation: " ++ (show name)
-- first parameter is whether this is a real ctor (with a fresh name)
-- or a fake ctor (when the name is actually a Haskell type)
mkCtor :: DsMonad q => Bool -> Name -> [DKind] -> q DCon
mkCtor real name args = do
(types, vars) <- evalForPair $ mapM (kindToType []) args
dataName <- if real then mkDataName (nameBase name) else return name
return $ DCon (map (`DPlainTV` SpecifiedSpec) vars) [] dataName
(DNormalC False (map (\ty -> (noBang, ty)) types))
(DConT repName)
where
noBang = Bang NoSourceUnpackedness NoSourceStrictness
-- demote a kind back to a type, accumulating any unbound parameters
kindToType :: DsMonad q => [DTypeArg] -> DKind -> QWithAux [Name] q DType
kindToType _ (DForallT _ _) = fail "Explicit forall encountered in kind"
kindToType _ (DConstrainedT _ _) = fail "Explicit constraint encountered in kind"
kindToType args (DAppT f a) = do
a' <- kindToType [] a
kindToType (DTANormal a' : args) f
kindToType args (DAppKindT f a) = do
a' <- kindToType [] a
kindToType (DTyArg a' : args) f
kindToType args (DSigT t k) = do
t' <- kindToType [] t
k' <- kindToType [] k
return $ DSigT t' k' `applyDType` args
kindToType args (DVarT n) = do
addElement n
return $ DVarT n `applyDType` args
kindToType args (DConT n) = return $ DConT name `applyDType` args
where name | isTypeKindName n = repName
| otherwise = n
kindToType args DArrowT = return $ DArrowT `applyDType` args
kindToType args k@(DLitT {}) = return $ k `applyDType` args
kindToType args DWildCardT = return $ DWildCardT `applyDType` args