aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Sucan <jan@jansucan.com>2025-08-30 20:35:55 +0200
committerJan Sucan <jan@jansucan.com>2025-08-30 20:35:55 +0200
commit4c79365adbe603f679f0fb8e223bbb5755a6e500 (patch)
treea87120a9f576697e87de67ceae15a3cf7480bf55
parent8db2f4fda69c213f0083eab0cfd7cd5c0ffce36a (diff)
18_a_1: Add solution
-rw-r--r--README.md2
-rw-r--r--ch18/18_a_1.hs68
2 files changed, 69 insertions, 1 deletions
diff --git a/README.md b/README.md
index c14bd63..3fdd6d1 100644
--- a/README.md
+++ b/README.md
@@ -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})