packages feed

large-anon-0.2: src/Data/Record/Anon/Internal/Plugin/Source/FreshT.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

-- | Fresh name generation
module Data.Record.Anon.Internal.Plugin.Source.FreshT (
    -- * Monad definition
    FreshT -- opaque
  , runFreshT
  , runFreshHsc
    -- * Generate fresh names
  , fresh
  , freshVar
  ) where

import Control.Monad.Reader

import Data.Record.Anon.Internal.Plugin.Source.GhcShim

{-------------------------------------------------------------------------------
  Monad definition
-------------------------------------------------------------------------------}

-- | Fresh name generation
newtype FreshT m a = WrapFreshT {
      unwrapNamingT :: ReaderT NameCacheIO m a
    }
  deriving (Functor, Applicative, Monad)

instance MonadTrans FreshT where
  lift = WrapFreshT . lift

runFreshT :: NameCacheIO -> FreshT m a -> m a
runFreshT ncVar = flip runReaderT ncVar . unwrapNamingT

runFreshHsc :: FreshT Hsc a -> Hsc a
runFreshHsc ma = do
    env <- getHscEnv
    runFreshT (hscNameCacheIO env) ma

{-------------------------------------------------------------------------------
  Key features of FreshT
-------------------------------------------------------------------------------}

fresh :: MonadIO m => SrcSpan -> RdrName -> FreshT m RdrName
fresh l name = WrapFreshT $ ReaderT $ \nc -> do
    newUniq <- liftIO $ takeUniqFromNameCacheIO nc
    return $ Exact $ mkInternalName newUniq (newOccName (rdrNameOcc name)) l
  where
    -- 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 -> FreshT m RdrName
freshVar l = fresh l . mkRdrUnqual . mkVarOcc