large-anon-0.1.0.0: src/Data/Record/Anon/Internal/Plugin/Source/NamingT.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- | Naming things is hard
module Data.Record.Anon.Internal.Plugin.Source.NamingT (
-- * Monad definition
NamingT -- opaque
, runNamingT
, runNamingHsc
-- * Key features of NamingT
, useName
, fresh
-- * Convenience derived features
, freshVar
) where
import Control.Monad.Reader
import Control.Monad.State
import Data.Bifunctor
import Data.IORef
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Record.Anon.Internal.Plugin.Source.GhcShim
{-------------------------------------------------------------------------------
Monad definition
-------------------------------------------------------------------------------}
data Env = Env {
envNameCache :: IORef NameCache
}
-- | Naming things is hard
--
-- The 'NamingT' monad transformer that provides two things:
--
-- 1. Keep track of the imports we need for the names that we use.
-- 2. Generation of fresh names.
newtype NamingT m a = WrapNamingT {
unwrapNamingT :: StateT (Set ModuleName) (ReaderT Env m) a
}
deriving (Functor, Applicative, Monad)
instance MonadTrans NamingT where
lift = WrapNamingT . lift . lift
runNamingT :: Functor m => IORef NameCache -> NamingT m a -> m (a, [ModuleName])
runNamingT ncVar =
fmap (second Set.toList)
. flip runReaderT env
. flip runStateT Set.empty
. unwrapNamingT
where
env :: Env
env = Env { envNameCache = ncVar }
runNamingHsc :: NamingT Hsc a -> Hsc (a, [ModuleName])
runNamingHsc ma = do
env <- getHscEnv
runNamingT (hsc_NC env) ma
{-------------------------------------------------------------------------------
Key features of NamingT
-------------------------------------------------------------------------------}
useName :: Monad m => RdrName -> NamingT m ()
useName (Qual modl _) = WrapNamingT $ modify (Set.insert modl)
useName _otherwise = error "useName: expected qualified name"
fresh :: MonadIO m => SrcSpan -> RdrName -> NamingT m RdrName
fresh l name = WrapNamingT $ do
ncVar <- asks envNameCache
liftIO $ atomicModifyIORef ncVar aux
where
aux :: NameCache -> (NameCache, RdrName)
aux nc = (
nc { nsUniqs = us }
, Exact $ mkInternalName newUniq (newOccName (rdrNameOcc name)) l
)
where
(newUniq, us) = takeUniqFromSupply (nsUniqs nc)
-- Even when we generate fresh names, ghc can still complain about name
-- shadowing, because this check only considers the 'OccName', not the
-- unique. We therefore prefix the name with an underscore to avoid the
-- warning.
newOccName :: OccName -> OccName
newOccName n = mkOccName (occNameSpace n) . ("_" ++) $ occNameString n
{-------------------------------------------------------------------------------
Derived convenience functions
-------------------------------------------------------------------------------}
freshVar :: MonadIO m => SrcSpan -> String -> NamingT m RdrName
freshVar l = fresh l . mkRdrUnqual . mkVarOcc