1 -----------------------------------------------------------------------------
3 -- Module : Data.Generics.Reify
4 -- Copyright : (c) The University of Glasgow, CWI 2001--2003
5 -- License : BSD-style (see the file libraries/base/LICENSE)
7 -- Maintainer : libraries@haskell.org
8 -- Stability : experimental
9 -- Portability : non-portable
11 -- \"Scrap your boilerplate\" --- Generic programming in Haskell
12 -- See <http://www.cs.vu.nl/boilerplate/>. The present module provides
13 -- some preliminary support some sort of structural reflection. This
14 -- module is presumably less common sense that most other boilerplate
15 -- modules. Also, it is a bit less easy-going.
17 -----------------------------------------------------------------------------
19 module Data.Generics.Reify (
22 TypeVal, -- view type "a" as "a -> ()"
23 typeVal, -- :: TypeVal a
24 sameType, -- two type values are the same
25 val2type, -- :: a -> TypeVal a
26 type2val, -- :: TypeVal a -> a
27 withType, -- :: a -> TypeVal a -> a
28 argType, -- :: (a -> b) -> TypeVal a
29 resType, -- :: (a -> b) -> TypeVal b
30 paraType, -- :: t a -> TypeVal a
31 TypeFun, -- functions on types
32 GTypeFun, -- polymorphic functions on types
33 extType, -- extend a function on types
35 -- * Generic operations to reify terms
43 -- * Generic operations to reify types
44 gmapType, -- query all constructors of a type
45 gmapConstr, -- query all subterm types of a constructor
46 constrArity, -- compute arity of constructor
47 gmapSubtermTypes, -- query all subterm types of a type
48 gmapSubtermTypesConst, -- variation on gmapSubtermTypes
49 gcountSubtermTypes, -- count all types of immediate subterms
50 reachableType, -- test for reachability on types
51 depthOfType, -- compute minimum depth of type
52 depthOfConstr -- compute minimum depth of constructor
57 ------------------------------------------------------------------------------
62 import Data.Generics.Basics
63 import Data.Generics.Aliases
64 import Data.Generics.Schemes
66 -------------------------------------------------------------
70 -------------------------------------------------------------
74 This group provides a style of encoding types as values and using
75 them. This style is seen as an alternative to the pragmatic style used
76 in Data.Typeable.typeOf and elsewhere, i.e., simply use an "undefined"
77 to denote a type argument. This pragmatic style suffers from lack
78 of robustness: one feels tempted to pattern match on undefineds.
79 Maybe Data.Typeable.typeOf etc. should be rewritten accordingly.
84 -- | Type as values to stipulate use of undefineds
85 type TypeVal a = a -> ()
88 -- | The value that denotes a type
93 -- | Test for type equivalence
94 sameType :: (Typeable a, Typeable b) => TypeVal a -> TypeVal b -> Bool
95 sameType tva tvb = typeOf (type2val tva) ==
99 -- | Map a value to its type
100 val2type :: a -> TypeVal a
104 -- | Stipulate this idiom!
105 type2val :: TypeVal a -> a
106 type2val _ = undefined
109 -- | Constrain a type
110 withType :: a -> TypeVal a -> a
114 -- | The argument type of a function
115 argType :: (a -> b) -> TypeVal a
119 -- | The result type of a function
120 resType :: (a -> b) -> TypeVal b
124 -- | The parameter type of type constructor
125 paraType :: t a -> TypeVal a
130 -- i.e., functions mapping types to values
132 type TypeFun a r = TypeVal a -> r
136 -- Generic type functions,
137 -- i.e., functions mapping types to values
139 type GTypeFun r = forall a. Data a => TypeFun a r
142 -- | Extend a type function
143 extType :: (Data a, Typeable r) => GTypeFun r -> TypeFun a r -> GTypeFun r
144 extType f = maybe f id . cast
148 ------------------------------------------------------------------------------
150 -- Generic operations to reify terms
152 ------------------------------------------------------------------------------
155 -- | Count the number of immediate subterms of the given term
156 glength :: GenericQ Int
157 glength = length . gmapQ (const ())
160 -- | Determine depth of the given term
161 gdepth :: GenericQ Int
162 gdepth = (+) 1 . foldr max 0 . gmapQ gdepth
165 -- | Determine the number of all suitable nodes in a given term
166 gcount :: GenericQ Bool -> GenericQ Int
167 gcount p = everything (+) (\x -> if p x then 1 else 0)
170 -- | Determine the number of all nodes in a given term
171 gnodecount :: GenericQ Int
172 gnodecount = gcount (const True)
175 -- | Determine the number of nodes of a given type in a given term
176 gtypecount :: Typeable a => (a -> ()) -> GenericQ Int
177 gtypecount f = gcount (False `mkQ` (const True . f))
180 -- | Find (unambiguously) an immediate subterm of a given type
181 gfindtype :: (Data x, Data y) => x -> Maybe y
182 gfindtype = singleton
184 . gmapQ (Nothing `mkQ` Just)
186 unJust l (Just x) = x:l
188 singleton [s] = Just s
189 singleton _ = Nothing
193 ------------------------------------------------------------------------------
195 -- Generic operations to reify types
197 ------------------------------------------------------------------------------
200 -- | Query all constructors of a given type
202 gmapType :: ([(Constr,r')] -> r)
203 -> GTypeFun (Constr -> r')
206 gmapType (o::[(Constr,r')] -> r) f (t::TypeVal a)
212 -- All constructors of the given type
214 cons = if isPrimType $ dataTypeOf $ type2val t
216 else dataCons $ dataTypeOf $ type2val t
218 -- Query constructors
220 query = map (f t) cons
223 -- | Query all subterm types of a given constructor
225 gmapConstr :: ([r] -> r')
227 -> GTypeFun (Constr -> r')
229 gmapConstr (o::[r] -> r') f (t::TypeVal a) c
235 -- Term for the given constructor
239 -- Query subterm types
241 query = gmapQ (f . val2type) term
244 -- | Compute arity of a given constructor
245 constrArity :: GTypeFun (Constr -> Int)
246 constrArity t c = glength $ withType (fromConstr c) t
249 -- | Query all immediate subterm types of a given type
250 gmapSubtermTypes :: (Data a, Typeable r)
251 => (r -> r -> r) -> r -> GTypeFun r -> TypeVal a -> r
252 gmapSubtermTypes o (r::r) f (t::TypeVal a)
254 reduce (concat (map (gmapQ (query . val2type)) terms))
259 -- All constructors of the given type
261 cons = if isPrimType $ dataTypeOf $ type2val t
263 else dataCons $ dataTypeOf $ type2val t
265 -- Terms for all constructors
267 terms = map fromConstr cons
269 -- Query a subterm type
270 query :: Data b => TypeVal b -> GTypeFun' r -> (r,GTypeFun' r)
271 query t f = (unGTypeFun' f t, GTypeFun' (disable t (unGTypeFun' f)))
273 -- Constant out given type
274 disable :: Data b => TypeVal b -> GTypeFun r -> GTypeFun r
275 disable (t::TypeVal b) f = f `extType` \(_::TypeVal b) -> r
277 -- Reduce all subterm types
278 reduce :: [GTypeFun' r -> (r,GTypeFun' r)] -> GTypeFun' r -> r
280 reduce (xy:z) g = fst (xy g) `o` reduce z (snd (xy g))
283 -- First-class polymorphic variation on GTypeFun
284 newtype GTypeFun' r = GTypeFun' (GTypeFun r)
285 unGTypeFun' (GTypeFun' f) = f
288 -- | Query all immediate subterm types.
289 -- There is an extra argument to \"constant out\" the type at hand.
290 -- This can be used to avoid cycles.
292 gmapSubtermTypesConst :: (Data a, Typeable r)
298 gmapSubtermTypesConst o (r::r) f (t::TypeVal a)
300 gmapSubtermTypes o r f' t
303 f' = f `extType` \(_::TypeVal a) -> r
306 -- Count all distinct subterm types
307 gcountSubtermTypes :: Data a => TypeVal a -> Int
308 gcountSubtermTypes = gmapSubtermTypes (+) (0::Int) (const 1)
311 -- | A simplied variation on gmapSubtermTypes.
312 -- Weakness: no awareness of doubles.
313 -- Strength: easy to comprehend as it uses gmapType and gmapConstr.
315 _gmapSubtermTypes :: (Data a, Typeable r)
316 => (r -> r -> r) -> r -> GTypeFun r -> TypeVal a -> r
317 _gmapSubtermTypes o (r::r) f
319 gmapType otype (gmapConstr oconstr f)
323 otype :: [(Constr,r)] -> r
324 otype = foldr (\x y -> snd x `o` y) r
330 -- | Reachability relation on types, i.e.,
331 -- test if nodes of type @a@ are reachable from nodes of type @b@.
332 -- The relation is defined to be reflexive.
334 reachableType :: (Data a, Data b) => TypeVal a -> TypeVal b -> Bool
335 reachableType (a::TypeVal a) (b::TypeVal b)
338 , gmapSubtermTypesConst (\x y -> or [x,y]) False (reachableType a) b
342 -- | Depth of a datatype as the constructor with the minimum depth.
343 -- The outermost 'Nothing' denotes a type without constructors.
344 -- The innermost 'Nothing' denotes potentially infinite.
346 depthOfType :: GTypeFun Bool -> GTypeFun (Maybe (Constr, Maybe Int))
347 depthOfType p (t::TypeVal a)
353 o :: [(Constr, Maybe Int)] -> Maybe (Constr, Maybe Int)
354 o l = if null l then Nothing else Just (foldr1 min' l)
356 f :: GTypeFun (Constr -> Maybe Int)
359 -- Specific minimum operator
360 min' :: (Constr, Maybe Int) -> (Constr, Maybe Int) -> (Constr, Maybe Int)
361 min' x (_, Nothing) = x
362 min' (_, Nothing) x = x
363 min' (c, Just i) (c', Just i') | i <= i' = (c, Just i)
364 min' (c, Just i) (c', Just i') = (c', Just i')
366 -- Updated predicate for unblocked types
368 p' = p `extType` \(_::TypeVal a) -> False
371 -- | Depth of a constructor.
372 -- Depth is viewed as the maximum depth of all subterm types + 1.
373 -- 'Nothing' denotes potentially infinite.
375 depthOfConstr :: GTypeFun Bool -> GTypeFun (Constr -> Maybe Int)
376 depthOfConstr p (t::TypeVal a) c
382 o :: [Maybe Int] -> Maybe Int
383 o = inc' . foldr max' (Just 0)
385 f :: GTypeFun (Maybe Int)
388 case depthOfType p t' of
393 -- Specific maximum operator
394 max' Nothing _ = Nothing
395 max' _ Nothing = Nothing
396 max' (Just i) (Just i') | i >= i' = Just i
397 max' (Just i) (Just i') = Just i'
399 -- Specific increment operator
400 inc' Nothing = Nothing
401 inc' (Just i) = Just (i+1)