10. Trees

File systems are one common application that uses a tree representation. We define the following data type to model a very simplistic file system:

data FTree = Dir String [FTree] | File String deriving (Show, Eq)
The deriving (Show, Eq) at the end offer a default enrollment into the Show and Eq type classes, which allows us to represent our filesystem as a string using show and to compare two filesystems using == and /=.

Each node is a filesystem entry; directory nodes have a name and a list of children (its contents: other directories or files) and regular files are leaves (no children) characterized by their name.

Here is an example filesystem instance:

t = Dir "/" [Dir "usr" [Dir "share" [],
                        Dir "var" [],
                        Dir "include" [File "stdio.h", File "string.h", File "stdlib.h"]],
             Dir "dev" [File "sda", File "sda1", File "sda2"],
             Dir "etc" [File "sudoers", File "passwd", File "shadow"],
             Dir "home" [Dir "mihai" [File ".zshrc", Dir "sol" [File "Lab10.hs"]],
                         Dir "student" [File ".bashrc", Dir "sol" []]]]

We also define a Path to be a list of names, representing a way through the tree, from the root to a particular node.

type Path = [String]
 
path1 = ["/", "usr", "include", "stdio.h"]
path2 = ["/", "home", "student"]
wrongPath1 = ["/", "usr", "include", "math.h"]
wrongPath2 = ["/", "sbin"]

10.1 Write a function countFiles :: FTree → Int that counts how many regular files are there in a filesystem.

10.2 Write a function longestPath :: FTree → Int which gives the length of the longest path in the filesystem.

10.3 Write a function findEntry :: String → FTree → Bool which takes the name of an entry (dir or file) and a filesystem and determines whether that entry can be found in the filesystem.

10.4 Write a function checkExists :: Path → FTree → Bool which takes a path and a filesystem and determines whether there exists an entry with that path.

10.5 Write a function checkIsFile :: Path → FTree → Bool which takes a path and a filesystem and determines whether there exists a regular file with that path (if the path exists, but it's a dir, the result should be False).

10.6 Write a function checkIsDir :: Path → FTree → Bool which takes a path and a filesystem and determines whether there exists a directory with that path (if the path exists, but it's a regular file, the result should be False).

10.7 Write a function touch :: Path → FTree → FTree which takes a path and a filesystem and produces a new filesystem. If the path existed in the original, nothing changes. If the path didn't exist in the original, the new filesystem will contain a regular file at that path.

10.8 Write a function mkdir :: Path → FTree → FTree which takes a path and a filesystem and produces a new filesystem. If the path existed in the original, nothing changes. If the path didn't exist in the original, the new filesystem will contain a directory at that path.

10.9 Write a function rm :: Path → FTree → FTree which takes a path and a filesystem and produces a new filesystem without the entry at that path.

10.10 Write a function longestPath2 :: FTree → Path which takes a filesystem and returns the longest path.

10.11 Write a function findEntry2 :: String → FTree → Path which takes the name of an entry (dir or file) and a filesystem and determines whether that entry can be found in the filesystem. If it doesn't exist, an empty path [] is returned.