Rework gmapMFor so it takes a list of type keys.

This means we only need one gmapMFor function, and we do fewer calls to
typeKey, but we have to make typeKey available for use where it's called.
This commit is contained in:
Adam Sampson 2008-04-02 15:09:07 +00:00
parent aeb2ebd9f4
commit d9df114909
3 changed files with 49 additions and 50 deletions

View File

@ -146,7 +146,8 @@ testTypeContains = TestList
Int -> Bool -> a -> b -> Test Int -> Bool -> a -> b -> Test
test n exp start find test n exp start find
= TestCase $ assertEqual ("testTypeContains " ++ show n) = TestCase $ assertEqual ("testTypeContains " ++ show n)
exp (typeContains start find) exp (typeContains (typeKey start)
(typeKey find))
--Returns the list of tests: --Returns the list of tests:
tests :: Test tests :: Test

View File

@ -22,7 +22,11 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
-- 'typeContains' is faster than PlateData's equivalent at the cost of some -- 'typeContains' is faster than PlateData's equivalent at the cost of some
-- flexibility: it'll only work for types that it knows about (which can be -- flexibility: it'll only work for types that it knows about (which can be
-- added to in the definition of 'contains'). -- added to in the definition of 'contains').
module GenericUtils (typeContains, gmapMFor, gmapMFor2) where module GenericUtils (
TypeKey, typeKey
, typeContains
, gmapMFor
) where
import Data.Generics import Data.Generics
import Data.IntMap (IntMap) import Data.IntMap (IntMap)
@ -35,6 +39,14 @@ import System.IO.Unsafe
import qualified AST as A import qualified AST as A
-- | A type identifier.
type TypeKey = Int
-- | Given a witness for a type, return its 'TypeKey'.
typeKey :: Typeable a => a -> TypeKey
typeKey x = unsafePerformIO $ typeRepKey $ typeOf x
-- | Container for 'Data' items.
data DataBox = forall a. (Typeable a, Data a) => DataBox a data DataBox = forall a. (Typeable a, Data a) => DataBox a
-- | Given a witness for a type, return witnesses for all the types that its -- | Given a witness for a type, return witnesses for all the types that its
@ -46,20 +58,16 @@ constrArgTypes x = if isAlgType dtype then concatMap f constrs else []
constrs = dataTypeConstrs dtype constrs = dataTypeConstrs dtype
dtype = dataTypeOf x dtype = dataTypeOf x
-- | Given a witness for a type, return its type key.
typeKey :: Typeable a => a -> Int
typeKey x = unsafePerformIO $ typeRepKey $ typeOf x
-- | Given a witness for a type, return a map from type keys to witnesses for -- | Given a witness for a type, return a map from type keys to witnesses for
-- all the types it contains recursively. -- all the types it contains recursively.
containsTypes :: (Data a, Typeable a) => a -> IntMap DataBox containedTypes :: (Data a, Typeable a) => a -> IntMap DataBox
containsTypes start = containsTypes' (DataBox start) IntMap.empty containedTypes start = containedTypes' (DataBox start) IntMap.empty
where where
containsTypes' :: DataBox -> IntMap DataBox -> IntMap DataBox containedTypes' :: DataBox -> IntMap DataBox -> IntMap DataBox
containsTypes' box@(DataBox thisType) seen containedTypes' box@(DataBox thisType) seen
= if thisKey `IntMap.member` seen = if thisKey `IntMap.member` seen
then seen then seen
else foldl (\s t -> containsTypes' t s) else foldl (\s t -> containedTypes' t s)
(IntMap.insert thisKey box seen) (IntMap.insert thisKey box seen)
(constrArgTypes thisType) (constrArgTypes thisType)
where where
@ -72,50 +80,33 @@ type ContainsMap = IntMap IntSet
-- At the moment this only knows about types reachable from the AST. -- At the moment this only knows about types reachable from the AST.
contains :: ContainsMap contains :: ContainsMap
contains = IntMap.fromList [(typeKey t, contains = IntMap.fromList [(typeKey t,
IntMap.keysSet $ containsTypes t) IntMap.keysSet $ containedTypes t)
| DataBox t <- IntMap.elems allTypes] | DataBox t <- IntMap.elems allTypes]
where where
allTypes = containsTypes (undefined :: A.AST) allTypes = containedTypes (undefined :: A.AST)
-- | Does one type contain another? -- | Does one type contain another?
-- (A type always contains itself.) -- (A type always contains itself.)
typeContains :: (Data a, Typeable a, Data b, Typeable b) => a -> b -> Bool typeContains :: TypeKey -> TypeKey -> Bool
typeContains start find typeContains start find
= if startKey == findKey | start == find = True
then True | otherwise = case IntMap.lookup start contains of
else case IntMap.lookup startKey contains of Just set -> find `IntSet.member` set
Just set -> findKey `IntSet.member` set
Nothing -> True -- can't tell, so it might be Nothing -> True -- can't tell, so it might be
where
startKey = typeKey start
findKey = typeKey find
-- | Type-smart generic mapM. -- | Type-smart generic mapM.
-- This is like 'gmapM', but it only applies the function to arguments that -- This is like 'gmapM', but it only applies the function to arguments that
-- could contain the target type. -- could contain any of the target types.
gmapMFor :: (Monad m, Data t, Data a) => gmapMFor :: (Monad m, Data t) =>
a -- ^ Witness for target type [TypeKey] -- ^ Target types
-> (forall s. Data s => s -> m s) -- ^ Function to apply -> (forall s. Data s => s -> m s) -- ^ Function to apply
-> (t -> m t) -- ^ Generic operation -> (t -> m t) -- ^ Generic operation
gmapMFor find top = gmapM (each find top) gmapMFor find f = gmapM (each f)
where where
each :: (Monad m, Data t, Data a) => each :: (Monad m, Data t) =>
a -> (forall s. Data s => s -> m s) -> (t -> m t) (forall s. Data s => s -> m s) -> (t -> m t)
each find top x each f x
= if cont then top x else return x = if cont then f x else return x
where cont = x `typeContains` find
-- | Two-type version of 'gmapMFor'.
gmapMFor2 :: (Monad m, Data t, Data a1, Data a2) =>
a1 -- ^ Witness for target type 1
-> a2 -- ^ Witness for target type 2
-> (forall s. Data s => s -> m s) -- ^ Function to apply
-> (t -> m t) -- ^ Generic operation
gmapMFor2 find1 find2 top = gmapM (each find1 find2 top)
where where
each :: (Monad m, Data t, Data a1, Data a2) => cont = or $ map (typeContains xKey) find
a1 -> a2 -> (forall s. Data s => s -> m s) -> (t -> m t) xKey = typeKey x
each find1 find2 top x
= if cont then top x else return x
where cont = x `typeContains` find1 || x `typeContains` find2

