th-typegraph-0.17: Language/Haskell/TH/TypeGraph/Free.hs
{-# LANGUAGE CPP, FlexibleContexts, FlexibleInstances, ScopedTypeVariables, TemplateHaskell #-}
module Language.Haskell.TH.TypeGraph.Free
( freeTypeVars
) where
import Control.Applicative ((<$>))
import Control.Lens hiding (Strict, cons)
import Control.Monad.State (MonadState, execStateT)
import Data.Set as Set (Set, delete, difference, empty, fromList, insert, member)
import Language.Haskell.TH
import Language.Haskell.TH.Syntax (Quasi(qReify))
import Language.Haskell.TH.TypeGraph.Core (pprint')
#if 0
data SetDifferences a = SetDifferences {unexpected :: Set a, missing :: Set a} deriving (Eq, Ord, Show)
setDifferences :: Ord a => Set a -> Set a -> SetDifferences a
setDifferences actual expected = SetDifferences {unexpected = Set.difference actual expected, missing = Set.difference expected actual}
noDifferences = SetDifferences {unexpected = Set.empty, missing = Set.empty}
unReify :: Data a => a -> a
unReify = everywhere (mkT unReifyName)
unReifyName :: Name -> Name
unReifyName = mkName . nameBase
-- Some very nasty bug is triggered here in ghc-7.8 if we try to implement
-- a generic function that unReifies the symbols. Ghc-7.10 works fine
-- pprint'' :: (Data a, Ppr a) => a -> String
-- pprint'' = pprint' . unReify
pprintDec :: Dec -> String
pprintDec = pprint' . unReify
pprintType :: E Type -> String
pprintType = pprint' . unReify . runExpanded
pprintVertex :: TypeGraphVertex -> String
pprintVertex = pprint'
pprintPred :: E Pred -> String
pprintPred = pprint' . unReify . runExpanded
edgesToStrings :: GraphEdges label TypeGraphVertex -> [(String, [String])]
edgesToStrings mp = List.map (\ (t, (_, s)) -> (pprintVertex t, map pprintVertex (Set.toList s))) (Map.toList mp)
typeGraphInfo' :: [(Maybe Field, E Type, VertexHint)] -> [Type] -> Q (TypeGraphInfo VertexHint)
typeGraphInfo' = typeGraphInfo
typeGraphEdges' :: forall m. (DsMonad m, MonadReader (TypeGraphInfo VertexHint) m) => m (GraphEdges VertexHint TypeGraphVertex)
typeGraphEdges' = typeGraphEdges
withTypeGraphInfo' :: forall m a. DsMonad m =>
[(Maybe Field, E Type, VertexHint)] -> [Type] -> ReaderT (TypeGraphInfo VertexHint) m a -> m a
withTypeGraphInfo' = withTypeGraphInfo
-- | Return a mapping from vertex to all the known type synonyms for
-- the type in that vertex.
typeSynonymMap :: forall m hint. (DsMonad m, Default hint, Eq hint, HasVertexHints hint, MonadReader (TypeGraphInfo hint) m) =>
m (Map TypeGraphVertex (Set Name))
typeSynonymMap =
(Map.filter (not . Set.null) .
Map.fromList .
List.map (\node -> (node, _syns node)) .
Map.keys) <$> typeGraphEdges
-- | Like 'typeSynonymMap', but with all field information removed.
typeSynonymMapSimple :: forall m hint. (DsMonad m, Default hint, Eq hint, HasVertexHints hint, MonadReader (TypeGraphInfo hint) m) =>
m (Map (E Type) (Set Name))
typeSynonymMapSimple =
simplify <$> typeSynonymMap
where
simplify :: Map TypeGraphVertex (Set Name) -> Map (E Type) (Set Name)
simplify mp = Map.fromListWith Set.union (List.map (\ (k, a) -> (_etype k, a)) (Map.toList mp))
#endif
#if 0
freeNamesOfTypes :: [Type] -> Set Name
freeNamesOfTypes = mconcat . map freeNamesOfType
-- | This is based on the freeNamesOfTypes function from the
-- th-desugar package. However, it has a weakness in that if
-- we encounter a type application, it may be that
freeNamesOfType :: Quasi m => Type -> m (Set Name)
freeNamesOfType = go
where
go (ForallT tvbs cxt ty) = (go ty <> mconcat (map go_pred cxt))
\\ Set.fromList (map tvbName tvbs)
go (AppT t1 t2) = go_app [t2] t1
go (AppT t1 t2) = go t1 <> go t2
go (SigT ty _) = go ty
go (VarT n) = Set.singleton n
go _ = Set.empty
#if MIN_VERSION_template_haskell(2,10,0)
go_pred = go
#else
go_pred (ClassP _ tys) = freeNamesOfTypes tys
go_pred (EqualP t1 t2) = go t1 <> go t2
#endif
go_app params (AppT t1 t2) = go_app (t2 : params) t1
go_app params (ConT n) = qReify n >>= go_info params
go_app params typ = concatMap go (typ : params)
#endif
data St
= St { _result :: Set Name
, _stack :: Set Name
} deriving Show
st0 :: St
st0 = St {_result = empty, _stack = empty}
$(makeLenses ''St)
freeTypeVars :: (FreeTypeVars t, Quasi m) => t -> m (Set Name)
freeTypeVars x = view result <$> execStateT (ftv x) st0
-- | This is based on the freeNamesOfTypes function from the
-- th-desugar package.
class FreeTypeVars t where
ftv :: (Quasi m, MonadState St m) => t -> m ()
instance FreeTypeVars a => FreeTypeVars [a] where
ftv ts = mapM_ ftv ts
instance FreeTypeVars Type where
ftv (ForallT tvbs cx ty) = do
ftv ty
mapM_ go_pred cx
result %= (`Set.difference` (Set.fromList (map tvbName tvbs)))
where
#if MIN_VERSION_template_haskell(2,10,0)
go_pred typ =
-- This looks wrong as the one below looks wrong. Wronger maybe.
ftv typ
#else
go_pred (ClassP _ tys) = ftv tys
go_pred (EqualP t1 t2) = do
-- This looks wrong - we need to unify t1 and t2 and look
-- at the free type variables in the resulting bindings
ftv t1
ftv t2
#endif
ftv (SigT ty _) = ftv ty
ftv (VarT n) = result %= Set.insert n
ftv (AppT t1 t2) = {-trace ("go_app " ++ show typ) (return ()) >>-} go_app [t2] t1
ftv typ@(ConT _) = {-trace ("go_app " ++ show typ) (return ()) >>-} go_app [] typ
ftv _ = return ()
go_app :: (Quasi m, MonadState St m) => [Type] -> Type -> m ()
go_app params (AppT t1 t2) = go_app (t2 : params) t1
go_app params (ConT n) = do
stk <- use stack
case Set.member n stk of
True -> return ()
False -> do
stack %= Set.insert n
qReify n >>= go_info (reverse params)
go_app params typ = mapM_ ftv (typ : params)
go_info :: (Quasi m, MonadState St m) => [Type] -> Info -> m ()
go_info params (TyConI dec) = go_dec params ({-trace ("go_dec " ++ show dec)-} dec)
go_info params (FamilyI dec _insts) = go_dec params dec
go_info _params (PrimTyConI _name _arity _unlifed) = return ()
go_info _params info = error $ "go_info - unexpected: " ++ pprint' info
go_dec :: (Quasi m, MonadState St m) => [Type] -> Dec -> m ()
go_dec params (NewtypeD cx tname tvs con supers) = go_dec params (DataD cx tname tvs [con] supers)
go_dec params (DataD _ tname tvs _ _) | length params > length tvs = error $ "Too many arguments to " ++ show tname
go_dec params (DataD _cx tname tvs cons _supers) = do
-- For each type variable bound to a type parameter,
-- replace the type variable with the free variables
-- in the parameter
ftv cons
go_params tname tvs params
go_dec params (TySynD tname tvs typ) = do
-- Add the free variables in the type, then subtract the ones that
-- are bound here.
ftv typ
go_params tname tvs params
-- I have a feeling this is utterly wrong. Example, with this class:
--
-- class OrderKey k => OrderMap k where
-- data Order k :: * -> *
-- ...
--
-- the resulting declaration of Order is
--
-- FamilyD DataFam Language.Haskell.TH.Path.Order.Order [PlainTV k,PlainTV $a] (Just StarT)
-- params=[ConT AbbrevPairID]
--
-- so the parameter is bound to k, and $a should be free.
go_dec params (FamilyD _flavour tname tvs _mkind) = go_params tname tvs params
go_dec params dec = error $ "go_dec - unexpected: " ++ pprint' dec ++ ", params=" ++ show params
go_params :: (Quasi m, MonadState St m) => Name -> [TyVarBndr] -> [Type] -> m ()
go_params tname tvs params | length params > length tvs = error $ "Too many arguments to " ++ show tname
go_params _ tvs params = mapM_ (uncurry go_param) (zip tvs (map Just params ++ repeat Nothing))
-- | Update the free variable set for a type parameter
go_param :: (Quasi m, MonadState St m) => TyVarBndr -> Maybe Type -> m ()
go_param tvb (Just param) = do
-- If there is a binding, add the free variables found in the type
-- and remove the variable bound here
-- trace ("go_param " ++ "(" ++ pprint tvb ++ ", " ++ pprint' param ++ ")") (return ())
ftv param
result %= Set.delete (tvbName tvb)
-- let tv = tvbName tvb
-- r <- use result
-- when (Set.member tv r) (ftv param >> result %= Set.delete tv)
go_param tvb Nothing = do
-- If there is a variable not bound to a type parameter it is fee
result %= Set.insert (tvbName tvb)
{-
instance FreeTypeVars Info where
ftv (TyConI dec) = ftv dec
instance FreeTypeVars Dec where
ftv dec@(DataD _ _ _ _ _ _) = ftv dec
#if MIN_VERSION_template_haskell(2,10,0)
go_pred = go
#else
go_pred (ClassP _ tys) = freeNamesOfTypes tys
go_pred (EqualP t1 t2) = go t1 <> go t2
#endif
-}
instance FreeTypeVars Con where
ftv (NormalC _name sts) = ftv sts
ftv (RecC _name vsts) = ftv vsts
ftv (InfixC st1 _ st2) = ftv [st1, st2]
-- I'm not sure what effect this forall has.
ftv (ForallC _tvbs _cx con) = ftv con
instance FreeTypeVars (Strict, Type) where
ftv (_, typ) = ftv typ
instance FreeTypeVars (Name, Strict, Type) where
ftv (_, _, typ) = ftv typ
-- | Extract a 'Name' from a 'TyVarBndr'
tvbName :: TyVarBndr -> Name
tvbName (PlainTV n) = n
tvbName (KindedTV n _) = n