aboutsummaryrefslogtreecommitdiff
path: root/ch18/18_a_2.hs
blob: e3ae316d18ca203845208872a839a0ce81fe9e34 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
-- 1. Add the WriterT transformer to the App monad transformer stack. Modify
--    runApp to work with this new setup.
--
-- 2. Rewrite the constrainedCount function to record results using the WriterT
--    transformer in your new App stack.


{-- From examples/examples/ch18/UglyStack.hs and modified --}
import CountEntries (listDirectory)

-- System.Directory also has listDirectory function. Hide it to not conflict
-- with our version from CountEntries module.
import System.Directory hiding (listDirectory)
import System.FilePath
import Control.Monad -- Needed for the forM_ and 'when'
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Writer

data AppConfig = AppConfig {
      cfgMaxDepth :: Int
    } deriving (Show)

data AppState = AppState {
      stDeepestReached :: Int
    } deriving (Show)


type App = WriterT [(FilePath, Int)] (ReaderT AppConfig (StateT AppState IO)) ()

runApp :: App -> Int -> IO (((), [(FilePath, Int)]), AppState)
runApp k maxDepth =
    let config = AppConfig maxDepth
        state = AppState 0
    in runStateT (runReaderT (runWriterT k) config) state

constrainedCount :: Int -> FilePath -> App
constrainedCount curDepth path = do
  contents <- liftIO . listDirectory $ path
  tell [(path, length contents)]
  cfg <- ask
  forM_ contents $ \name -> do
    let newPath = path </> name
    isDir <- liftIO $ doesDirectoryExist newPath
    if isDir && curDepth < cfgMaxDepth cfg
      then do
        let newDepth = curDepth + 1
        st <- get
        when (stDeepestReached st < newDepth) $
          put st { stDeepestReached = newDepth }
        constrainedCount newDepth newPath
      else return ()
{-- End of code from examples --}


-- ghci> :l 18_a_2.hs
-- [1 of 3] Compiling CountEntries     ( CountEntries.hs, interpreted )
-- [2 of 3] Compiling Main             ( 18_a_2.hs, interpreted )
-- Ok, two modules loaded.

-- ghci> runApp (constrainedCount 0 "../ch08") 2
-- (((),[("../ch08",12),("../ch08/test-8_b_3",3),("../ch08/test-8_b_3/dir1",7),("../ch08/test-8_b_3/dir2",1)]),AppState {stDeepestReached = 2})