77module Data.Universe.Some.TH (
88 DeriveUniverseSome (.. ),
99 universeSomeQ ,
10+ universeFSomeQ ,
1011 ) where
1112
1213import Control.Monad (forM , mapM , unless )
13- import Data.Some (Some , mkSome )
14- import Data.Universe.Class (Universe (.. ))
15- import Data.Universe.Some (UniverseSome (.. ))
14+ import Data.Some (Some , mkSome , withSomeM )
15+ import Data.Traversable (for )
16+ import Data.Universe.Class (Universe (.. ), Finite (.. ))
17+ import Data.Universe.Some (UniverseSome (.. ), FiniteSome (.. ))
1618import Data.Universe.Helpers (interleave , (<+*+>) )
1719import Language.Haskell.TH
1820import Language.Haskell.TH.Datatype
@@ -41,14 +43,28 @@ import Language.Haskell.TH.Datatype
4143class DeriveUniverseSome a where
4244 deriveUniverseSome :: a -> DecsQ
4345
46+ -- | 'deriveFiniteSome' derives 'FiniteSome' instance with slightly different
47+ -- code, without interleaving of different branches. This allows to handle
48+ -- more cases.
49+ --
50+ -- It also defines a trivial 'universeSome = universeFSome' 'UniverseSome' instance.
51+ --
52+ deriveFiniteSome :: a -> DecsQ
53+
4454instance DeriveUniverseSome a => DeriveUniverseSome [a ] where
4555 deriveUniverseSome a = fmap concat (mapM deriveUniverseSome a)
56+ deriveFiniteSome a = fmap concat (mapM deriveFiniteSome a)
4657
4758instance DeriveUniverseSome a => DeriveUniverseSome (Q a ) where
4859 deriveUniverseSome a = deriveUniverseSome =<< a
60+ deriveFiniteSome a = deriveFiniteSome =<< a
4961
5062instance DeriveUniverseSome Name where
51- deriveUniverseSome name = do
63+ deriveUniverseSome = deriveUniverseSomeName ClsUniverse
64+ deriveFiniteSome = deriveUniverseSomeName ClsFinite
65+
66+ deriveUniverseSomeName :: Cls -> Name -> Q [Dec ]
67+ deriveUniverseSomeName cls name = do
5268 di <- reifyDatatype name
5369 let DatatypeInfo { datatypeContext = ctxt
5470 , datatypeName = parentName
@@ -73,40 +89,77 @@ instance DeriveUniverseSome Name where
7389
7490#if MIN_VERSION_template_haskell(2,10,0)
7591 let constrs :: [TypeQ ]
76- constrs = map (\ n -> conT ''Universe `appT` varT n) varNames
92+ constrs = map (\ n -> conT clsName `appT` varT n) varNames
7793#else
7894 let constrs :: [PredQ ]
79- constrs = map (\ n -> classP ''Universe [varT n]) varNames
95+ constrs = map (\ n -> classP clsName [varT n]) varNames
8096#endif
8197 let typ = foldl (\ c n -> c `appT` varT n) (conT parentName) varNames
8298
83- i <- instanceD (cxt constrs) (conT ''UniverseSome `appT` typ)
84- [ instanceDecFor di
85- ]
99+ case cls of
100+ ClsUniverse -> do
101+ i <- instanceD (cxt constrs) (conT clsSomeName `appT` typ)
102+ [ instanceDecForU di
103+ ]
104+ return [i]
105+ ClsFinite -> do
106+ i <- instanceD (cxt constrs) (conT clsSomeName `appT` typ)
107+ [ instanceDecForF di
108+ ]
109+ j <- instanceD (cxt constrs) (conT ''UniverseSome `appT` typ)
110+ [ instanceDecForUviaF
111+ ]
112+ return [i, j]
113+
114+ where
115+ clsName = case cls of
116+ ClsUniverse -> ''Universe
117+ ClsFinite -> ''Finite
118+
119+ clsSomeName = case cls of
120+ ClsUniverse -> ''UniverseSome
121+ ClsFinite -> ''FiniteSome
122+
123+ instanceDecForU :: DatatypeInfo -> Q Dec
124+ instanceDecForU di = valD (varP 'universeSome) (normalB $ universeSomeQ' di) []
86125
87- return [i]
126+ instanceDecForF :: DatatypeInfo -> Q Dec
127+ instanceDecForF di = valD (varP 'universeFSome) (normalB $ universeFSomeQ' di) []
88128
89- instanceDecFor :: DatatypeInfo -> Q Dec
90- instanceDecFor di = valD (varP 'universeSome) (normalB $ universeSomeQ' di ) []
129+ instanceDecForUviaF :: Q Dec
130+ instanceDecForUviaF = valD (varP 'universeSome) (normalB $ varE 'universeFSome ) []
91131
92132instance DeriveUniverseSome Dec where
133+ deriveUniverseSome = deriveUniverseSomeDec ClsUniverse
134+ deriveFiniteSome = deriveUniverseSomeDec ClsFinite
135+
136+ deriveUniverseSomeDec :: Cls -> Dec -> Q [Dec ]
93137#if MIN_VERSION_template_haskell(2,11,0)
94- deriveUniverseSome (InstanceD overlaps c classHead [] ) = do
95- let instanceFor = InstanceD overlaps c classHead
138+ deriveUniverseSomeDec cls (InstanceD overlaps c classHead [] ) = do
139+ let instanceFor = fmap (InstanceD overlaps c classHead) . sequence
140+ let instanceForU = fmap (InstanceD overlaps c (overHeadOfType (const ''UniverseSome) classHead)) . sequence
96141#else
97- deriveUniverseSome (InstanceD c classHead [] ) = do
98- let instanceFor = InstanceD c classHead
142+ deriveUniverseSomeDec cls (InstanceD c classHead [] ) = do
143+ let instanceFor = fmap (InstanceD c classHead) . sequence
144+ let instanceForU = fmap (InstanceD c (overHeadOfType (const ''UniverseSome) classHead)) . sequence
99145#endif
100146 case classHead of
101- ConT u `AppT ` t | u == ''UniverseSome -> do
102- name <- headOfType t
103- di <- reifyDatatype name
104- i <- fmap instanceFor $ mapM id
105- [ instanceDecFor di
106- ]
107- return [i]
108- _ -> fail $ " deriveUniverseSome: expected an instance head like `UniverseSome (C a b ...)`, got " ++ show classHead
109- deriveUniverseSome _ = fail " deriveUniverseSome: expected an empty instance declaration"
147+ ConT u `AppT ` t
148+ | cls == ClsUniverse
149+ , u == ''UniverseSome -> do
150+ name <- headOfType t
151+ di <- reifyDatatype name
152+ i <- instanceFor [ instanceDecForU di ]
153+ return [i]
154+ | cls == ClsFinite
155+ , u == ''FiniteSome -> do
156+ name <- headOfType t
157+ di <- reifyDatatype name
158+ i <- instanceFor [ instanceDecForF di ]
159+ j <- instanceForU [ instanceDecForUviaF ]
160+ return [i, j]
161+ _ -> fail $ " deriveUniverseSome/deriveFiniteSome: expected an instance head like `UniverseSome (C a b ...)`, got " ++ show classHead
162+ deriveUniverseSomeDec _ _ = fail " deriveUniverseSome/deriveFiniteSome: expected an empty instance declaration"
110163
111164-- | Derive the method for @:: ['Some' tag]@
112165--
@@ -123,6 +176,32 @@ instance DeriveUniverseSome Dec where
123176universeSomeQ :: Name -> ExpQ
124177universeSomeQ name = reifyDatatype name >>= universeSomeQ'
125178
179+ -- | Like 'universeSomeQ' but derives expression for 'universeF'.
180+ --
181+ -- Uses the fact that subterms should be finite,
182+ -- thus allowing to derive more instances.
183+ --
184+ -- >>> :set -XGADTs -XTemplateHaskell -XStandaloneDeriving
185+ -- >>> import Data.GADT.Show
186+ --
187+ -- >>> data Tag b a where IntTag :: Tag b Int; BoolTag :: b -> Tag b Bool
188+ -- >>> deriving instance Show b => Show (Tag b a)
189+ -- >>> instance Show b => GShow (Tag b) where gshowsPrec = showsPrec
190+ -- >>> $(deriveFiniteSome ''Tag);
191+ --
192+ -- >>> universeFSome :: [Some (Tag Bool)]
193+ -- [Some IntTag,Some (BoolTag False),Some (BoolTag True)]
194+ --
195+ -- >>> data Wrap a where Once :: Tag Bool a -> Wrap a; Empty :: Wrap ()
196+ -- >>> deriving instance Show (Wrap a)
197+ -- >>> instance GShow Wrap where gshowsPrec = showsPrec
198+ --
199+ -- >>> $(universeFSomeQ ''Wrap) :: [Some Wrap]
200+ -- [Some (Once IntTag),Some (Once (BoolTag False)),Some (Once (BoolTag True)),Some Empty]
201+ --
202+ universeFSomeQ :: Name -> ExpQ
203+ universeFSomeQ name = reifyDatatype name >>= universeFSomeQ'
204+
126205universeSomeQ' :: DatatypeInfo -> Q Exp
127206universeSomeQ' di = do
128207 let DatatypeInfo { datatypeContext = ctxt
@@ -141,20 +220,65 @@ universeSomeQ' di = do
141220 case safeUnsnoc vars0 of
142221 Nothing -> fail " Datatype should have at least one type variable"
143222 Just (vars, var) -> do
144- let universe' = [| universe | ]
145- let uap = [| (<+*+>) | ]
146- let interleave' = [| interleave | ]
147- let mapSome' = [| map mkSome | ]
148-
149- let sums = map (universeForCon mapSome' universe' uap) cons
223+ let sums = map universeForCon cons
150224 interleave' `appE` listE sums
151225 where
152- universeForCon mapSome' universe' uap ci =
226+ universeForCon ci = do
153227 let con = listE [ conE (constructorName ci) ]
154228 nargs = length (constructorFields ci)
155229 conArgs = foldl (\ f x -> infixE (Just f) uap (Just universe')) con (replicate nargs universe')
230+ mapSome' `appE` conArgs
231+
232+ universe' = [| universe | ]
233+ uap = [| (<+*+>) | ]
234+ interleave' = [| interleave | ]
235+ mapSome' = [| map mkSome | ]
156236
157- in mapSome' `appE` conArgs
237+ universeFSomeQ' :: DatatypeInfo -> Q Exp
238+ universeFSomeQ' di = do
239+ let DatatypeInfo { datatypeContext = ctxt
240+ , datatypeName = parentName
241+ #if MIN_VERSION_th_abstraction(0,3,0)
242+ , datatypeInstTypes = vars0
243+ #else
244+ , datatypeVars = vars0
245+ #endif
246+ , datatypeCons = cons
247+ } = di
248+
249+ -- check
250+ unless (null ctxt) $ fail " Datatype context is not empty"
251+
252+ case safeUnsnoc vars0 of
253+ Nothing -> fail " Datatype should have at least one type variable"
254+ Just (vars, var) -> do
255+ let sums = map universeForCon cons
256+ [| concat | ] `appE` listE sums
257+ where
258+ universeForCon ci = do
259+ let con = conE (constructorName ci)
260+ args <- forM (zip [1 .. ] (constructorFields ci)) $ \ (j, f) -> do
261+ name <- newName (" x" ++ show (j :: Int ))
262+ let defStep kont = infixE (Just universeF') [| (>>=) | ] (Just (lamE [varP name] kont))
263+ return $ (,) (varE name) $ \ kont -> case f of
264+ AppT c _ -> do
265+ #if MIN_VERSION_template_haskell(2,6,0)
266+ isInst <- isInstance ''FiniteSome [c]
267+ #else
268+ isInst <- isClassInstance ''FiniteSome [c]
269+ #endif
270+ if isInst
271+ then [| withSomeM | ] `appE` [| universeFSome | ] `appE` (lamE [varP name] kont)
272+ else defStep kont
273+ _ -> defStep kont
274+
275+ foldl (\ acc (_, kont) -> kont acc)
276+ (listE [([| mkSome | ] `appE` (foldl appE con (map fst args)))])
277+ args
278+
279+ universeF' = [| universeF | ]
280+ universeFSome' = [| universeFSome | ]
281+ mapSome' = [| map mkSome | ]
158282
159283-------------------------------------------------------------------------------
160284-- helpers
@@ -166,7 +290,15 @@ headOfType (VarT n) = return n
166290headOfType (ConT n) = return n
167291headOfType t = fail $ " headOfType: " ++ show t
168292
293+ overHeadOfType :: (Name -> Name ) -> Type -> Type
294+ overHeadOfType f (AppT x y) = AppT (overHeadOfType f x) y
295+ overHeadOfType f (VarT n) = VarT (f n)
296+ overHeadOfType f (ConT n) = ConT (f n)
297+ overHeadOfType f t = t
298+
169299safeUnsnoc :: [a ] -> Maybe ([a ], a )
170300safeUnsnoc xs = case reverse xs of
171301 [] -> Nothing
172302 (y: ys) -> Just (reverse ys, y)
303+
304+ data Cls = ClsUniverse | ClsFinite deriving (Eq )
0 commit comments