monad-controlのMonadTransControlとMonadBaseControlについて
monad-controlパッケージにはMonadTransControlとMonadBaseControlがありますが、どのような使い分けをするのが正しいのでしょうか?
http://maoe.hatenadiary.jp/entry/20111207/1323185162
には
簡単に解説すると、MonadTransControlクラスのliftWith :: Monad m => (Run t -> m a) ->
t m aはモナドスタックt m aを一つpopした型m
aを弄れるようにする役割を持ち、MonadBaseControlのliftBaseWith :: (RunInBase m b -> b
a) -> m aは、スタックの最下部までpopしたベースモナドの型b aを弄れるようにする役割を持っています。
とかいてありますが、どちらも一番下まで持ち上げなければなりませんでした。
MonadTransControlはMonadBaseControlを定義するための補助なのでしょうか?
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
import Control.Monad.Base
import Control.Monad.Reader
import Control.Monad.State
import Control.Applicative
import Control.Monad.Trans.Control
import Control.Monad.Identity
import Control.Monad.Except
import Control.Monad.Trans.Except
import Data.Maybe
import qualified Data.Map as Map
type Name = String
type Value = String
type Env = Map.Map Name Value
-- EvalRの内部
newtype EvalE m a = EvalE {runEvalE :: ExceptT String m a}
deriving (Functor
, Applicative
, Monad
, MonadError String
, MonadBase base
)
instance MonadTrans EvalE where
lift = EvalE . lift
instance MonadTransControl EvalE where
type StT EvalE a = StT (ExceptT String) a
liftWith = defaultLiftWith EvalE runEvalE
restoreT = defaultRestoreT EvalE
instance (MonadBaseControl b m) => MonadBaseControl b (EvalE m) where
type StM (EvalE m) a = ComposeSt EvalE m a
liftBaseWith = defaultLiftBaseWith
restoreM = defaultRestoreM
evalET :: (Monad (t []), MonadTransControl t) => t [] Int
evalET = return 1
evalEM :: (MonadBaseControl [] m) => m Int
evalEM = return 1
doEvalET :: EvalE [] Int -> EvalE [] Int
doEvalET t = liftWith (\run -> run t) >>= restoreT . return
doEvalEM :: EvalE [] Int -> EvalE [] Int
doEvalEM m = liftBaseWith (\run -> run m) >>= restoreM
exEvalE :: EvalE [] Int -> IO ()
exEvalE = print . runEvalE
exEvalET :: EvalE [] Int -> IO ()
exEvalET = print . runEvalE . doEvalET
exEvalEM :: EvalE [] Int -> IO ()
exEvalEM = print . runEvalE . doEvalEM
newtype EvalR m a = EvalR {runEvalR :: ReaderT Env (EvalE m) a}
deriving (Functor
, Applicative
, Monad
, MonadError String
, MonadReader Env
, MonadBase base
)
instance MonadTrans EvalR where
lift = EvalR . lift . lift
-- 全部持ち上げないとだめだった
-- 本当は1つだけ持ち上げてEvalEにしかかった
instance MonadTransControl EvalR where
type StT EvalR a = StT EvalE (StT (ReaderT Env) a)
liftWith f = EvalR $ liftWith $ \runReader ->
liftWith $ \runEval ->
f $ runEval . runReader . runEvalR
restoreT = EvalR . restoreT . restoreT
instance (MonadBaseControl b m) => MonadBaseControl b (EvalR m) where
type StM (EvalR m) a = ComposeSt EvalR m a
liftBaseWith = defaultLiftBaseWith
restoreM = defaultRestoreM
env :: Env
env = Map.fromList [("0", "zero"), ("1", "one")]
doEvalRT :: EvalR [] Int -> EvalR [] Int
doEvalRT t = do
x <- liftWith $ \r ->
r t
restoreT $ return x
doEvalRM :: EvalR [] Int -> EvalR [] Int
doEvalRM m = liftBaseWith (\run -> run m) >>= restoreM
exEvalR :: EvalR [] Int -> IO ()
exEvalR = print . runEvalE . (flip runReaderT env) . runEvalR
exEvalRT :: EvalR [] Int -> IO ()
exEvalRT = print . runEvalE . (flip runReaderT env) . runEvalR . doEvalRT
exEvalRM :: EvalR [] Int -> IO ()
exEvalRM = print . runEvalE . (flip runReaderT env) . runEvalR . doEvalRM
それとも型がt m aとm aで型が違うのでうまく使い分けるのでしょうか?
maybeTIO :: (Monad (t IO), MonadTransControl t) => t IO Int
maybeTIO = lift (putStrLn "testMaybeT") >> return 1
maybeMIO :: (MonadBaseControl IO m) => m Int
maybeMIO = liftBase (putStrLn "testMaybeT") >> return 1
twiceIO :: IO a -> IO a
twiceIO action = action >> action
testLiftWith :: MaybeT IO Int -> MaybeT IO Int
testLiftWith f = liftWith (\run -> twiceIO (run f)) >>= restoreT . return
testLiftBaseWith :: MaybeT IO Int -> MaybeT IO Int
testLiftBaseWith f = liftBaseWith (\run -> twiceIO (run f)) >>= restoreM
testControl :: MaybeT IO Int -> MaybeT IO Int
testControl f = control (\run -> twiceIO (run maybeTIO))