<?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: A simple directory-tree printer in Haskell</title>
    <link>http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Quality rants on programming theory and stuff geeks like</description>
    <item>
      <title>A simple directory-tree printer in Haskell</title>
      <description>&lt;p&gt;At a recent gathering, my friend &lt;a href="http://blog.caseywest.com/"&gt;Casey&lt;/a&gt;
mentioned that he was learning a new programming language and, as a
learning exercise, had written a directory-tree printer.  That&amp;#8217;s a program that recursively scans directory hierarchies and
prints out a tree representation for each:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;$ tlist cheating-hangman

cheating-hangman
|-- CVS
|   |-- Entries
|   |-- Repository
|   `-- Root
|-- Makefile
|-- cheating-hangman.lhs
`-- cheating-hangman.pl
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;I thought the problem sounded like fun, and so I wrote a small
solution in Haskell.  Let&amp;#8217;s take a look.&lt;/p&gt;&lt;p&gt;First, some documentation by way of comments.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='comment'&gt;-- Tree list for directories&lt;/span&gt;
&lt;span class='comment'&gt;-- Tom Moertel &amp;lt;tom@moertel.com&amp;gt;&lt;/span&gt;
&lt;span class='comment'&gt;-- 2007-02-20&lt;/span&gt;
&lt;span class='comment'&gt;--&lt;/span&gt;
&lt;span class='comment'&gt;-- Compile:  ghc -O -o tlist --make tlist.hs&lt;/span&gt;
&lt;span class='comment'&gt;-- Usage:    ./tlist [dir...]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Next, we&amp;#8217;ll start a new module and import a few libraries that will
make our job easier.&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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Now we arrive at the first real code, the &lt;em&gt;main&lt;/em&gt; function:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&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;tlist&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;

The code gets the arguments from the command line, each representing a file
or directory.  Then it maps the &lt;em&gt;tlist&lt;/em&gt; function over the list of
arguments or, if the list is empty, over the default argument, which
represents the current working directory.  Because &lt;em&gt;tlist&lt;/em&gt; has side
effects (e.g., directory scanning and tree printing), it returns an
&lt;code&gt;IO&lt;/code&gt; action, and thus we must use a monadic flavor of map,
&lt;em&gt;mapM_&lt;/em&gt;, to sequence its effects.

	&lt;p&gt;Next, &lt;em&gt;tlist&lt;/em&gt; visits the file-system path it receives as
an argument, handing off to the &lt;em&gt;visit&lt;/em&gt; function, to which
it passes some sensible initial values.  We&amp;#8217;ll examine
what these values mean in a moment.&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;The &lt;em&gt;visit&lt;/em&gt; function, in turn, &amp;#8220;visits&amp;#8221; a node, printing its
little portion of the tree.  Before we look at that function, however,
let&amp;#8217;s think about drawing a single node.&lt;/p&gt;


	&lt;p&gt;Say we&amp;#8217;re visiting the &amp;#8220;Repository&amp;#8221; node in the hierarchy:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;cheating-hangman
|-- CVS
|   |-- Entries
|   |-- Repository    &amp;lt;--- consider just this line

    ^   ^
    A   B
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;To connect the node to the tree, we need to draw three things:&lt;/p&gt;


	&lt;ol&gt;
	&lt;li&gt;the &lt;em&gt;leader&lt;/em&gt;, which connects the tree structure way above the node;&lt;/li&gt;
		&lt;li&gt;the &lt;em&gt;arm&lt;/em&gt;, which connects the node&amp;#8217;s siblings; and &lt;/li&gt;
		&lt;li&gt;the &lt;em&gt;tie&lt;/em&gt;, which connects the node to the arm.&lt;/li&gt;
	&lt;/ol&gt;


	&lt;p&gt;In this particular case, the leader is everything to the left of point
