我正在阅读 Real World Haskell 中的monad变换器。在以下示例中,堆栈Writer
位于State
之上的Reader
之上IO
。
{-# Language GeneralizedNewtypeDeriving #-}
import Control.Monad
import Control.Monad.State
import Control.Monad.Reader
import Control.Monad.Writer
import System.Directory
import System.FilePath
data AppConfig = AppConfig {
cfgMaxDepth :: Int
} deriving Show
data AppState = AppState {
stDeepestReached :: Int
} deriving Show
newtype MyApp a = MyA {
runA :: WriterT [(FilePath,Int)] (StateT AppState (ReaderT AppConfig IO)) a
} deriving (Monad, MonadIO, Functor, MonadReader AppConfig,
MonadWriter [(FilePath,Int)], MonadState AppState)
runApp :: MyApp a -> Int -> IO ([(FilePath,Int)], AppState)
runApp k maxDepth = let config = AppConfig maxDepth
state' = AppState 0
in runReaderT (runStateT (execWriterT $ runA k) state') config
constrainedCount :: Int -> FilePath -> MyApp ()
constrainedCount curDepth path = do
contents <- liftIO . getDirectoryContents $ path
cfg <- ask
let maxDepth = cfgMaxDepth cfg
tell [(path,curDepth)]
forM_ (filter (\d' -> d' /= ".." && d' /= ".") contents) $ \d -> do
let newPath = path </> d
isDir <- liftIO $ doesDirectoryExist newPath
when (isDir && curDepth < maxDepth) $ do
let newDepth = curDepth+1
st <- get
when (stDeepestReached st < newDepth) $
put st { stDeepestReached = newDepth }
constrainedCount newDepth newPath
main = runApp (constrainedCount 0 "/tmp") 2 >>= print
我(我想)了解我如何简单地致电ask
,get
和put
,因为这些是MonadReader
,MonadWriter
和{ {1}}类型类,有MonadState
等实例。
我不理解的是为什么我不能明确MonadWriter (StateT s m)
从下面的层到当前monad变换器的动作。在lift
我是读者monad,如果我理解正确,我认为constrainedCount
和st <- get
都应该有用。 (并st <- lift get
并举起电梯。告诉tell
st&lt; - get should be the same). If I change
st&lt; - lift get`我收到错误
to
告诉我很少......我对此的理解是完全错误的吗?
答案 0 :(得分:9)
让我们看一下lift get
的类型:
lift get :: (MonadTrans t, MonadState a m) => t m a
但你的MyApp
不是monad变换器,它只是一个monad。但当然,内在的是,如果你使用
st <- MyA $ lift get
它有效。