View File

@ -57,8 +57,11 @@ applyExplicitM :: forall t1 s. (Data t1, Data s) =>
ExplicitTrans t1 -> s -> PassM s ExplicitTrans t1 -> s -> PassM s
applyExplicitM f1 = doGeneric `extM` (doSpecific f1) applyExplicitM f1 = doGeneric `extM` (doSpecific f1)
where where
typeSet :: [TypeKey]
typeSet = [typeKey (undefined :: t1)]
doGeneric :: Data t => t -> PassM t doGeneric :: Data t => t -> PassM t
doGeneric = gmapMFor (undefined :: t1) (applyExplicitM f1) doGeneric = gmapMFor typeSet (applyExplicitM f1)
doSpecific :: Data t => ExplicitTrans t -> t -> PassM t doSpecific :: Data t => ExplicitTrans t -> t -> PassM t
doSpecific f x = f doGeneric x doSpecific f x = f doGeneric x
@ -69,9 +72,13 @@ applyExplicitM2 :: forall t1 t2 s. (Data t1, Data t2, Data s) =>
applyExplicitM2 f1 f2 = doGeneric `extM` (doSpecific f1) applyExplicitM2 f1 f2 = doGeneric `extM` (doSpecific f1)
`extM` (doSpecific f2) `extM` (doSpecific f2)
where where
typeSet :: [TypeKey]
typeSet = [ typeKey (undefined :: t1)
, typeKey (undefined :: t2)
]
doGeneric :: Data t => t -> PassM t doGeneric :: Data t => t -> PassM t
doGeneric = gmapMFor2 (undefined :: t1) (undefined :: t2) doGeneric = gmapMFor typeSet (applyExplicitM2 f1 f2)
(applyExplicitM2 f1 f2)
doSpecific :: Data t => ExplicitTrans t -> t -> PassM t doSpecific :: Data t => ExplicitTrans t -> t -> PassM t
doSpecific f x = f doGeneric x doSpecific f x = f doGeneric x