Real World Haskell Chapter 9 Solutions

Exercise P.221

Is the order in which we call bracket and handle important?

Yup, it is pretty important: the code executed during the in-between statement of bracket still can raise an exception. We do not want our application to crash in case of an error while opening the file, therefore, we still need the handle statement.

We could put the handle statement inside of the bracket statement, but since no exception would be thrown, we would risk to leak some resources.

Exercise P.228

Question 1

How could we traverse the directory three in reverse alphabetic order?

Well, it is quite straightforward, before iterating on directories, we apply the order function on the list containing them. Therefore, we only need to apply reverse as the order function.

ControllerVisit> ControllerVisit.traverse reverse "/home/minoulefou/test"

Question 2

Implement an order function which traverses the tree in postorder.

For each file info type handled by the order function, we have two options:

  1. The file is a directory: we want to first handle its children then handle the directory.
  2. The file is not a directory: we will apply the identity function to this file.

I implemented this recursive function as follows:

postOrder :: [Info] -> [Info]
postOrder (x:xs) = if isDirectory x
  then postOrder xs ++ [x]
  else x:postOrder xs
postOrder [] = []

As you can see, it the implementation is quite straightforward.

There may be some other ones, do not hesitate to shout me out if you found something else.

Question 3

We want to adapt previously implemented predicates with the new Info datatype.

First we need to express InfoP using info:

type InfoP a = Info -> a

We will now implement predicates equality and comparaison: we’ll need to reimplement liftP with the new InfoP type:

liftP :: (a -> b -> c) -> InfoP a -> b -> InfoP c
liftP op infoP test info = infoP info `op` test

Once we implemented this function, implementing equality and inequity predicates is trivial:

greaterP, lesserP :: (Ord a) => InfoP a -> a -> InfoP Bool
greaterP = liftP (>)
lesserP = liftP (<)

equalsP :: (Eq a) => InfoP a -> a -> InfoP Bool
equalsP = liftP (==)

Once again, in order to implement combinators, we’ll use another lift function.

liftP2 :: (a -> b -> c) -> InfoP a -> InfoP b -> InfoP c
liftP2 op pred1 pred2 info = pred1 info `op` pred2 info

andP, orP :: InfoP Bool -> InfoP Bool -> InfoP Bool
andP = liftP2 (&&)
orP = liftP2 (||)

As for the previous question, we’ll use some infix notations for each operation: it is more handy to use.

(==?)::(Eq a) => InfoP a -> a -> InfoP Bool
(==?) = equalsP

(>?), (<?) :: (Ord a) => InfoP a -> a -> InfoP Bool
(>?) = greaterP
(<?) = lesserP

(&&?),(||?):: InfoP Bool -> InfoP Bool -> InfoP Bool
(&&?) = andP
(||?) = orP

Question 4

Write a wrapper for traverse that lets you control traversal using one predicate and filter results using another.

Here, we want to add a behaviour to the ord function. Not only we want that function to order the results, be we also want it to filter them. An easy approach to this problem seems to be composing the filter function with the order function:

traverse' order pred path = ControllerVisit.traverse (order . filter pred) path

Exercise P. 232

Question 1

Modify foldtree to allow the caller to change the order of traversal of entries in a directory.

First, we need to specify the new fold function signature:

foldTree' ::([FilePath] -> [FilePath]) -> Iterator a -> a -> FilePath -> IO a
foldTree' order iter initSeed path = ...

We will plug this function to the existing foldTree on the fold subfunction as follow:

fold seed subpath = do 
  paths <- getUsefulContent subpath
  walk seed $ order paths

The remaining code will still unchanged.

Aaaand, the two remaining exercises are quite similar to the previous ones, I will pass on that :)

See you soon for chapter 10 solutions!

Bye Bye