Exercises part 3
-
As we have seen in the examples above,
IO
actions working with file handles often come with the risk of failure. We can therefore simplify things by writing some utility functions and a custom bind operator to work with these nested effects. In a new namespaceIOErr
, implement the following utility functions and use these to further cleanup the implementation ofcountEmpty'
:pure : a -> IO (Either e a) fail : e -> IO (Either e a) lift : IO a -> IO (Either e a) catch : IO (Either e1 a) -> (e1 -> IO (Either e2 a)) -> IO (Either e2 a) (>>=) : IO (Either e a) -> (a -> IO (Either e b)) -> IO (Either e b) (>>) : IO (Either e ()) -> Lazy (IO (Either e a)) -> IO (Either e a)
-
Write a function
countWords
for counting the words in a file. Consider usingData.String.words
and the utilities from exercise 1 in your implementation. -
We can generalize the functionality used in
countEmpty
andcountWords
, by implementing a helper function for iterating over the lines in a file and accumulating some state along the way. ImplementwithLines
and use it to reimplementcountEmpty
andcountWords
:covering withLines : (path : String) -> (accum : s -> String -> s) -> (initialState : s) -> IO (Either FileError s)
-
We often use a
Monoid
for accumulating values. It is therefore convenient to specializewithLines
for this case. UsewithLines
to implementfoldLines
according to the type given below:covering foldLines : Monoid s => (path : String) -> (f : String -> s) -> IO (Either FileError s)
-
Implement function
wordCount
for counting the number of lines, words, and characters in a text document. Define a custom record type together with an implementation ofMonoid
for storing and accumulating these values and usefoldLines
in your implementation ofwordCount
.