2013-08-29 3 views
3

Я читаю о трансформаторах монады в Real World Haskell. В следующем примере стек равен Writer сверху State поверх Reader поверх IO.Monad transformer - Явный подъем

{-# 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 и MonadState и классов типов есть случаи, такие как MonadWriter (StateT s m) и так далее.

Что я не понять, почему я не могу указать lift действие со слоя ниже до текущего монадного трансформатора. В constrainedCount Я нахожусь в монаде читателя, если я правильно понимаю, и я думал, что должны работать и st <- get, и st <- lift get. .. (И это tell и лифт лифта сказать should be the same). If I change ул < - получить to ул < - подъемную get` я получаю ошибку

Couldn't match type `t0 m0' with `MyApp' 
Expected type: MyApp() 
Actual type: t0 m0() 

, который говорит мне, очень мало ... Это мое понимание этого совершенно неправильно

?
+0

IIRC, 'get' не принимает никаких параметров, поэтому' lift. get' не будет работать, потому что '(.)' принимает две функции и составляет их. Вы пробовали 'lift get' без композиции? – bheklilr

+0

Ух, мой плохой. Благодаря! Я обновил этот вопрос сейчас. – beta

+0

Чтобы (возможно) ответить на ваш вопрос, я думаю, это потому, что он завернут в новый тип. Я не знаю, почему вы хотите явно «поднять» действия. – bheklilr

ответ

9

Давайте посмотрим на тип lift get:.

lift get :: (MonadTrans t, MonadState a m) => t m a 

Но ваш MyApp не монада трансформатор, это только монада Но что Внутри, конечно, так, если вы используете

st <- MyA $ lift get 

это работает.

+2

Ах. Правильно. Это * имеет смысл. Если я перейду с «MyApp» на 'type App = WriterT [(FilePath, Int)] (StateT AppState (ReaderT AppConfig IO)), он работает так, как ожидалось, и я могу использовать, например. 'lift get'. – beta

Смежные вопросы