&lt;strong&gt;A&lt;/strong&gt;.  Here it is &lt;code&gt;"|   "&lt;/code&gt;, but it will get
longer as we descend deeper into the hierarchy.  The arm is the single
character at point &lt;strong&gt;A&lt;/strong&gt;.  It&amp;#8217;s usually a vertical bar,
but for the final sibling it&amp;#8217;s rounded off into an elbow (a backquote
in &lt;span class="caps"&gt;ASCII&lt;/span&gt; art).  Finally the tie, which connects the name of the
current node to the arm, is everything between points
&lt;strong&gt;A&lt;/strong&gt; and &lt;strong&gt;B&lt;/strong&gt;.  It&amp;#8217;s always &lt;code&gt;"--
"&lt;/code&gt; except at the tree&amp;#8217;s root, which needs no tie.&lt;/p&gt;


	&lt;p&gt;With the tree&amp;#8217;s anatomy firmly in mind, we&amp;#8217;re ready to
visit a node and draw its contribution to the tree:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Let&amp;#8217;s examine the arguments.  &lt;em&gt;Path&lt;/em&gt; is the path to the directory
we&amp;#8217;re listing, and &lt;em&gt;node&lt;/em&gt; is the item we&amp;#8217;re visiting within that
directory.  The remaining arguments are the tree-drawing parts
necessary to connect this node to the tree.&lt;/p&gt;


	&lt;p&gt;The first line of the function&amp;#8217;s body prints the node&amp;#8217;s addition to
the tree.  The second line visits the node&amp;#8217;s children, if any,
updating the &lt;em&gt;path&lt;/em&gt; and &lt;em&gt;leader&lt;/em&gt; accordingly.  The &lt;em&gt;path&lt;/em&gt; is updated
to point to the current &lt;em&gt;node&lt;/em&gt;.  The &lt;em&gt;leader&lt;/em&gt; is extended to ensure
that the current node&amp;#8217;s unvisited siblings, if any, will be connected
to the tree by the current node&amp;#8217;s children, if any.  In effect, the
current node&amp;#8217;s arm is projected into the children&amp;#8217;s leader to make the
connection.&lt;/p&gt;


	&lt;p&gt;Visiting the children is a little more complicated:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&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;whenM&lt;/em&gt; line tests to see if the path represents a directory.
If it&amp;#8217;s not, the remaining
code is ignored.  (Only directories have children to visit.)&lt;/p&gt;


&lt;p&gt;The remaining code, if it does run, gets the directory&amp;#8217;s contents
(catching and reporting errors, if any), filters out the
&lt;code&gt;"."&lt;/code&gt; and &lt;code&gt;".."&lt;/code&gt; directory entries, and stores
the sorted, remaining entries in &lt;em&gt;visibles&lt;/em&gt;.  Then it prepares a list
of &lt;em&gt;arms&lt;/em&gt;: straight arms for all but the last entry, which gets an
elbow.  Finally, &lt;em&gt;zipWithM_&lt;/em&gt; &amp;#8220;zips&amp;#8221; the prepared arms and
visible entries into one big, effectful computation that calls
(recursively) to the &lt;em&gt;visit&lt;/em&gt; function to zip each arm with its
corresponding entry.  And that completes our algorithm.&lt;/p&gt;

	&lt;p&gt;One final tidbit: the handy &lt;em&gt;whenM&lt;/em&gt;, which lamentably is not part
of the Haskell standard libraries (yet), is defined like so:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;whenM&lt;/span&gt; &lt;span class='varid'&gt;mtest&lt;/span&gt; &lt;span class='varid'&gt;ma&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;mtest&lt;/span&gt; &lt;span class='varop'&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class='varid'&gt;flip&lt;/span&gt; &lt;span class='varid'&gt;when&lt;/span&gt; &lt;span class='varid'&gt;ma&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h3&gt; The complete code&lt;/h3&gt;


	&lt;p&gt;Here&amp;#8217;s the code, without the commentary:&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;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='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;tlist&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;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;span class='varid'&gt;whenM&lt;/span&gt; &lt;span class='varid'&gt;mtest&lt;/span&gt; &lt;span class='varid'&gt;ma&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;mtest&lt;/span&gt; &lt;span class='varop'&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class='varid'&gt;flip&lt;/span&gt; &lt;span class='varid'&gt;when&lt;/span&gt; &lt;span class='varid'&gt;ma&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;h3&gt;Next time: a refactoring&lt;/h3&gt;


	&lt;p&gt;Our program does two things: it scans directory hierarchies and it
prints trees.  You may have noticed, however, that those two things
are intertwined in the code.  There are  times when we might
want to scan a directory hierarchy or print a tree, but our code can&amp;#8217;t
be reused for those purposes.  It can only scan &lt;em&gt;and&lt;/em&gt; print as one
indivisible action.&lt;/p&gt;


	&lt;p&gt;Wouldn&amp;#8217;t it be better to separate the directory-scanning code from the
tree-printing code so that each formed a reusable module?  Next time,
we&amp;#8217;ll do just that.  Haskell&amp;#8217;s lazy magic makes it easy &lt;em&gt;and&lt;/em&gt;
efficient.&lt;/p&gt;


	&lt;p&gt;See you then.&lt;/p&gt;


&lt;div class="update"&gt;
&lt;strong&gt;Update 2007-02-23:&lt;/strong&gt; If you&amp;#8217;re interested in coding up an implementation,
please don&amp;#8217;t forget to round off corners and prune unnecessary connecting lines. For example:

&lt;pre&gt;&lt;code&gt;dir1
|-- file1
|-- subdir1
|   |-- file11
|   |-- file12
|   `-- subdir13
|       `-- file131
`-- zfile
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</description>
      <pubDate>Thu, 22 Feb 2007 16:30:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:eb3e46da-1437-4588-b06b-7cc086f75651</guid>
      <author>Tom Moertel</author>
      <link>http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell</link>
      <category>programming</category>
      <category>functional programming</category>
      <category>haskell</category>
      <category>haskell</category>
      <category>trees</category>
      <category>io</category>
      <category>directory_tree_series</category>
      <trackback:ping>http://blog.moertel.com/articles/trackback/387</trackback:ping>
    </item>
    <item>
      <title>"A simple directory-tree printer in Haskell" by Bastl</title>
      <description>&lt;p&gt;Please include types of all functions for a better understanding. Thanks, Bastl.&lt;/p&gt;</description>
      <pubDate>Fri, 25 Jul 2008 09:53:06 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:1c213f9c-ec9f-4ec4-8684-7285ea50ba1e</guid>
      <link>http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell#comment-753</link>
    </item>
    <item>
      <title>"A simple directory-tree printer in Haskell" by Parag</title>
      <description>&lt;p&gt;People who might have the same problem as me, i.e. if you see code that is cut off, please use this trick: 
Select segment of page including the code, copy and paste into a plain text editor such as, Kate (or Notepad) and you will see the whole code. 
If your browser can display as plain-text that might be useful also.&lt;/p&gt;</description>
      <pubDate>Tue, 03 Jun 2008 12:58:37 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:bc2e8e74-374a-4fa9-8fe3-7dcd21e4d957</guid>
      <link>http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell#comment-735</link>
    </item>
    <item>
      <title>"A simple directory-tree printer in Haskell" by Parag</title>
      <description>&lt;p&gt;Hello Tom, 
Thanks for explaining the concepts! Great article and great code!&lt;/p&gt;


	&lt;p&gt;One problem: 
On my Linux computer (Kubuntu), in both Firefox and Konqueror the code display gets cut off, (i.e. $ contents is not seen) as it gets overwritten by sidebar with blog categories, etc.&lt;/p&gt;


	&lt;p&gt;So, instead of seeing the line: 
        let visibles = sort . filter (`notElem` [&amp;#8221;.&amp;#8221;, &amp;#8221;..&amp;#8221;]) $ contents&lt;/p&gt;


	&lt;p&gt;I see 
        let visibles = sort . filter (`notElem` [&amp;#8221;.&amp;#8221;, &amp;#8221;..&amp;#8221;])&lt;/p&gt;


	&lt;p&gt;So, this left me puzzling as to accuracy of the code until I did view source.&lt;/p&gt;</description>
      <pubDate>Tue, 03 Jun 2008 12:56:04 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:71dd523e-ca3c-4330-840a-3a8e965be4d4</guid>
      <link>http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell#comment-734</link>
    </item>
    <item>
      <title>"A simple directory-tree printer in Haskell" by Andy </title>
      <description>&lt;p&gt;Hi &amp;#8211; &lt;/p&gt;


	&lt;pre&gt;&lt;code&gt;Ok - thanks!&lt;/code&gt;&lt;/pre&gt;


	&lt;p&gt;- Andy&lt;/p&gt;</description>
      <pubDate>Wed, 20 Jun 2007 01:49:08 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:99219a3e-5776-42c1-baff-cf9a19486a12</guid>
      <link>http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell#comment-480</link>
    </item>
    <item>
      <title>"A simple directory-tree printer in Haskell" by Tom Moertel</title>
      <description>&lt;p&gt;Andy, unless otherwise specified, my code on this site is licensed under the &lt;a href="http://www.gnu.org/licenses/gpl.txt"&gt;GNU GPL&lt;/a&gt;.&lt;/p&gt;


	&lt;p&gt;Cheers,&lt;br /&gt;
Tom&lt;/p&gt;</description>
      <pubDate>Tue, 19 Jun 2007 12:12:17 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:add46180-35fe-4557-88fc-bd0246cca46b</guid>
      <link>http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell#comment-479</link>
    </item>
    <item>
      <title>"A simple directory-tree printer in Haskell" by Andy </title>
      <description>&lt;p&gt;Hi Tom! Thanks for this great article! 
I&amp;#8217;m just getting into Haskell myself, and I&amp;#8217;m very impressed by its elegance.&lt;/p&gt;


	&lt;p&gt;Oh, just a quick question&amp;#8230; I was wondering what license the code in the article uses. Is it public-domain? 
- Andy&lt;/p&gt;</description>
      <pubDate>Tue, 19 Jun 2007 04:59:41 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:d0472807-036a-42de-b7af-28262a215267</guid>
      <link>http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell#comment-478</link>
    </item>
    <item>
      <title>"A simple directory-tree printer in Haskell" by Kurt Hutchinson</title>
      <description>&lt;p&gt;Nice article, and nice little puzzle. The version I wrote is structured like your refactoring suggestion: a data structure for the tree, and a function that recurses the data structure to create output.&lt;/p&gt;


	&lt;p&gt;One minor quibble: your version doesn&amp;#8217;t check if the command line arguments are existing files before displaying them.&lt;/p&gt;</description>
      <pubDate>Thu, 01 Mar 2007 10:59:29 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:f11f6e7d-dbd8-46e2-98e2-436e16164ac8</guid>
      <link>http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell#comment-396</link>
    </item>
    <item>
      <title>"A simple directory-tree printer in Haskell" by Nick</title>
      <description>&lt;p&gt;Good article, I like it. Although I wonder how you will treat the laziness being an obstacle. I presume it will be intimate massage with the profiler and mumba-youmba dances around !, seq and $! :-)&lt;/p&gt;</description>
      <pubDate>Tue, 27 Feb 2007 13:14:12 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:12cc165a-11ad-458f-a962-1c85005346cc</guid>
      <link>http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell#comment-395</link>
    </item>
    <item>
      <title>"A simple directory-tree printer in Haskell" by petekaz</title>
      <description>&lt;p&gt;Tom I love your Haskell articles!!  I&amp;#8217;ve started to learn Haskell and really appreciate your tutorials as they pack so much information in them!  Keep them coming!&lt;/p&gt;</description>
      <pubDate>Sat, 24 Feb 2007 00:49:13 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:130f8605-fa75-49b4-ab96-24dd3e74ac5b</guid>
      <link>http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell#comment-391</link>
    </item>
    <item>
      <title>"A simple directory-tree printer in Haskell" by Dru Nelson</title>
      <description>&lt;p&gt;Great, real-world, simple example that really puts across the power of Haskell.&lt;/p&gt;</description>
      <pubDate>Fri, 23 Feb 2007 20:41:43 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:f7efd192-e6d7-4072-83d1-58db329161d0</guid>
      <link>http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell#comment-390</link>
    </item>
  </channel>
</rss>
