Merge pull request #284 from DanielG/dev-monad

Turn `GhcMod` into a monad transformer
This commit is contained in:
Kazu Yamamoto 2014-07-11 12:13:19 +09:00
commit 17dfe6b63e

View File

@ -1,14 +1,18 @@
{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, FlexibleInstances #-} {-# LANGUAGE CPP, GeneralizedNewtypeDeriving, FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses, RankNTypes, TypeFamilies #-} {-# LANGUAGE FlexibleContexts, MultiParamTypeClasses, RankNTypes #-}
{-# LANGUAGE TypeFamilies, UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
module Language.Haskell.GhcMod.Monad ( module Language.Haskell.GhcMod.Monad (
GhcMod GhcMod
, GhcModT
, GhcModEnv(..) , GhcModEnv(..)
, GhcModWriter , GhcModWriter
, GhcModState(..) , GhcModState(..)
, runGhcMod' , runGhcMod'
, runGhcMod , runGhcMod
, runGhcModT'
, runGhcModT
, newGhcModEnv , newGhcModEnv
, withErrorHandler , withErrorHandler
, toGhcMod , toGhcMod
@ -44,10 +48,13 @@ import Control.Monad.Trans.Class (lift)
import Data.Monoid (Monoid) import Data.Monoid (Monoid)
#endif #endif
import Control.Monad (liftM) import Control.Applicative (Alternative)
import Control.Monad (MonadPlus, liftM)
import Control.Monad.Base (MonadBase, liftBase) import Control.Monad.Base (MonadBase, liftBase)
import Control.Monad.Reader.Class import Control.Monad.Reader.Class
import Control.Monad.State.Class import Control.Monad.State.Class
import Control.Monad.Trans.Class
import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith, control, liftBaseOp, liftBaseOp_) import Control.Monad.Trans.Control (MonadBaseControl(..), StM, liftBaseWith, control, liftBaseOp, liftBaseOp_)
import Control.Monad.Trans.RWS.Lazy (RWST(..), runRWST) import Control.Monad.Trans.RWS.Lazy (RWST(..), runRWST)
import Control.Monad.Writer.Class import Control.Monad.Writer.Class
@ -73,16 +80,20 @@ defaultState = GhcModState
type GhcModWriter = () type GhcModWriter = ()
---------------------------------------------------------------- ----------------------------------------------------------------
type GhcMod a = GhcModT IO a
newtype GhcMod a = GhcMod { newtype GhcModT m a = GhcModT {
unGhcMod :: RWST GhcModEnv GhcModWriter GhcModState IO a unGhcModT :: RWST GhcModEnv GhcModWriter GhcModState m a
} deriving (Functor } deriving (Functor
, Applicative , Applicative
, Alternative
, Monad , Monad
, MonadPlus
, MonadIO , MonadIO
, MonadReader GhcModEnv , MonadReader GhcModEnv
, MonadWriter GhcModWriter , MonadWriter GhcModWriter
, MonadState GhcModState , MonadState GhcModState
, MonadTrans
) )
#if __GLASGOW_HASKELL__ < 708 #if __GLASGOW_HASKELL__ < 708
@ -92,13 +103,13 @@ instance (Monoid w, MonadIO m) => MonadIO (RWST r w s m) where
#endif #endif
---------------------------------------------------------------- ----------------------------------------------------------------
runGhcModT' :: (MonadIO m, MonadBaseControl IO m)
runGhcMod' :: GhcModEnv => GhcModEnv
-> GhcModState -> GhcModState
-> GhcMod a -> GhcModT m a
-> IO (a,(GhcModState, GhcModWriter)) -> m (a,(GhcModState, GhcModWriter))
runGhcMod' r s a = do runGhcModT' r s a = do
(a', s',w) <- runRWST (unGhcMod $ initGhcMonad (Just libdir) >> a) r s (a',s',w) <- runRWST (unGhcModT $ initGhcMonad (Just libdir) >> a) r s
return (a',(s',w)) return (a',(s',w))
newGhcModEnv :: Options -> FilePath -> IO GhcModEnv newGhcModEnv :: Options -> FilePath -> IO GhcModEnv
@ -111,16 +122,24 @@ newGhcModEnv opt dir = do
, gmCradle = c , gmCradle = c
} }
runGhcMod :: Options -> GhcMod a -> IO a runGhcModT :: (MonadIO m, MonadBaseControl IO m) => Options -> GhcModT m a -> m a
runGhcMod opt action = do runGhcModT opt action = do
env <- liftIO $ newGhcModEnv opt =<< getCurrentDirectory env <- liftBase $ newGhcModEnv opt =<< getCurrentDirectory
(a,(_,_)) <- runGhcMod' env defaultState $ do (a,(_,_)) <- runGhcModT' env defaultState $ do
dflags <- getSessionDynFlags dflags <- getSessionDynFlags
defaultCleanupHandler dflags $ do defaultCleanupHandler dflags $ do
toGhcMod $ initializeFlagsWithCradle opt (gmCradle env) initializeFlagsWithCradle opt (gmCradle env)
action action
return a return a
runGhcMod' :: GhcModEnv
-> GhcModState
-> GhcModT IO a
-> IO (a,(GhcModState, GhcModWriter))
runGhcMod' = runGhcModT'
runGhcMod :: Options -> GhcMod a -> IO a
runGhcMod = runGhcModT
---------------------------------------------------------------- ----------------------------------------------------------------
withErrorHandler :: String -> GhcMod a -> GhcMod a withErrorHandler :: String -> GhcMod a -> GhcMod a
@ -133,7 +152,7 @@ withErrorHandler label = ghandle ignore
exitSuccess exitSuccess
-- | This is only a transitional mechanism don't use it for new code. -- | This is only a transitional mechanism don't use it for new code.
toGhcMod :: Ghc a -> GhcMod a toGhcMod :: (Functor m, MonadIO m) => Ghc a -> GhcModT m a
toGhcMod a = do toGhcMod a = do
s <- gmGhcSession <$> ask s <- gmGhcSession <$> ask
liftIO $ unGhc a $ Session s liftIO $ unGhc a $ Session s
@ -146,30 +165,90 @@ options = gmOptions <$> ask
cradle :: GhcMod Cradle cradle :: GhcMod Cradle
cradle = gmCradle <$> ask cradle = gmCradle <$> ask
instance MonadBase IO GhcMod where
liftBase = GhcMod . liftBase
instance MonadBaseControl IO GhcMod where instance (MonadBaseControl IO m) => MonadBase IO (GhcModT m) where
newtype StM GhcMod a = StGhcMod { liftBase = GhcModT . liftBase
unStGhcMod :: StM (RWST GhcModEnv () GhcModState IO) a }
liftBaseWith f = GhcMod . liftBaseWith $ \runInBase -> instance (MonadBaseControl IO m) => MonadBaseControl IO (GhcModT m) where
f $ liftM StGhcMod . runInBase . unGhcMod newtype StM (GhcModT m) a = StGhcMod {
unStGhcMod :: StM (RWST GhcModEnv () GhcModState m) a }
restoreM = GhcMod . restoreM . unStGhcMod liftBaseWith f = GhcModT . liftBaseWith $ \runInBase ->
f $ liftM StGhcMod . runInBase . unGhcModT
restoreM = GhcModT . restoreM . unStGhcMod
{-# INLINE liftBaseWith #-} {-# INLINE liftBaseWith #-}
{-# INLINE restoreM #-} {-# INLINE restoreM #-}
instance GhcMonad GhcMod where -- GHC cannot prove the following instances to be decidable automatically using
getSession = liftIO . readIORef . gmGhcSession =<< ask -- the FlexibleContexts extension as they violate the second Paterson Condition,
setSession a = liftIO . flip writeIORef a . gmGhcSession =<< ask -- namely that: The assertion has fewer constructors and variables (taken
-- together and counting repetitions) than the head. Specifically the
-- @MonadBaseControl IO m@ constraint is causing this violation.
--
-- Proof of termination:
--
-- Assuming all constraints containing the variable `m' exist and are decidable
-- we show termination by manually replacing the current set of constraints with
-- their own set of constraints and show that this, after a finite number of
-- steps, results in the empty set, i.e. not having to check any more
-- constraints.
--
-- We start by setting the constraints to be those immediate constraints of the
-- instance declaration which cannot be proven decidable automatically for the
-- type under consideration.
--
-- @
-- { MonadBaseControl IO m }
-- @
--
-- Classes used:
--
-- * @class MonadBase b m => MonadBaseControl b m@
--
-- @
-- { MonadBase IO m }
-- @
--
-- Classes used:
--
-- * @class (Applicative b, Applicative m, Monad b, Monad m) => MonadBase b m@
--
-- @
-- { Applicative IO, Applicative m, Monad IO, Monad m }
-- @
--
-- Classes used:
--
-- * @class Monad m@
-- * @class Applicative f => Functor f@
--
-- @
-- { Functor m }
-- @
--
-- Classes used:
--
-- * @class Functor f@
--
-- @
-- { }
-- @
-- ∎
instance (Functor m, MonadIO m, MonadBaseControl IO m)
=> GhcMonad (GhcModT m) where
getSession = (liftIO . readIORef) . gmGhcSession =<< ask
setSession a = (liftIO . flip writeIORef a) . gmGhcSession =<< ask
#if __GLASGOW_HASKELL__ >= 706 #if __GLASGOW_HASKELL__ >= 706
instance HasDynFlags GhcMod where instance (Functor m, MonadIO m, MonadBaseControl IO m)
=> HasDynFlags (GhcModT m) where
getDynFlags = getSessionDynFlags getDynFlags = getSessionDynFlags
#endif #endif
instance ExceptionMonad GhcMod where instance (MonadIO m, MonadBaseControl IO m)
=> ExceptionMonad (GhcModT m) where
gcatch act handler = control $ \run -> gcatch act handler = control $ \run ->
run act `gcatch` (run . handler) run act `gcatch` (run . handler)