Exercises part 3
-
As we have seen in the examples above,
IOactions 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
countWordsfor counting the words in a file. Consider usingData.String.wordsand the utilities from exercise 1 in your implementation. -
We can generalize the functionality used in
countEmptyandcountWords, by implementing a helper function for iterating over the lines in a file and accumulating some state along the way. ImplementwithLinesand use it to reimplementcountEmptyandcountWords:covering withLines : (path : String) -> (accum : s -> String -> s) -> (initialState : s) -> IO (Either FileError s) -
We often use a
Monoidfor accumulating values. It is therefore convenient to specializewithLinesfor this case. UsewithLinesto implementfoldLinesaccording to the type given below:covering foldLines : Monoid s => (path : String) -> (f : String -> s) -> IO (Either FileError s) -
Implement function
wordCountfor counting the number of lines, words, and characters in a text document. Define a custom record type together with an implementation ofMonoidfor storing and accumulating these values and usefoldLinesin your implementation ofwordCount.