Scrapbook-0.4.0: code examples
Copyright© Frank Jung 2020
LicenseGPL-3
Safe HaskellSafe-Inferred
LanguageHaskell2010

CountEntries

Description

Function variations of counting directory entries from Chapter 18, Monad Transformers, Real World Haskell by Bryan O'Sullivan, Don Stewart, and John Goerzen. The original version in the book is countEntriesTrad which here is called countEntries0.

The Monad Transformer version which in the book is countEntries is countEntries2 in this code.

The other versions countEntries1 and countEntries3 are my variations of this code.

GHCi Session

To test these functions in GHCi you need the following packages:

:m + Control.Monad
:m + System.Directory
:m + System.FilePath

Resources

This is a really good introduction to Monad Transformers, Monday Morning Haskell: Monad Transformers.

Synopsis

Documentation

countEntries0 :: FilePath -> IO [(FilePath, Int)] Source #

Count entries in directories for given path.

This is the standard version from "Real World Haskell".

>>> p = "public"
>>> :t listDirectory p
listDirectory p :: IO [FilePath]
>>> countEntries0 p
[("public",25),("public/src",16)]

countEntries1 :: FilePath -> IO [(FilePath, Int)] Source #

Count entries for a list of paths. (My version.)

Example

What the function returns is a list of tuples of directory and count of entries in that directory:

>>> countEntries1 "public"
[("public",25),("public/src",16)]

Explanation

The function composes a number of different system calls. But the process is simple but clunky using traditional methods. The process will be much simplified once when Monad Transformers are used in countEntries2.

>>> p = "public"

Get contents of path ... and filter to report directories only:

>>> (getDirectoryContents p) >>= filterM (\n -> doesDirectoryExist (p </> n))
["..","src","."]

List directory ignores current and parent directories:

>>> listDirectory p >>= filterM (\n -> doesDirectoryExist (p </> n))
["src"]

Some ways to count number of entries in the path:

>>> ps <- listDirectory p
>>> length ps
25

Same as:

>>> listDirectory p >>= return . length
25

Which is equivalent to:

>>> liftM length (listDirectory p)
25

Recurse into subdirectories:

>>> listDirectory p >>= filterM (\n -> doesDirectoryExist (p </> n)) >>= mapM_ print
"src"

countEntries2 :: FilePath -> IO [(FilePath, Int)] Source #

My updated version to count entries in directories for given path.

This updated version of countEntries1 uses WriterT.

countEntries3 :: FilePath -> IO [(FilePath, Int)] Source #

Count entries in directories for given path.

My version using WriterT.

This function takes a FilePath argument and returns an IO action that, when executed, returns a list of (FilePath, Int) pairs representing the number of entries in each directory.

The implementation of countEntries3 uses the execWriterT function from the Writer module to extract the final value of the writer monad and discard the log. The writer monad is used to accumulate a list of (FilePath, Int) pairs representing the number of entries in each directory.

The countEntries3 function is defined in terms of a helper function go that uses WriterT to accumulate the log of (FilePath, Int) pairs. The go function first uses listDirectory to get the contents of the directory specified by the FilePath argument. It then uses tell to add a tuple of the directory path and the number of entries in the directory to the log. Next, it uses filterM and doesDirectoryExist to get a list of sub-directories in the directory. Finally, it uses mapM_ to recurse into each sub-directory and accumulate the log.

The countEntries3 function is defined using function composition, where go is composed with execWriterT using the . operator. This creates a new function that first applies go to its input, and then applies execWriterT to the output of go.