-
-
Save guibou/900da1e3740b7b3804f4d6b6778c4cb8 to your computer and use it in GitHub Desktop.
Help on reddit for: https://www.reddit.com/r/haskell/comments/7imf2x/iostate_monad_code_sample_for_review/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{- | |
Functionality: | |
- Read all folders and sub-folders with structure like below | |
- store all folder and file info in State monad, | |
- at the end of the loop, print it. | |
├── aaa | |
│ ├── b | |
│ │ ├── b1 | |
│ │ │ ├── b1-1 | |
│ │ │ └── b1-2 | |
│ │ └── b2 | |
│ │ └── 2-1 | |
│ │ └── b2-1-1 | |
│ └── c | |
│ ├── c1 | |
│ └── c2 | |
Goal: to practice using State and IO monad together by leveraging liftIO and unsafePerformIO | |
Would appreciate any correction, better way to do it or generic advices | |
-} | |
module Main where | |
import Control.Monad.State | |
import System.Directory | |
import System.Exit | |
import System.FilePath | |
import Data.Time | |
import Control.Monad.Extra (partitionM) | |
main :: IO ([Dir], [File]) | |
main | |
= do | |
let root = "/tmp/poulet" | |
s@(d, f) <- k root | |
putStrLn $ "Root: " ++ root | |
pretty d True "DIR:" | |
pretty f True "FILE:" | |
return s | |
-- | Order is arbitrary | |
pretty :: Show a => [a] -> Bool -> String -> IO () | |
pretty src rev prefix = do | |
putStrLn $ "\n" ++ prefix ++ "\n" | |
if rev | |
then mapM_ print $ reverse src | |
else mapM_ print src | |
data File = File | |
{ name :: String | |
, date :: UTCTime | |
, size :: Integer | |
, content :: String | |
} deriving (Show) | |
data Dir = Dir | |
{ dirName :: String | |
, dirDate :: UTCTime | |
, dirContent :: [FilePath] | |
} deriving (Show) | |
k :: String -> IO ([Dir], [File]) | |
k root = do | |
isDir <- doesDirectoryExist root | |
unless isDir $ die "not a dir" | |
execStateT (list root) ([], []) | |
list :: String -> StateT ([Dir], [File]) IO () | |
list path = do | |
-- walk current directory | |
dir <- liftIO $ do | |
children <- listDirectory path | |
dt <- getModificationTime path | |
return Dir {dirName = path, dirContent = children, dirDate = dt} | |
modify (\(ds, fs) -> (dir : ds, fs)) | |
-- remove unwanted files and prepend directory name | |
let | |
fullPaths = map (path</>) (dirContent dir) | |
filteredFiles = filter (/=".DS_Store") fullPaths | |
-- Split between sub directories and sub files | |
(subDirs, subFiles) <- liftIO (partitionM doesDirectoryExist filteredFiles) | |
-- recursivly walk sub directories | |
forM_ subDirs list | |
-- handle files in the current directory | |
forM_ subFiles $ \f -> do | |
file <- liftIO $ do | |
s <- getFileSize f | |
dt <- getModificationTime f | |
c <- readFile f | |
return File {name = f, date = dt, size = s, content = c} | |
modify (\(ds, fs) -> (ds, file:fs)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment