<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Tom Moertel's Weblog: Directory-tree printing in Haskell, part two: refactoring</title>
    <link>http://blog.moertel.com/articles/2007/03/07/directory-tree-printing-in-haskell-part-two-refactoring</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Quality rants on programming theory and stuff geeks like</description>
    <item>
      <title>Directory-tree printing in Haskell, part two: refactoring</title>
      <description>&lt;p&gt;In &lt;a href="http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell"&gt;my previous article on writing a simple directory-tree printer in
Haskell&lt;/a&gt;,
I wrote a small program to recursively scan file-system
directories and print their contents as &lt;span class="caps"&gt;ASCII&lt;/span&gt;-art trees.  The
program made for an approachable example of how to use Haskell for
&amp;#8220;imperative&amp;#8221; tasks, but it has a few problems.&lt;/p&gt;


	&lt;p&gt;First, the directory-scanning logic and tree-printing logic are
intertwined.  Neither is reusable.  Second, both bits of logic are
rigid, specialized for this particular task.  Even if you could
reuse them, you wouldn&amp;#8217;t &lt;em&gt;want&lt;/em&gt; to.&lt;/p&gt;


	&lt;p&gt;In this article, the second in a series, we will explore ways to make
our original code more reusable.  We will separate the directory
scanning from the tree printing, harness the power of some old
friends from Haskell&amp;#8217;s libraries, and think about the costs
and benefits of our changes.&lt;/p&gt;


	&lt;h3&gt;The plan&lt;/h3&gt;


	&lt;p&gt;Recall our original directory tree&amp;#8211;listing solution, the
core of which I will reprint below:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;tlist&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='varid'&gt;visit&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='keyword'&gt;if&lt;/span&gt; &lt;span class='str'&gt;"/"&lt;/span&gt; &lt;span class='varop'&gt;`isPrefixOf`&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='keyword'&gt;then&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='keyword'&gt;else&lt;/span&gt; &lt;span class='str'&gt;"."&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt;

&lt;span class='varid'&gt;visit&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='varid'&gt;tie&lt;/span&gt; &lt;span class='varid'&gt;arm&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='varid'&gt;putStrLn&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;arm&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;tie&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
    &lt;span class='varid'&gt;visitChildren&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='str'&gt;"/"&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;extension&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
  &lt;span class='keyword'&gt;where&lt;/span&gt;
    &lt;span class='varid'&gt;extension&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;case&lt;/span&gt; &lt;span class='varid'&gt;arm&lt;/span&gt; &lt;span class='keyword'&gt;of&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt;  &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt;&lt;span class='layout'&gt;;&lt;/span&gt; &lt;span class='str'&gt;"`"&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='str'&gt;"    "&lt;/span&gt;&lt;span class='layout'&gt;;&lt;/span&gt; &lt;span class='keyword'&gt;_&lt;/span&gt;   &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='str'&gt;"|   "&lt;/span&gt;

&lt;span class='varid'&gt;visitChildren&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='varid'&gt;whenM&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;doesDirectoryExist&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
        &lt;span class='varid'&gt;contents&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;getDirectoryContents&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt;
            &lt;span class='varop'&gt;`catch`&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='keyglyph'&gt;\&lt;/span&gt;&lt;span class='varid'&gt;e&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='varid'&gt;show&lt;/span&gt; &lt;span class='varid'&gt;e&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
        &lt;span class='keyword'&gt;let&lt;/span&gt; &lt;span class='varid'&gt;visibles&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;sort&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;filter&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varop'&gt;`notElem`&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='str'&gt;"."&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='str'&gt;".."&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;contents&lt;/span&gt;
            &lt;span class='varid'&gt;arms&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;replicate&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;length&lt;/span&gt; &lt;span class='varid'&gt;visibles&lt;/span&gt; &lt;span class='comment'&gt;-&lt;/span&gt; &lt;span class='num'&gt;1&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='str'&gt;"|"&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='str'&gt;"`"&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
        &lt;span class='varid'&gt;zipWithM_&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;visit&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='str'&gt;"-- "&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;arms&lt;/span&gt; &lt;span class='varid'&gt;visibles&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;The &lt;em&gt;tlist&lt;/em&gt; function kicks off the process for a particular
file-system &lt;em&gt;path&lt;/em&gt;, handing off to &lt;em&gt;visit&lt;/em&gt; which recursively descends the
directory tree from the root node.  The &lt;em&gt;visit&lt;/em&gt; function calls
&lt;em&gt;visitChildren&lt;/em&gt; to expand the subtree, if any, for each node visited.
The &lt;em&gt;visitChildren&lt;/em&gt; function, in turn, calls back to &lt;em&gt;visit&lt;/em&gt; to
repeat the process for each child in the subtree.
In effect, we are traversing the tree rooted at &lt;em&gt;path&lt;/em&gt;, printing each
node in passing.&lt;/p&gt;


	&lt;p&gt;To separate the traversal part from the printing part, we will
introduce a tree data structure.  The file system&amp;#8211;traversal code
will emit a tree, and the tree-showing code will consume a tree.  We
will rewrite our old &lt;em&gt;tlist&lt;/em&gt; function, which we might as well rename to
the more descriptive &lt;em&gt;traverseAndPrint&lt;/em&gt;, to glue the two pieces
together with the tree serving as glue:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;traverseAndPrint&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Path&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;span class='varid'&gt;traverseAndPrint&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='varid'&gt;tree&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;fsTraverse&lt;/span&gt; &lt;span class='varid'&gt;root&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt;
    &lt;span class='varid'&gt;putStrLn&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;showTree&lt;/span&gt; &lt;span class='varid'&gt;tree&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
  &lt;span class='keyword'&gt;where&lt;/span&gt;
    &lt;span class='varid'&gt;root&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;if&lt;/span&gt; &lt;span class='str'&gt;"/"&lt;/span&gt; &lt;span class='varop'&gt;`isPrefixOf`&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='keyword'&gt;then&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='keyword'&gt;else&lt;/span&gt; &lt;span class='str'&gt;"."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;That&amp;#8217;s the plan.  Now let&amp;#8217;s carry it out.&lt;/p&gt;&lt;h3&gt; Step 1:  Introduce the intermediate tree data structure&lt;/h3&gt;


	&lt;p&gt;To make our desired separation possible, let&amp;#8217;s introduce the tree data
structure.  Conveniently enough, the &lt;code&gt;Data.Tree&lt;/code&gt; library
provides the structure for us:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='keyword'&gt;data&lt;/span&gt; &lt;span class='conid'&gt;Tree&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;Node&lt;/span&gt; &lt;span class='layout'&gt;{&lt;/span&gt;&lt;span class='varid'&gt;rootLabel&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='varid'&gt;subForest&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Forest&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt;&lt;span class='layout'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Let&amp;#8217;s also define some descriptive type synonyms to help us think about
our data:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;Path&lt;/span&gt;       &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt;           &lt;span class='comment'&gt;-- path&lt;/span&gt;
&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;DentName&lt;/span&gt;   &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt;           &lt;span class='comment'&gt;-- directory-entry name&lt;/span&gt;
&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;DirNode&lt;/span&gt;    &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;Path&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='conid'&gt;DentName&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='comment'&gt;-- directory-path/dentname pair&lt;/span&gt;
&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;DirTree&lt;/span&gt;    &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;Tree&lt;/span&gt; &lt;span class='conid'&gt;DentName&lt;/span&gt;    &lt;span class='comment'&gt;-- file-system tree&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;(Unlike last time, in this article I&amp;#8217;ll place type annotations
on all top-level declarations to help you see how the data
flows between the pieces of our code.)&lt;/p&gt;


	&lt;p&gt;To give you an idea of what the tree structure looks like,
here&amp;#8217;s an example &lt;em&gt;DirTree&lt;/em&gt; value:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;exampleTree&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;DirTree&lt;/span&gt;
&lt;span class='varid'&gt;exampleTree&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;Node&lt;/span&gt; &lt;span class='str'&gt;"root"&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;
                &lt;span class='conid'&gt;Node&lt;/span&gt; &lt;span class='str'&gt;"subdir1"&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;
                  &lt;span class='conid'&gt;Node&lt;/span&gt; &lt;span class='str'&gt;"file1.1"&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
                &lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='conid'&gt;Node&lt;/span&gt; &lt;span class='str'&gt;"file1.2"&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
                &lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='conid'&gt;Node&lt;/span&gt; &lt;span class='str'&gt;"subdir1.3"&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;
                    &lt;span class='conid'&gt;Node&lt;/span&gt; &lt;span class='str'&gt;"file1.3.1"&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt; &lt;span class='keyglyph'&gt;]&lt;/span&gt; &lt;span class='keyglyph'&gt;]&lt;/span&gt;
              &lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='conid'&gt;Node&lt;/span&gt; &lt;span class='str'&gt;"file1"&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt; &lt;span class='keyglyph'&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Now let&amp;#8217;s revise our old code to show a tree in this structure.&lt;/p&gt;


	&lt;h3&gt; Step 2:  Write the tree-showing code&lt;/h3&gt;


	&lt;p&gt;This step is easy.  We just copy and paste our old code, remove the
I/O related bits, and tweak for clarity:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='comment'&gt;-- Purely functional tree-to-string formatting&lt;/span&gt;

&lt;span class='varid'&gt;showTree&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Tree&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt;
&lt;span class='varid'&gt;showTree&lt;/span&gt; &lt;span class='varid'&gt;t&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;unlines&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;showNode&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='varid'&gt;t&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;

&lt;span class='varid'&gt;showNode&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Tree&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='conid'&gt;String&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
&lt;span class='varid'&gt;showNode&lt;/span&gt; &lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='varid'&gt;tie&lt;/span&gt; &lt;span class='varid'&gt;arm&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='varid'&gt;nodeRep&lt;/span&gt; &lt;span class='conop'&gt;:&lt;/span&gt; &lt;span class='varid'&gt;showChildren&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;extension&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
  &lt;span class='keyword'&gt;where&lt;/span&gt;
    &lt;span class='varid'&gt;nodeRep&lt;/span&gt;   &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;arm&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;tie&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;rootLabel&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;
    &lt;span class='varid'&gt;extension&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;case&lt;/span&gt; &lt;span class='varid'&gt;arm&lt;/span&gt; &lt;span class='keyword'&gt;of&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt;  &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt;&lt;span class='layout'&gt;;&lt;/span&gt; &lt;span class='str'&gt;"`"&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='str'&gt;"    "&lt;/span&gt;&lt;span class='layout'&gt;;&lt;/span&gt; &lt;span class='keyword'&gt;_&lt;/span&gt;   &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='str'&gt;"|   "&lt;/span&gt;

&lt;span class='varid'&gt;showChildren&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Tree&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='conid'&gt;String&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
&lt;span class='varid'&gt;showChildren&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='keyword'&gt;let&lt;/span&gt; &lt;span class='varid'&gt;children&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;subForest&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;
        &lt;span class='varid'&gt;arms&lt;/span&gt;     &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;replicate&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;length&lt;/span&gt; &lt;span class='varid'&gt;children&lt;/span&gt; &lt;span class='comment'&gt;-&lt;/span&gt; &lt;span class='num'&gt;1&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='str'&gt;"|"&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='str'&gt;"`"&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
    &lt;span class='keyword'&gt;in&lt;/span&gt;  &lt;span class='varid'&gt;concat&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;zipWith&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;showNode&lt;/span&gt; &lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='str'&gt;"-- "&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;arms&lt;/span&gt; &lt;span class='varid'&gt;children&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;There are two main differences between this code and the old code.
First, instead of processing directory nodes in the file system, we
are processing tree nodes of the &lt;em&gt;Tree&lt;/em&gt; data type.  Second, because
the code is pure, that is, free of side effects, its result is a
&lt;em&gt;String&lt;/em&gt;, not an &lt;em&gt;IO&lt;/em&gt; action.&lt;/p&gt;


	&lt;p&gt;Lets take this code for a spin in GHCi.  We&amp;#8217;ll ask it to show
our example tree from earlier in the article.&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;Ok, modules loaded: Main.
*Main&amp;gt; putStr (showTree exampleTree)
root
|-- subdir1
|   |-- file1.1
|   |-- file1.2
|   `-- subdir1.3
|       `-- file1.3.1
`-- file1
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Great.  Let&amp;#8217;s move on.&lt;/p&gt;


	&lt;h3&gt; Step 3:  Write the directory-traversal code&lt;/h3&gt;


	&lt;p&gt;In this step, we want to write the code that recursively scans
(traverses) a directory hierarchy and returns a &lt;em&gt;DirTree&lt;/em&gt; that
represents the hierarchy.  The following code, which does just
that, is a straightforward extraction of our old code&amp;#8217;s
&lt;em&gt;visit&lt;/em&gt; and &lt;em&gt;visitChildren&lt;/em&gt; functions:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='comment'&gt;-- Effectful directory-traversal code that returns a tree&lt;/span&gt;
&lt;span class='comment'&gt;-- representing the directory hierarchy rooted at 'path/node'.&lt;/span&gt;

&lt;span class='varid'&gt;fsTraverse&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Path&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;DentName&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='conid'&gt;DirTree&lt;/span&gt;
&lt;span class='varid'&gt;fsTraverse&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='conid'&gt;Node&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='varop'&gt;`liftM`&lt;/span&gt; &lt;span class='varid'&gt;fsTraverseChildren&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='str'&gt;"/"&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;

&lt;span class='varid'&gt;fsTraverseChildren&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Path&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;Forest&lt;/span&gt; &lt;span class='conid'&gt;DentName&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;span class='varid'&gt;fsTraverseChildren&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='varid'&gt;mapM&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;uncurry&lt;/span&gt; &lt;span class='varid'&gt;fsTraverse&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;=&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='varid'&gt;fsGetChildren&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;The &lt;em&gt;fsTraverse&lt;/em&gt; function takes advantage of &lt;em&gt;liftM&lt;/em&gt; to &amp;#8220;lift&amp;#8221; the
pure function (&lt;em&gt;Node node&lt;/em&gt;) into the &lt;em&gt;IO&lt;/em&gt; monad, where it
is applied to the result of the effectful &lt;em&gt;fsTraverseChildren&lt;/em&gt;
function.  It is shorter and more communicative of
our intent than the following equivalent code:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='keyword'&gt;do&lt;/span&gt; &lt;span class='varid'&gt;childForest&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;fsTraverseChildren&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='str'&gt;"/"&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
   &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;Node&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='varid'&gt;childForest&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;While extracting these functions from our original code, I took the
liberty of factoring out the code that grabs the contents of a
directory.  This code now lives in its own helper function:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='comment'&gt;-- helper to get traversable directory entries&lt;/span&gt;

&lt;span class='varid'&gt;fsGetChildren&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Path&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='conid'&gt;DirNode&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
&lt;span class='varid'&gt;fsGetChildren&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='varid'&gt;contents&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;getDirectoryContents&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varop'&gt;`catch`&lt;/span&gt; &lt;span class='varid'&gt;const&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
    &lt;span class='keyword'&gt;let&lt;/span&gt; &lt;span class='varid'&gt;visibles&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;sort&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;filter&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varop'&gt;`notElem`&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='str'&gt;"."&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='str'&gt;".."&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;contents&lt;/span&gt;
    &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;map&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;visibles&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;This bit of code differs from the code in the previous article in that
it treats any error encountered when scanning a directory as if the
directory were empty.  That&amp;#8217;s what the&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varop'&gt;...&lt;/span&gt; &lt;span class='varop'&gt;`catch`&lt;/span&gt; &lt;span class='varid'&gt;const&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;addendum does.  It says that any error caught while scanning the
directory will be handled by a constant policy, that of returning an
empty list.&lt;/p&gt;


	&lt;p&gt;This policy tends to be what we want in practice because the two most
common errors we expect to encounter here will be &amp;#8220;oops, that&amp;#8217;s a
file, so it can&amp;#8217;t have children&amp;#8221; and &amp;#8220;oops, you&amp;#8217;re not allowed to scan
that directory.&amp;#8221;  This error-handling policy will, however, throw away
potentially useful information, say, in the rare case that the error
is something like &amp;#8220;oops, your hard drive caught on fire and is about
to explode: start running now.&amp;#8221;  (Maybe we&amp;#8217;ll revisit our policy
later.)&lt;/p&gt;


	&lt;h3&gt; Step 4: Connect the traversal code to the printing code&lt;/h3&gt;


	&lt;p&gt;We can traverse file-system hierarchies to produce trees.  We can
print trees.  To traverse &lt;em&gt;and&lt;/em&gt; print a hierarchy, then, requires
nothing but a little glue.  Conveniently, we have already written
the needed glue, &lt;em&gt;traverseAndPrint&lt;/em&gt;, when we formulated our plan.
Here is a slightly more concise version:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;traverseAndPrint&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Path&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;span class='varid'&gt;traverseAndPrint&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='varid'&gt;putStr&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;showTree&lt;/span&gt; &lt;span class='varop'&gt;=&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='varid'&gt;fsTraverse&lt;/span&gt; &lt;span class='varid'&gt;root&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt;
  &lt;span class='keyword'&gt;where&lt;/span&gt;
    &lt;span class='varid'&gt;root&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;if&lt;/span&gt; &lt;span class='str'&gt;"/"&lt;/span&gt; &lt;span class='varop'&gt;`isPrefixOf`&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='keyword'&gt;then&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='keyword'&gt;else&lt;/span&gt; &lt;span class='str'&gt;"."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Now we have a complete solution.  Let&amp;#8217;s give it a spin in
GHCi.  I&amp;#8217;ll traverse and print the &lt;a href="http://darcs.net/"&gt;Darcs&lt;/a&gt; repository
that holds the code for this article.&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;*Main&amp;gt; traverseAndPrint "_darcs" 
_darcs
|-- inventories
|-- inventory
|-- patches
|   |-- 20070226210515-8fbd9-42e4...74102.gz
|   |-- 20070226210634-8fbd9-86cd...490ef.gz
|   |-- 20070227053004-8fbd9-122b...5023d.gz
|   |--   [ entries omitted for brevity ]
|   |-- 20070227233827-8fbd9-94ab...4852f.gz
|   |-- 20070228181747-8fbd9-db9b...e8ce6.gz
|   |-- 20070303192612-8fbd9-ff2a...3d7bb.gz
|   `-- pending
|-- prefs
|   |-- author
|   |-- binaries
|   |-- boring
|   `-- motd
`-- pristine
    |-- Makefile
    `-- tlist.hs
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;(I shortened some of the longer file names to prevent them from escaping
the narrow text column of my blog.)&lt;/p&gt;


	&lt;p&gt;Very well.  Now let&amp;#8217;s see if we can tighten our code.&lt;/p&gt;


	&lt;h3&gt; Step 5: Simplify&lt;/h3&gt;


	&lt;p&gt;One benefit of breaking monolithic code into composable pieces is that
the pieces are often amenable to further simplification.  Our
&lt;em&gt;fsTraverse&lt;/em&gt; function, for example, was extracted from our old code,
where it combined directory-scanning and tree-printing knowledge. Now,
the tree-printing knowledge factored out, it combines
directory-scanning and tree-&lt;em&gt;building&lt;/em&gt; knowledge.  But the &lt;em&gt;Tree&lt;/em&gt;
data type, as you might expect, already contains that second piece
of knowledge.  So we can simplify &lt;em&gt;fsTraverse&lt;/em&gt; further by removing this
duplicated know-how.&lt;/p&gt;


	&lt;p&gt;The function that makes the simplification possible is &lt;em&gt;unfoldTreeM&lt;/em&gt;:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varop'&gt;*&lt;/span&gt;&lt;span class='conid'&gt;Main&lt;/span&gt;&lt;span class='varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='conop'&gt;:&lt;/span&gt;&lt;span class='varid'&gt;t&lt;/span&gt; &lt;span class='varid'&gt;unfoldTreeM&lt;/span&gt;
&lt;span class='varid'&gt;unfoldTreeM&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;Monad&lt;/span&gt; &lt;span class='varid'&gt;m&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='keyglyph'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;b&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;m&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;a&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='varid'&gt;b&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;b&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;m&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;Tree&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;This function, which is parametrized by a &lt;em&gt;step&lt;/em&gt; function, grows a
tree from a single seed value.  The &lt;em&gt;step&lt;/em&gt; function is what we provide
to guide each step of the process.  It consumes a seed value and
produces a tree node and a list of seeds from which the node&amp;#8217;s
children can be grown.  The &lt;em&gt;unfoldTreeM&lt;/em&gt; function calls our step
function recursively on the seeds until the entire tree is grown.  (If
our step function were not effectful, requiring I/O, we could use the
pure function &lt;em&gt;unfoldTree&lt;/em&gt; instead of &lt;em&gt;unfoldTreeM&lt;/em&gt; to build our
trees.)&lt;/p&gt;


	&lt;p&gt;Our directory-scanning step function parallels our earlier
&lt;em&gt;fsTraverse&lt;/em&gt; function, which I will reprint below for comparison, but
eliminates knowledge of the &lt;em&gt;Tree&lt;/em&gt; data structure.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;fsTraverse&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Path&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;DentName&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='conid'&gt;DirTree&lt;/span&gt;
&lt;span class='varid'&gt;fsTraverse&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='conid'&gt;Node&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='varop'&gt;`liftM`&lt;/span&gt; &lt;span class='varid'&gt;fsTraverseChildren&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='str'&gt;"/"&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;

&lt;span class='varid'&gt;fsTraverseStep&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;DirNode&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;DentName&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='conid'&gt;DirNode&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;span class='varid'&gt;fsTraverseStep&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;path&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='varop'&gt;`liftM`&lt;/span&gt; &lt;span class='varid'&gt;fsGetChildren&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='str'&gt;"/"&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Note that a &lt;em&gt;DirNode&lt;/em&gt; is simply a (&lt;em&gt;Path&lt;/em&gt;, &lt;em&gt;DentName&lt;/em&gt;) pair.  Thus the
first argument of &lt;em&gt;fsTraverseStep&lt;/em&gt; is equivalent to the first two
arguments of &lt;em&gt;fsTraverse&lt;/em&gt;.  This pairing is convenient because the
seed values we produce are also &lt;em&gt;DirNode&lt;/em&gt;s, and this parity is
required by the type of &lt;em&gt;unfoldTreeM&lt;/em&gt;.  (Had our step function
retained the two-argument form, we could have used the &lt;em&gt;uncurry&lt;/em&gt;
combinator to transform it into the form required for &lt;em&gt;unfoldTreeM&lt;/em&gt;.
Later, we&amp;#8217;ll use &lt;em&gt;curry&lt;/em&gt; to perform the opposite transformation.)&lt;/p&gt;


	&lt;p&gt;Now we can use &lt;em&gt;unfoldTreeM&lt;/em&gt; to build file-system hierarchies.
Let&amp;#8217;s try it using GHCi:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varop'&gt;*&lt;/span&gt;&lt;span class='conid'&gt;Main&lt;/span&gt;&lt;span class='varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;unfoldTreeM&lt;/span&gt; &lt;span class='varid'&gt;fsTraverseStep&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='str'&gt;"."&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='str'&gt;"_darcs"&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;span class='conid'&gt;Node&lt;/span&gt; &lt;span class='layout'&gt;{&lt;/span&gt;&lt;span class='varid'&gt;rootLabel&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='str'&gt;"_darcs"&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='varid'&gt;subForest&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt; &lt;span class='comment'&gt;{- more nodes here -}&lt;/span&gt; &lt;span class='keyglyph'&gt;]&lt;/span&gt;&lt;span class='layout'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;The final step of our simplification is to rewrite &lt;em&gt;fsTraverse&lt;/em&gt;
in terms of &lt;em&gt;unfoldTreeM&lt;/em&gt;:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;fsTraverse&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Path&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;DentName&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='conid'&gt;DirTree&lt;/span&gt;
&lt;span class='varid'&gt;fsTraverse&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;curry&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;unfoldTreeM&lt;/span&gt; &lt;span class='varid'&gt;fsTraverseStep&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Note the use of &lt;em&gt;curry&lt;/em&gt; to adapt &lt;em&gt;unfoldTreeM&lt;/em&gt;&amp;#8217;s arguments
into the form implied by &lt;em&gt;fsTraverse&lt;/em&gt;&amp;#8217;s type signature.&lt;/p&gt;


	&lt;h3&gt; The final code (for now)&lt;/h3&gt;


	&lt;p&gt;Our simplification done, here is our complete, refactored
program:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='keyword'&gt;module&lt;/span&gt; &lt;span class='conid'&gt;Main&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;main&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='keyword'&gt;where&lt;/span&gt;

&lt;span class='keyword'&gt;import&lt;/span&gt; &lt;span class='conid'&gt;Control&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Monad&lt;/span&gt;
&lt;span class='keyword'&gt;import&lt;/span&gt; &lt;span class='conid'&gt;Data&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;List&lt;/span&gt;
&lt;span class='keyword'&gt;import&lt;/span&gt; &lt;span class='conid'&gt;Data&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Tree&lt;/span&gt;
&lt;span class='keyword'&gt;import&lt;/span&gt; &lt;span class='conid'&gt;System&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Directory&lt;/span&gt;
&lt;span class='keyword'&gt;import&lt;/span&gt; &lt;span class='conid'&gt;System&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Environment&lt;/span&gt;

&lt;span class='comment'&gt;-- Some convenient type synonyms&lt;/span&gt;

&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;Path&lt;/span&gt;       &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt;           &lt;span class='comment'&gt;-- path&lt;/span&gt;
&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;DentName&lt;/span&gt;   &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt;           &lt;span class='comment'&gt;-- directory-entry name&lt;/span&gt;
&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;DirNode&lt;/span&gt;    &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;Path&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='conid'&gt;DentName&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='comment'&gt;-- directory-path/dentname pair&lt;/span&gt;
&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;DirTree&lt;/span&gt;    &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;Tree&lt;/span&gt; &lt;span class='conid'&gt;DentName&lt;/span&gt;    &lt;span class='comment'&gt;-- file-system tree&lt;/span&gt;

&lt;span class='comment'&gt;-- High-level program logic:  get args and print a tree for each&lt;/span&gt;

&lt;span class='varid'&gt;main&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;span class='varid'&gt;main&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='varid'&gt;args&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;getArgs&lt;/span&gt;
    &lt;span class='varid'&gt;mapM_&lt;/span&gt; &lt;span class='varid'&gt;traverseAndPrint&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='keyword'&gt;if&lt;/span&gt; &lt;span class='varid'&gt;null&lt;/span&gt; &lt;span class='varid'&gt;args&lt;/span&gt; &lt;span class='keyword'&gt;then&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='str'&gt;"."&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt; &lt;span class='keyword'&gt;else&lt;/span&gt; &lt;span class='varid'&gt;args&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;

&lt;span class='varid'&gt;traverseAndPrint&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Path&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;span class='varid'&gt;traverseAndPrint&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='varid'&gt;putStr&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;showTree&lt;/span&gt; &lt;span class='varop'&gt;=&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='varid'&gt;fsTraverse&lt;/span&gt; &lt;span class='varid'&gt;root&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt;
  &lt;span class='keyword'&gt;where&lt;/span&gt;
    &lt;span class='varid'&gt;root&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;if&lt;/span&gt; &lt;span class='str'&gt;"/"&lt;/span&gt; &lt;span class='varop'&gt;`isPrefixOf`&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='keyword'&gt;then&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='keyword'&gt;else&lt;/span&gt; &lt;span class='str'&gt;"."&lt;/span&gt;

&lt;span class='comment'&gt;-- Effectful tree-builder for file-system hierarchies&lt;/span&gt;

&lt;span class='varid'&gt;fsTraverse&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Path&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;DentName&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='conid'&gt;DirTree&lt;/span&gt;
&lt;span class='varid'&gt;fsTraverse&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;curry&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;unfoldTreeM&lt;/span&gt; &lt;span class='varid'&gt;fsTraverseStep&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;

&lt;span class='varid'&gt;fsTraverseStep&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;DirNode&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;DentName&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='conid'&gt;DirNode&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;span class='varid'&gt;fsTraverseStep&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;path&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='varop'&gt;`liftM`&lt;/span&gt; &lt;span class='varid'&gt;fsGetChildren&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='str'&gt;"/"&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;

&lt;span class='comment'&gt;-- Helper to get traversable directory entries&lt;/span&gt;

&lt;span class='varid'&gt;fsGetChildren&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Path&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='conid'&gt;DirNode&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
&lt;span class='varid'&gt;fsGetChildren&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='varid'&gt;contents&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;getDirectoryContents&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt; &lt;span class='varop'&gt;`catch`&lt;/span&gt; &lt;span class='varid'&gt;const&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
    &lt;span class='keyword'&gt;let&lt;/span&gt; &lt;span class='varid'&gt;visibles&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;sort&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;filter&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varop'&gt;`notElem`&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='str'&gt;"."&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='str'&gt;".."&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;contents&lt;/span&gt;
    &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;map&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;path&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;visibles&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;

&lt;span class='comment'&gt;-- Purely functional tree-to-string formatting&lt;/span&gt;

&lt;span class='varid'&gt;showTree&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Tree&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt;
&lt;span class='varid'&gt;showTree&lt;/span&gt; &lt;span class='varid'&gt;t&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;unlines&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;showNode&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt; &lt;span class='varid'&gt;t&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;

&lt;span class='varid'&gt;showNode&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Tree&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='conid'&gt;String&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
&lt;span class='varid'&gt;showNode&lt;/span&gt; &lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='varid'&gt;tie&lt;/span&gt; &lt;span class='varid'&gt;arm&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='varid'&gt;nodeRep&lt;/span&gt; &lt;span class='conop'&gt;:&lt;/span&gt; &lt;span class='varid'&gt;showChildren&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;extension&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
  &lt;span class='keyword'&gt;where&lt;/span&gt;
    &lt;span class='varid'&gt;nodeRep&lt;/span&gt;   &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;arm&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;tie&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='varid'&gt;rootLabel&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;
    &lt;span class='varid'&gt;extension&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;case&lt;/span&gt; &lt;span class='varid'&gt;arm&lt;/span&gt; &lt;span class='keyword'&gt;of&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt;  &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='str'&gt;""&lt;/span&gt;&lt;span class='layout'&gt;;&lt;/span&gt; &lt;span class='str'&gt;"`"&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='str'&gt;"    "&lt;/span&gt;&lt;span class='layout'&gt;;&lt;/span&gt; &lt;span class='keyword'&gt;_&lt;/span&gt;   &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='str'&gt;"|   "&lt;/span&gt;

&lt;span class='varid'&gt;showChildren&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Tree&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='conid'&gt;String&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
&lt;span class='varid'&gt;showChildren&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt; &lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='keyword'&gt;let&lt;/span&gt; &lt;span class='varid'&gt;children&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;subForest&lt;/span&gt; &lt;span class='varid'&gt;node&lt;/span&gt;
        &lt;span class='varid'&gt;arms&lt;/span&gt;     &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;replicate&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;length&lt;/span&gt; &lt;span class='varid'&gt;children&lt;/span&gt; &lt;span class='comment'&gt;-&lt;/span&gt; &lt;span class='num'&gt;1&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='str'&gt;"|"&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='str'&gt;"`"&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
    &lt;span class='keyword'&gt;in&lt;/span&gt;  &lt;span class='varid'&gt;concat&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;zipWith&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;showNode&lt;/span&gt; &lt;span class='varid'&gt;leader&lt;/span&gt; &lt;span class='str'&gt;"-- "&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;arms&lt;/span&gt; &lt;span class='varid'&gt;children&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h3&gt; Let&amp;#8217;s review&lt;/h3&gt;


	&lt;p&gt;We have refactored our original, small, monolithic solution into a
collection of more reusable code snippets.  The total &amp;#8220;cost&amp;#8221; of our
new solution is certainly higher than before &amp;#8211; the code is twice as
long &amp;#8211; but we can reasonably expect that by reusing this code
we can reduce the cost of future solutions that might require similar
functionality.&lt;/p&gt;


	&lt;p&gt;Say, for example, we need to write some code to count the files and
directories within a hierarchy.  Building upon our new code, we can
write it easily, as a one-liner in GHCi even:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varop'&gt;*&lt;/span&gt;&lt;span class='conid'&gt;Main&lt;/span&gt;&lt;span class='varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;print&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;length&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;flatten&lt;/span&gt; &lt;span class='varop'&gt;=&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='varid'&gt;fsTraverse&lt;/span&gt; &lt;span class='str'&gt;"."&lt;/span&gt; &lt;span class='str'&gt;"_darcs"&lt;/span&gt;
&lt;span class='num'&gt;27&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Still, there is much about our code that could be better.  First,
unlike our old solution, our new solution builds the entire tree
before it begins to emit output.  If the tree is large, its memory
footprint could be daunting.  Second, our error-handling policy is
mildly embarrassing.  Third, our directory-traversal code isn&amp;#8217;t
flexible.  What if we want to get the size of each file we visit
during the traversal?  Right now, there&amp;#8217;s no easy way to do it.&lt;/p&gt;


	&lt;p&gt;Fortunately, we aren&amp;#8217;t done yet.  Haskell offers us many
opportunities for abstraction and parametrization that we are not
taking advantage of.  In the next article, we&amp;#8217;ll exploit a
few of these opportunities to make our code a little more
flexible and a little more idiomatic.&lt;/p&gt;


	&lt;p&gt;See you then.&lt;/p&gt;</description>
      <pubDate>Wed, 07 Mar 2007 16:04:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:228f4d10-a4a2-4284-a648-4f741a82dc66</guid>
      <author>Tom Moertel</author>
      <link>http://blog.moertel.com/articles/2007/03/07/directory-tree-printing-in-haskell-part-two-refactoring</link>
      <category>haskell</category>
      <category>haskell</category>
      <category>trees</category>
      <category>io</category>
      <category>refactoring</category>
      <category>directory_tree_series</category>
      <trackback:ping>http://blog.moertel.com/articles/trackback/401</trackback:ping>
    </item>
    <item>
      <title>"Directory-tree printing in Haskell, part two: refactoring" by petekaz</title>
      <description>&lt;p&gt;Great! Thanks again Tom! This has been extremely helpful.  Sorry for hijacking the comments.&lt;/p&gt;


	&lt;p&gt;Now if you could only decipher Monads for me.  Don Stewart&amp;#8217;s recent blog entry on using Haskell for shell scripting has my head spinning &amp;#8230; all those type signatures.&lt;/p&gt;</description>
      <pubDate>Fri, 09 Mar 2007 00:05:02 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:24424911-6353-4c53-9990-0b7a890387b4</guid>
      <link>http://blog.moertel.com/articles/2007/03/07/directory-tree-printing-in-haskell-part-two-refactoring#comment-408</link>
    </item>
    <item>
      <title>"Directory-tree printing in Haskell, part two: refactoring" by Tom Moertel</title>
      <description>&lt;p&gt;Pete,&lt;/p&gt;


	&lt;p&gt;No, you don&amp;#8217;t have to read the entire contents of the string returned by &lt;em&gt;readFile&lt;/em&gt;.  Once you are done reading from the string, the underlying handle that was used to feed you data lazily can be reclaimed, and its finalizer will close the file.&lt;/p&gt;


	&lt;p&gt;One good way to see this behavior is to run your program on two files, the first of which is of &amp;#8220;infinite&amp;#8221; length.  Here I&amp;#8217;ll use the Unix &lt;code&gt;yes&lt;/code&gt; program to generate an infinite-length stream of &amp;#8220;y&amp;#8221; lines.  Let&amp;#8217;s see if we can &amp;#8220;take 5&amp;#8221; from this stream and then move on to another file without getting hung up:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;$ cat &amp;gt; take5.hs
module Main where
import System.Environment

main = mapM_ doFile =&amp;lt;&amp;lt; getArgs
    where
      doFile f = do
        contents &amp;lt;- readFile f
        putStr . unlines . take 5 . lines $ contents
^D

$ yes | runhaskell take5.hs /dev/stdin take5.hs
y
y
y
y
y
module Main where
import System.Environment

main = mapM_ doFile =&amp;lt;&amp;lt; getArgs
    where
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;As you can see, the program did not need to read the first file to exhaustion, which would have taken forever.  Instead, it read as much as it wanted (five lines) and moved on to the next file (which happened to be its own source code).&lt;/p&gt;


	&lt;p&gt;Cheers,&lt;br /&gt;
Tom&lt;/p&gt;</description>
      <pubDate>Thu, 08 Mar 2007 23:57:58 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:9357ba73-a7c3-481a-bf51-9d793e96ee2f</guid>
      <link>http://blog.moertel.com/articles/2007/03/07/directory-tree-printing-in-haskell-part-two-refactoring#comment-407</link>
    </item>
    <item>
      <title>"Directory-tree printing in Haskell, part two: refactoring" by petekaz</title>
      <description>&lt;p&gt;Tom,&lt;/p&gt;


	&lt;p&gt;Thank you once again for the lucid explanation.  It&amp;#8217;s starting to make sense to me.  With that said, I have one final question along this line of thought.  The small program below prints the top 5 lines of all files specified as arguments. It uses readFile which as you state uses the interleaving trick to allow the output of it to be consumed lazily.&lt;/p&gt;


&lt;pre&gt;
module Main where
import System.Environment

main = mapM_ doFile =&amp;lt;&amp;lt; getArgs
    where
      doFile f = do
        contents &amp;lt;- readFile f
        putStr . unlines . take 5 . lines $ contents
&lt;/pre&gt;

	&lt;p&gt;If I invoke this program with two arguments and assuming the first of which is a very, very large file, is this interleaving trick smart enough so that readFile does not have to read the entire file?  Just to be clear, I&amp;#8217;m not asking whether or not putStr can start consuming before reading the entire file, but rather does the current invocation of readFile have to complete in its entirety before the next invocation occurs?&lt;/p&gt;


	&lt;p&gt;Thanks,
Pete&lt;/p&gt;</description>
      <pubDate>Thu, 08 Mar 2007 20:30:18 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:f878b37b-1b99-4cac-94f4-3dcbafd1744d</guid>
      <link>http://blog.moertel.com/articles/2007/03/07/directory-tree-printing-in-haskell-part-two-refactoring#comment-406</link>
    </item>
    <item>
      <title>"Directory-tree printing in Haskell, part two: refactoring" by Tom Moertel</title>
      <description>&lt;p&gt;Pete,&lt;/p&gt;


	&lt;p&gt;That&amp;#8217;s another great question.  Before I answer, let me say that
despite how complicated this explanation may seem, with a little
time, all of this monad and I/O business becomes intuitive.&lt;/p&gt;


	&lt;p&gt;Now, the answer.&lt;/p&gt;


	&lt;p&gt;Different monads provide different rules that govern how
their actions are interpreted.  The &lt;em&gt;IO&lt;/em&gt; monad&amp;#8217;s rules say that
if you combine two actions&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;action1 &amp;gt;&amp;gt; action2
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;the side effects produced by &lt;em&gt;action1&lt;/em&gt; will be finished before the
side effects of &lt;em&gt;action2&lt;/em&gt; begin.  This ordering rule applies to
the side effects, not necessarily to the Haskell code and data
that interact with those effects.  The interactions, however, often
force our code and data to be evaluated and constructed in parallel
with the effects, thus imposing an ordering on them, too.&lt;/p&gt;


	&lt;p&gt;In our tree-building code, for example, a tree is constructed by a
single &lt;em&gt;IO&lt;/em&gt; action created by &lt;em&gt;fsTraverse&lt;/em&gt;.  When that action is
sequenced with the action of printing the tree, all of the
tree-building side effects are forced to occur before the first
printing side effect.  But because the tree-building side effects
are intertwined with the code for building the tree data structure,
forcing the all of the side effects to occur also forces the all
of the tree to be constructed.&lt;/p&gt;


	&lt;p&gt;Other monads offer different rules, and most do not have the profound
ordering effects on intertwined code that the &lt;em&gt;IO&lt;/em&gt; monad does.  As an
example, let me write some code to build an infinite tree as a single
monadic action:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;infTreeM :: Monad m =&amp;gt; m (Tree String)
infTreeM = unfoldTreeM step 1
    where step n = return (show n, [n+1, n+2])
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;We will run this code in different monads to see whether we can print
the top of the tree before the entire tree is constructed, which will
take forever since the tree is infinite.  That is, if we can print
the first five lines of the tree, we know the tree was produced lazily.&lt;/p&gt;


	&lt;p&gt;I&amp;#8217;ll write some code to print the top of a tree.&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;printTreeTop tree =
    putStr . unlines . take 5 . lines . showTree $ tree
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;As you might expect, when we build our infinite tree as an &lt;em&gt;Identity&lt;/em&gt;
monad action, which is equivalent to running a &amp;#8220;pure&amp;#8221; version of our
code, the tree is constructed lazily, with no unexpected sequencing:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;*Main&amp;gt; :m +Control.Monad.Identity
*Main Control.Monad.Identity&amp;gt; printTreeTop (runIdentity infTreeM)
1
|-- 2
|   |-- 3
|   |   |-- 4
|   |   |   |-- 5
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;To answer your question, What about the &lt;em&gt;Reader&lt;/em&gt; monad?&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;*Main Control.Monad.Identity&amp;gt; :m -Control.Monad.Identity
*Main&amp;gt; :m +Control.Monad.Reader
*Main Control.Monad.Reader&amp;gt; :t runReader
runReader :: Reader r a -&amp;gt; r -&amp;gt; a
*Main Control.Monad.Reader&amp;gt; printTreeTop (runReader infTreeM ())
1
|-- 2
|   |-- 3
|   |   |-- 4
|   |   |   |-- 5
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;So the &lt;em&gt;Reader&lt;/em&gt; monad does not interfere with the lazy construction of
our tree.&lt;/p&gt;


	&lt;p&gt;But when we force the tree-building to occur as an &lt;em&gt;IO&lt;/em&gt; action,
the monad&amp;#8217;s sequencing guarantees prevent any printing from occurring
before the entire infinite tree is constructed:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;*Main&amp;gt; printTreeTop =&amp;lt;&amp;lt; infTreeM
Interrupted.
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;In the end, I had to interrupt the computation to regain control of my
GHCi session.&lt;/p&gt;


	&lt;p&gt;In sum, the &lt;em&gt;IO&lt;/em&gt; monad often forces our code to be evaluated in a certain sequence by forcing the side effects that intertwine with our code to be sequenced.  Most other monads do not have such drastic effects on evaluation order.&lt;/p&gt;


	&lt;p&gt;I hope this answers your question.&lt;/p&gt;


	&lt;p&gt;Cheers,
Tom&lt;/p&gt;</description>
      <pubDate>Thu, 08 Mar 2007 12:54:25 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:5d901c12-9e9a-4705-be5b-01634a57211e</guid>
      <link>http://blog.moertel.com/articles/2007/03/07/directory-tree-printing-in-haskell-part-two-refactoring#comment-405</link>
    </item>
    <item>
      <title>"Directory-tree printing in Haskell, part two: refactoring" by petekaz</title>
      <description>&lt;p&gt;Thank you, that makes sense.  It also explains some things I&amp;#8217;ve seen in my own code.&lt;/p&gt;


	&lt;p&gt;One followup question though, does this also apply to non-IO monads as well?  For example, let&amp;#8217;s pretend that fsTraverse worked outside of the IO monad, but for other reasons we wanted to have access to some configuration that we access from inside the Reader monad:&lt;/p&gt;


&lt;pre&gt;
fsTraverse :: Path -&amp;gt; DentName -&amp;gt; Reader DirTree
&lt;/pre&gt;

	&lt;p&gt;Would this also prevent the tree from being lazily consumed also assuming that showTree was in the Reader monad?  I ask only because it seems when I read something about monads, it varies depending on whether or not it refers to the IO monad.&lt;/p&gt;


	&lt;p&gt;Thanks,
Pete&lt;/p&gt;</description>
      <pubDate>Thu, 08 Mar 2007 07:27:31 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:5c97537f-62e6-4775-b5ff-f12b8cf6a003</guid>
      <link>http://blog.moertel.com/articles/2007/03/07/directory-tree-printing-in-haskell-part-two-refactoring#comment-404</link>
    </item>
    <item>
      <title>"Directory-tree printing in Haskell, part two: refactoring" by Tom Moertel</title>
      <description>&lt;p&gt;Pete, that&amp;#8217;s a great question.  For the full answer, wait for the
next article in this ever-growing series.  Until then, here&amp;#8217;s
the short answer.&lt;/p&gt;


	&lt;p&gt;The reason why this version emits the whole tree at once is because of
the ordering guarantees provided by the &lt;em&gt;IO&lt;/em&gt; monad.  If you look at
the type of &lt;em&gt;fsTraverse&lt;/em&gt;,&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;fsTraverse :: Path -&amp;gt; DentName -&amp;gt; IO DirTree
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;you&amp;#8217;ll see that it produces an I/O action that, when performed,
returns a &lt;em&gt;DirTree&lt;/em&gt;.  If you sequence that action with another I/O
action, say one that prints the tree, the first action&amp;#8217;s effects (in
this case I/O) are guaranteed to have been completed before the
second&amp;#8217;s begin to take place.  Thus all of the directory scanning must
be finished before the first output from the formatted tree can
be emitted.&lt;/p&gt;


	&lt;p&gt;Some of the standard library functions, however, use an interleaving
trick to produce I/O actions that immediately return values which can
be consumed lazily.  The ever-popular &lt;em&gt;getContents&lt;/em&gt; and &lt;em&gt;readFile&lt;/em&gt;
functions are good examples.&lt;/p&gt;


	&lt;p&gt;In the next article in this series, I&amp;#8217;ll show how we can use the same
trick to restore the lean memory characteristics of our original
implementation.&lt;/p&gt;


	&lt;p&gt;Cheers,&lt;br /&gt;Tom&lt;/p&gt;</description>
      <pubDate>Thu, 08 Mar 2007 01:08:15 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:4700c4b7-a52f-44b5-a052-2bbdce74e85a</guid>
      <link>http://blog.moertel.com/articles/2007/03/07/directory-tree-printing-in-haskell-part-two-refactoring#comment-403</link>
    </item>
    <item>
      <title>"Directory-tree printing in Haskell, part two: refactoring" by petekaz</title>
      <description>&lt;p&gt;Hi Tom,&lt;/p&gt;


	&lt;p&gt;I don&amp;#8217;t understand why this version has to read the whole tree before it begins to emit output.  Doesn&amp;#8217;t Haskell&amp;#8217;s laziness help here?  showTree looks like it builds a list incrementally, so I would have thought that the first output line could be emitted before the entire tree is produced.  In fact, as soon as the first Node of the tree is created, couldn&amp;#8217;t this be consumed by showTree?&lt;/p&gt;


	&lt;p&gt;As a newcomer to Haskell, laziness is one of the more difficult subjects to understand (next to Monads).  I&amp;#8217;d appreciate any insight into the matter.&lt;/p&gt;


	&lt;p&gt;Thanks,
Pete&lt;/p&gt;</description>
      <pubDate>Thu, 08 Mar 2007 00:16:04 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:02c583a2-fdc1-4163-b52a-4742b5a64417</guid>
      <link>http://blog.moertel.com/articles/2007/03/07/directory-tree-printing-in-haskell-part-two-refactoring#comment-402</link>
    </item>
  </channel>
</rss>
