diff options
| author | Jan Sucan <jan@jansucan.com> | 2025-08-30 20:35:55 +0200 |
|---|---|---|
| committer | Jan Sucan <jan@jansucan.com> | 2025-08-30 20:35:55 +0200 |
| commit | 4c79365adbe603f679f0fb8e223bbb5755a6e500 (patch) | |
| tree | a87120a9f576697e87de67ceae15a3cf7480bf55 | |
| parent | 8db2f4fda69c213f0083eab0cfd7cd5c0ffce36a (diff) | |
18_a_1: Add solution
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | ch18/18_a_1.hs | 68 |
2 files changed, 69 insertions, 1 deletions
@@ -176,7 +176,7 @@ are prefixed with 'Module_'. | 16_a_2 | yes | | | | 16_a_3 | yes | | | | 16_a_4 | yes | | | -| **_18_a_1_** | | 436 | 18. Monad transformers | +| **_18_a_1_** | yes | 436 | 18. Monad transformers | | 18_a_2 | | | | | 18_a_3 | | | | | **_18_b_1_** | | 441 | | diff --git a/ch18/18_a_1.hs b/ch18/18_a_1.hs new file mode 100644 index 0000000..e05c2bb --- /dev/null +++ b/ch18/18_a_1.hs @@ -0,0 +1,68 @@ +-- Modify the App type synonym to swap the order of ReaderT and WriterT. What +-- effect does this have on the runApp execution function? + + +-- Typo in the assignment: There should be StateT instead of WriterT. WriterT is +-- added in the next exercise. + + +-- In the execution function, the runStateT and runReaderT functions have to be +-- swapped as well. + + +{-- From examples/examples/ch18/UglyStack.hs and modified --} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} + +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 + +data AppConfig = AppConfig { + cfgMaxDepth :: Int + } deriving (Show) + +data AppState = AppState { + stDeepestReached :: Int + } deriving (Show) + + +type App = StateT AppState (ReaderT AppConfig IO) + +runApp :: App a -> Int -> IO (a, AppState) +runApp k maxDepth = + let config = AppConfig maxDepth + state = AppState 0 + in runReaderT (runStateT k state) config + +constrainedCount :: Int -> FilePath -> App [(FilePath, Int)] +constrainedCount curDepth path = do + contents <- liftIO . listDirectory $ path + cfg <- ask + rest <- 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 [] + return $ (path, length contents) : concat rest +{-- End of code from examples --} + + +-- ghci> :l 18_a_1.hs +-- [1 of 3] Compiling CountEntries ( CountEntries.hs, interpreted ) +-- [2 of 3] Compiling Main ( 18_a_1.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}) |
