Online Book Reader

Home Category

Learn You a Haskell for Great Good! - Miran Lipovaca [163]

By Root 551 0

Manipulating a Filesystem


Now that we can navigate our filesystem, manipulating it is easy. Here’s a function that renames the currently focused file or folder:

fsRename :: Name -> FSZipper -> FSZipper

fsRename newName (Folder name items, bs) = (Folder newName items, bs)

fsRename newName (File name dat, bs) = (File newName dat, bs)

Let’s rename our "pics" folder to "cspi":

ghci> let newFocus = (myDisk, []) -: fsTo "pics" -: fsRename "cspi" -: fsUp

We descended to the "pics" folder, renamed it, and then moved backup.

How about a function that makes a new item in the current folder? Behold:

fsNewFile :: FSItem -> FSZipper -> FSZipper

fsNewFile item (Folder folderName items, bs) =

(Folder folderName (item:items), bs)

Easy as pie. Note that this would crash if we tried to add an item but were focusing on a file instead of a folder.

Let’s add a file to our "pics" folder, and then move back up to the root:

ghci> let newFocus =

(myDisk, []) -: fsTo "pics" -: fsNewFile (File "heh.jpg" "lol") -: fsUp

What’s really cool about all this is that when we modify our filesystem, our changes are not actually made in place, but instead, the function returns a whole new filesystem. That way, we have access to our old filesystem (in this case, myDisk), as well as the new one (the first component of newFocus).

By using zippers, we get versioning for free. We can always refer to older versions of data structures, even after we’ve changed them. This isn’t unique to zippers, but it is a property of Haskell, because its data structures are immutable. With zippers, however, we get the ability to easily and efficiently walk around our data structures, so the persistence of Haskell’s data structures really begins to shine.

Watch Your Step

So far, while walking through our data structures—whether they were binary trees, lists, or filesystems—we didn’t really care if we took a step too far and fell off. For instance, our goLeft function takes a zipper of a binary tree and moves the focus to its left subtree:

goLeft :: Zipper a -> Zipper a

goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs)

But what if the tree we’re stepping off from is an empty tree? What if it’s not a Node, but an Empty? In this case, we would get a runtime error, because the pattern match would fail, and we have not made a pattern to handle an empty tree, which doesn’t have any subtrees.

So far, we just assumed that we would never try to focus on the left subtree of an empty tree, as its left subtree doesn’t exist. But going to the left subtree of an empty tree doesn’t make much sense, and so far we’ve just conveniently ignored this.

Or what if we are already at the root of some tree and don’t have any breadcrumbs but still try to move up? The same thing would happen. It seems that when using zippers, any step could be our last (cue ominous music). In other words, any move can result in a success, but it can also result in a failure. Does that remind you of something? Of course: monads! More specifically, the Maybe monad, which adds a context of possible failure to normal values.

Let’s use the Maybe monad to add a context of possible failure to our movements. We’re going to take the functions that work on our binary tree zipper and make them into monadic functions.

First, let’s take care of possible failure in goLeft and goRight. So far, the failure of functions that could fail was always reflected in their result, and this example is no different.

Here are goLeft and goRight with an added possibility of failure:

goLeft :: Zipper a -> Maybe (Zipper a)

goLeft (Node x l r, bs) = Just (l, LeftCrumb x r:bs)

goLeft (Empty, _) = Nothing

goRight :: Zipper a -> Maybe (Zipper a)

goRight (Node x l r, bs) = Just (r, RightCrumb x l:bs)

goRight (Empty, _) = Nothing

Now, if we try to take a step to the left of an empty tree, we get a Nothing!

ghci> goLeft (Empty, [])

Nothing

ghci> goLeft (Node 'A' Empty Empty, [])

Just (Empty,[LeftCrumb 'A' Empty])

Looks good! How about going up? The problem before happened if we tried to go up but we didn’t have

Return Main Page Previous Page Next Page

®Online Book Reader