Let's have a look at the type of lift get
:
lift get :: (MonadTrans t, MonadState a m) => t m a
But your MyApp
isn't a monad transformer, it's only a monad. But what's inside is, of course, so if you use
st <- MyA $ lift get
it works.
Pergunta
I'm reading about monad transformers in Real World Haskell. In the following example, the stack is Writer
on top State
on top of Reader
on top of 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
I (think I) understand how I can simply call ask
, get
and put
since these are defined in the MonadReader
, MonadWriter
and MonadState
typeclasses and there are instances such as MonadWriter (StateT s m)
and so on.
What I don't understand is why I cannot explicit lift
an action from the layer below up to the current monad transformer. In constrainedCount
I'm in the Reader monad, if I understand correctly, and I thought both st <- get
and st <- lift get
should work. (And that tell
and lift . lift . tellshould be the same). If I change
st <- getto
st <- lift get` I get the error
Couldn't match type `t0 m0' with `MyApp'
Expected type: MyApp ()
Actual type: t0 m0 ()
which tells me very little... Is my understanding of this completely wrong?
Solução
Let's have a look at the type of lift get
:
lift get :: (MonadTrans t, MonadState a m) => t m a
But your MyApp
isn't a monad transformer, it's only a monad. But what's inside is, of course, so if you use
st <- MyA $ lift get
it works.