<?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: Tag io</title>
    <link>http://blog.moertel.com/articles/tag/io?tag=io</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 three: lazy I/O</title>
      <description>&lt;p&gt;This article is part three in a series on introductory Haskell
programming.  In &lt;a href="http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell"&gt;the first
article&lt;/a&gt;,
we 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.  In the &lt;a href="http://blog.moertel.com/articles/2007/03/07/directory-tree-printing-in-haskell-part-two-refactoring"&gt;second
article&lt;/a&gt;,
we refactored the program to make its logic more reusable by separating
the directory-scanning logic from the tree-printing logic.  In this
article, we will address a shortcoming of the refactored version: It
must scan directory hierarchies completely before printing their
trees, i.e., it must scan and &lt;em&gt;then&lt;/em&gt; print,
when doing both simultaneously is both more efficient and
more user friendly.&lt;/p&gt;


	&lt;p&gt;Recall from the previous article that our directory-printing program
is factored into three pieces of logic:&lt;/p&gt;


	&lt;ol&gt;
	&lt;li&gt;&lt;em&gt;fsTraverse&lt;/em&gt;, which traverses a file-system hierarchy and returns a tree data structure;&lt;/li&gt;
		&lt;li&gt;&lt;em&gt;showTree&lt;/em&gt;, which converts a tree into lovingly crafted &lt;span class="caps"&gt;ASCII&lt;/span&gt; art; and&lt;/li&gt;
		&lt;li&gt;&lt;em&gt;traverseAndPrint&lt;/em&gt;, which prints the tree for a file-system hierarchy by
using the first two pieces of logic.&lt;/li&gt;
	&lt;/ol&gt;


	&lt;p&gt;The types of the functions are as follows:&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;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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Note that &lt;em&gt;showTree&lt;/em&gt; is a pure function, but the other two return
&lt;em&gt;IO&lt;/em&gt; actions that may have side effects.&lt;/p&gt;


	&lt;p&gt;Within &lt;em&gt;traverseAndPrint&lt;/em&gt;, &lt;em&gt;fsTraverse&lt;/em&gt; and &lt;em&gt;showTree&lt;/em&gt; are combined
into a composite &lt;em&gt;IO&lt;/em&gt; action by the &lt;code&gt;=&amp;lt;&amp;lt;&lt;/code&gt; combinator:&lt;/p&gt;


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

	&lt;p&gt;The sequencing semantics of Haskell&amp;#8217;s &lt;em&gt;IO&lt;/em&gt; monad forces all of the
effects of &lt;em&gt;fsTraverse&lt;/em&gt; to complete before any following
effects can begin.  To better understand these sequencing semantics,
let&amp;#8217;s consider a simple example.&lt;/p&gt;


	&lt;p&gt;The &lt;em&gt;IO&lt;/em&gt;-monad code,&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;a&lt;/span&gt; &lt;span class='varop'&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;can loosely be interpreted as running the action &lt;em&gt;a&lt;/em&gt;, which forces
its side effects to occur, and then running the action &lt;em&gt;b&lt;/em&gt;, which forces
its side effects to occur.&lt;/p&gt;


	&lt;p&gt;In reality, &lt;em&gt;a&lt;/em&gt; and &lt;em&gt;b&lt;/em&gt; are not actions.  They are
functions.  Like all Haskell functions, they are pure and have no side
effects.  It&amp;#8217;s just that &lt;em&gt;a&lt;/em&gt; and &lt;em&gt;b&lt;/em&gt; return values that
represent actions, and those actions may have side effects, and the
semantics of the &lt;em&gt;IO&lt;/em&gt; monad guarantee the ordering of those effects
(should the actions end up being connected to the runtime&amp;#8217;s
top-level &lt;em&gt;IO&lt;/em&gt; action and executed).  If you think that&amp;#8217;s weird, hold
that thought.  For now, all that&amp;#8217;s important is that, if the composite
action represented by the expression
(&lt;em&gt;a&lt;/em&gt;&amp;#160;&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;&amp;#160;&lt;em&gt;b&lt;/em&gt;) is executed, the
effects of &lt;em&gt;a&lt;/em&gt;, regardless of how complex, will be executed
before the effects of &lt;em&gt;b&lt;/em&gt;.&lt;/p&gt;


	&lt;p&gt;Thus if &lt;em&gt;a&lt;/em&gt; represents building a tree by recursively scanning
a file-system hierarchy, the &lt;em&gt;entire&lt;/em&gt; tree must be built before
&lt;em&gt;b&lt;/em&gt; ever gets a chance to do its thing.  For our particular
application, however, that particular sequencing is suboptimal.  We
know from our earlier, monolithic implementation that the 
file-system hierarchy can be scanned and printed simultaneously, which
is more efficient.  Ideally, then, our refactored
implementation should be just as efficient.&lt;/p&gt;


	&lt;p&gt;In this article, we will look at one way to maintain the clean,
logical separation of the &lt;em&gt;a&lt;/em&gt; part from the &lt;em&gt;b&lt;/em&gt; part
while allowing the parts&amp;#8217; effects to be interleaved for efficiency.
We will use an extension to the Haskell language to make the
directory-scanning action lazy so that it builds the tree as the tree
is consumed.&lt;/p&gt;


	&lt;p&gt;Ready?  Let&amp;#8217;s dive in.&lt;/p&gt;&lt;h3&gt; Lazy I/O&lt;/h3&gt;


	&lt;p&gt;I&amp;#8217;ll be up front: we are going to be naughty.  We are going to break
the effect-ordering guarantees of the &lt;em&gt;IO&lt;/em&gt; monad, but, if we are
careful, we can get away with it.  In fact, if you have used Haskell&amp;#8217;s
&lt;em&gt;getContents&lt;/em&gt; or &lt;em&gt;readFile&lt;/em&gt; functions, you have already used the
fruits of this very naughtiness.&lt;/p&gt;


	&lt;p&gt;Before we go down the dark path, however, let&amp;#8217;s spend a moment
understanding why we might want to be naughty.  Currently, our
tree-building code builds the entire tree for a file-system hierarchy
as one large &lt;em&gt;IO&lt;/em&gt; action.  If the hierarchy turns out to be large, the
tree could gobble up lots of memory before the printing process has
the chance to consume it.  Further, if the underlying file-system
operations are expensive, we could wait a long time before seeing any
output from our program.&lt;/p&gt;


	&lt;p&gt;If we could produce the tree lazily, both of these problems would go
away.  As we built the tree, our tree-printing process would consume
it, keeping the overall memory footprint low and keeping us informed
of progress with timely output.&lt;/p&gt;


	&lt;p&gt;To better visualize our tree-building code&amp;#8217;s laziness or lack
thereof, let&amp;#8217;s artificially raise the cost of visiting a file-system
node.  We will introduce &lt;em&gt;fsVisit&lt;/em&gt; to simulate a costly file-system
operation that takes 0.1 second to complete:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;fsVisit&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='conid'&gt;DentName&lt;/span&gt;
&lt;span class='varid'&gt;fsVisit&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='keyword'&gt;_&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='varid'&gt;name&lt;/span&gt;&lt;span class='layout'&gt;)&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;usleep&lt;/span&gt; &lt;span class='num'&gt;100000&lt;/span&gt;
    &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='varid'&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Now let&amp;#8217;s work &lt;em&gt;fsVisit&lt;/em&gt; into each step of our file-system-traversal
logic by revising &lt;em&gt;fsTraverseStep&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;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='varid'&gt;dnode&lt;/span&gt;&lt;span class='keyglyph'&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;node&lt;/span&gt;&lt;span class='layout'&gt;)&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;name&lt;/span&gt;     &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;fsVisit&lt;/span&gt; &lt;span class='varid'&gt;dnode&lt;/span&gt;
    &lt;span class='varid'&gt;children&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&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='varid'&gt;return&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;name&lt;/span&gt;&lt;span class='layout'&gt;,&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;Now have a slow version of our original
&lt;strong&gt;tlist&lt;/strong&gt; program.  I recommend that you
&lt;a href="http://community.moertel.com/~thor/directory-printing/tlist-slow.hs"&gt;download the source code&lt;/a&gt; and compile it so that you can
run the program locally and experience the timing of its output.&lt;/p&gt;


	&lt;p&gt;If you run the program on a directory that contains a handful of
files, it will pause for a second or two while it performs our
artificially expensive directory scan.  Finally, the scan completed,
the program will print the directory tree all at once.&lt;/p&gt;


	&lt;p&gt;Now let&amp;#8217;s make the tree-building process lazy.  For this, we need the
&lt;em&gt;unsafeInterleaveIO&lt;/em&gt; combinator, an extension to Haskell 98.  (We will
examine why it is &amp;#8220;unsafe&amp;#8221; later.) Here is its type signature:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;unsafeInterleaveIO&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='varid'&gt;a&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='varid'&gt;a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;What the combinator does is convert an action into a promise to
perform the action later, should its result be needed.  It takes an
&lt;em&gt;IO&lt;/em&gt; action, which might take a long time to carry out, and returns
a substitute action that produces a value immediately.  That value, however, is
a placeholder: when you try to consume it, you will be made to wait
while the real value is computed by performing the original action.&lt;/p&gt;


	&lt;p&gt;This ability to defer work via promises is exactly what we need to
make our directory scans lazy.  We can return each node of the tree as
a pair of promises, one to compute the node&amp;#8217;s root, and one to compute
the node&amp;#8217;s children.  Let&amp;#8217;s modify &lt;em&gt;fsTraverseStep&lt;/em&gt; to do just that:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&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;IO&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Unsafe&lt;/span&gt; &lt;span class='comment'&gt;-- add to imports at top of code&lt;/span&gt;

&lt;span class='varid'&gt;fsTraverseStep&lt;/span&gt; &lt;span class='varid'&gt;dnode&lt;/span&gt;&lt;span class='keyglyph'&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;node&lt;/span&gt;&lt;span class='layout'&gt;)&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;name&lt;/span&gt;     &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;unsafeInterleaveIO&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;fsVisit&lt;/span&gt; &lt;span class='varid'&gt;dnode&lt;/span&gt;
    &lt;span class='varid'&gt;children&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;unsafeInterleaveIO&lt;/span&gt; &lt;span class='varop'&gt;$&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='varid'&gt;return&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;name&lt;/span&gt;&lt;span class='layout'&gt;,&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;Now run this modified version of the program.  (For convenience, you
can &lt;a href="http://community.moertel.com/~thor/directory-printing/tlist-slow-lazy.hs"&gt;download this version of the source
code&lt;/a&gt;.)
Notice how each line of the directory tree is printed as it is visited
in the file-system scan.  We have made our tree-building process lazy!&lt;/p&gt;


	&lt;p&gt;Or have we?  Let&amp;#8217;s modify &lt;em&gt;fsGetChildren&lt;/em&gt; to print the list of
visible children as they are discovered:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&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;print&lt;/span&gt; &lt;span class='varid'&gt;visibles&lt;/span&gt;  &lt;span class='comment'&gt;-- &amp;lt;--- add this line&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;Recompiling and re-running our &amp;#8220;lazy&amp;#8221; program shows that all of the
children are found before any of the printing occurs.  In other words,
even if we are visiting each node lazily, we are still &lt;em&gt;finding&lt;/em&gt; all
of the nodes in the hierarchy before the first node of the resulting
tree is emitted from &lt;em&gt;fsTraverse&lt;/em&gt;.&lt;/p&gt;


	&lt;p&gt;To see why, let&amp;#8217;s examine the source code for &lt;em&gt;unfoldTreeM&lt;/em&gt;, which is
the library function we used to build the tree using our &lt;em&gt;fsTraverseStep&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;unfoldTreeM&lt;/span&gt; &lt;span class='keyglyph'&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='conid'&gt;IO&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='conid'&gt;IO&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;span class='varid'&gt;unfoldTreeM&lt;/span&gt; &lt;span class='varid'&gt;step&lt;/span&gt; &lt;span class='varid'&gt;seed&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;root&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='varid'&gt;seeds&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;step&lt;/span&gt; &lt;span class='varid'&gt;seed&lt;/span&gt;                &lt;span class='comment'&gt;-- (1)&lt;/span&gt;
    &lt;span class='varid'&gt;children&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&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;unfoldTreeM&lt;/span&gt; &lt;span class='varid'&gt;step&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;seeds&lt;/span&gt; &lt;span class='comment'&gt;-- (2)&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;root&lt;/span&gt; &lt;span class='varid'&gt;children&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;               &lt;span class='comment'&gt;-- (3)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;When &lt;em&gt;unfoldTreeM&lt;/em&gt; calls our step function in line (1), it gets back a
pair of promises, one for the &lt;em&gt;root&lt;/em&gt; of the tree and another for the
&lt;em&gt;seeds&lt;/em&gt; from which to build the root&amp;#8217;s children.  In line (2), we see
the problem: In order to convert &lt;em&gt;seeds&lt;/em&gt; into a list of child nodes,
&lt;em&gt;unfoldTreeM&lt;/em&gt; calls &lt;em&gt;mapM&lt;/em&gt; to recursively apply itself to &lt;em&gt;seeds&lt;/em&gt;.
The &lt;em&gt;mapM&lt;/em&gt; action is sequenced before the &lt;em&gt;return&lt;/em&gt; in line (3), and
thus the value of &lt;em&gt;seeds&lt;/em&gt; is consumed before the new tree &lt;em&gt;Node&lt;/em&gt; can
be constructed.  In other words, because the &lt;em&gt;mapM&lt;/em&gt; action isn&amp;#8217;t
deferred and because it depends on the value of &lt;em&gt;seeds&lt;/em&gt;, it forces us
to make good on our &lt;em&gt;seeds&lt;/em&gt; promise earlier than we
would like.&lt;/p&gt;


	&lt;p&gt;To get around this problem, we can create a lazy variant
of &lt;em&gt;unfoldTreeM&lt;/em&gt;.  This version defers the &lt;em&gt;mapM&lt;/em&gt; action
until the &lt;em&gt;children&lt;/em&gt; value is consumed:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;lazyUnfoldTreeM&lt;/span&gt; &lt;span class='keyglyph'&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='conid'&gt;IO&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='conid'&gt;IO&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;span class='varid'&gt;lazyUnfoldTreeM&lt;/span&gt; &lt;span class='varid'&gt;step&lt;/span&gt; &lt;span class='varid'&gt;seed&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;root&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='varid'&gt;seeds&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;step&lt;/span&gt; &lt;span class='varid'&gt;seed&lt;/span&gt;
    &lt;span class='varid'&gt;children&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;unsafeInterleaveIO&lt;/span&gt; &lt;span class='varop'&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;lazyUnfoldTreeM&lt;/span&gt; &lt;span class='varid'&gt;step&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;seeds&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;root&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;Then we can use this version in our high-level &lt;em&gt;fsTraverse&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;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;lazyUnfoldTreeM&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;These changes made, we have a fully lazy version of the
tree-building code.  To see it in action, &lt;a href="http://community.moertel.com/~thor/directory-printing/tlist-slow-lazy2.hs"&gt;download this version of
the whole program&amp;#8217;s source
code&lt;/a&gt;,
compile it, and give it a try.  If you add the &lt;em&gt;print&lt;/em&gt; line to
&lt;em&gt;fsGetChildren&lt;/em&gt; like we did earlier, you&amp;#8217;ll see that this version
expands each directory only when its contents are needed.  Now
our lazy directory-tree-building code works as we would expect.&lt;/p&gt;


	&lt;p&gt;Mission accomplished.&lt;/p&gt;


	&lt;h3&gt;Optional: Hiding the plumbing&lt;/h3&gt;


	&lt;p&gt;[&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;The technique described in this section is hacky.  I&amp;#8217;m
including it because it&amp;#8217;s fun, not because I think it&amp;#8217;s good
Haskell-programming practice.&lt;/em&gt;]&lt;/p&gt;


	&lt;p&gt;It&amp;#8217;s unfortunate that we had to create our own version of the library
function &lt;em&gt;unfoldTreeM&lt;/em&gt;.  With its introduction, we lost some of our
code&amp;#8217;s tidiness.  Can we get our clean code back?&lt;/p&gt;


	&lt;p&gt;Maybe we can.  Recall that &lt;em&gt;unfoldTreeM&lt;/em&gt; works for any monad.  What if
we created a &lt;em&gt;LazyIO&lt;/em&gt; monad that was just like &lt;em&gt;IO&lt;/em&gt; except that each
action within it was performed only when its value was consumed?  With
this monad, we could eliminate the need for a special, lazy version of
&lt;em&gt;unfoldTreeM&lt;/em&gt;: the normal version would &amp;#8220;do the right thing,&amp;#8221; using our
monad.&lt;/p&gt;


	&lt;p&gt;Let&amp;#8217;s create our &lt;em&gt;LazyIO&lt;/em&gt; monad.  First, we&amp;#8217;ll need a new module,&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;LazyIO&lt;/span&gt;
 &lt;span class='layout'&gt;(&lt;/span&gt; &lt;span class='conid'&gt;LazyIO&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;runLazy&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='varid'&gt;deferIO&lt;/span&gt; &lt;span class='layout'&gt;)&lt;/span&gt;
&lt;span class='keyword'&gt;where&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;and, of course, we&amp;#8217;ll need to import &lt;em&gt;unsafeInterleaveIO&lt;/em&gt;:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&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;IO&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Unsafe&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;unsafeInterleaveIO&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;We will define &lt;em&gt;LazyIO&lt;/em&gt; to be a type wrapper around the regular &lt;em&gt;IO&lt;/em&gt;
type:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='keyword'&gt;newtype&lt;/span&gt; &lt;span class='conid'&gt;LazyIO&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;LazyIO&lt;/span&gt; &lt;span class='layout'&gt;{&lt;/span&gt; &lt;span class='varid'&gt;unLazyIO&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;IO&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;Note that our module definition, above, does not export the &lt;em&gt;LazyIO&lt;/em&gt;
type&amp;#8217;s data constructor.  The only way, then, to construct and
destruct values will be through our interface.&lt;/p&gt;


	&lt;p&gt;To construct a value, we will provide &lt;em&gt;deferIO&lt;/em&gt;, which converts a normal
&lt;em&gt;IO&lt;/em&gt; action into a &lt;em&gt;LazyIO&lt;/em&gt; action:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;deferIO&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;IO&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;LazyIO&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt;
&lt;span class='varid'&gt;deferIO&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;LazyIO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;To get a regular &lt;em&gt;IO&lt;/em&gt; action &amp;#8220;out&amp;#8221; of our monad, we will provide &lt;em&gt;runLazy&lt;/em&gt;
which converts a &lt;em&gt;LazyIO&lt;/em&gt; action into an &lt;em&gt;IO&lt;/em&gt; action that is deferred
until it is consumed:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;runLazy&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;LazyIO&lt;/span&gt; &lt;span class='varid'&gt;a&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='varid'&gt;a&lt;/span&gt;
&lt;span class='varid'&gt;runLazy&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;unsafeInterleaveIO&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;unLazyIO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;The key to our monad is that the only way to get an &lt;em&gt;IO&lt;/em&gt; action
out of &lt;em&gt;LazyIO&lt;/em&gt; is through &lt;em&gt;runLazy&lt;/em&gt; and thus through
&lt;em&gt;unsafeInterleaveIO&lt;/em&gt;.  In fact, injecting an &lt;em&gt;IO&lt;/em&gt; action into our
monad and then immediately pulling it out again is identical to deferring
it with &lt;em&gt;unsafeInterleaveIO&lt;/em&gt;:&lt;/p&gt;


&lt;p style="text-align:center"&gt;
(&lt;em&gt;runLazy&lt;/em&gt;&amp;#160;.&amp;#160;&lt;em&gt;deferIO&lt;/em&gt;)&amp;#160;=&amp;#160;&lt;em&gt;unsafeInterleaveIO&lt;/em&gt;
&lt;/p&gt;

	&lt;p&gt;But what makes a monad interesting are its &lt;em&gt;return&lt;/em&gt; and &lt;em&gt;bind&lt;/em&gt;
definitions.  Here are ours:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='keyword'&gt;instance&lt;/span&gt; &lt;span class='conid'&gt;Monad&lt;/span&gt; &lt;span class='conid'&gt;LazyIO&lt;/span&gt; &lt;span class='keyword'&gt;where&lt;/span&gt;
    &lt;span class='varid'&gt;return&lt;/span&gt;     &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;LazyIO&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;return&lt;/span&gt;
    &lt;span class='varid'&gt;ma&lt;/span&gt; &lt;span class='varop'&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class='varid'&gt;fmb&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;LazyIO&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;runLazy&lt;/span&gt; &lt;span class='varid'&gt;ma&lt;/span&gt; &lt;span class='varop'&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class='varid'&gt;unLazyIO&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;fmb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Our &lt;em&gt;return&lt;/em&gt; simply returns the given value in the underlying &lt;em&gt;IO&lt;/em&gt;
monad and wraps the resulting action with the &lt;em&gt;LazyIO&lt;/em&gt; constructor.
The implication is that the only way to unwrap the action is via
&lt;em&gt;runLazy&lt;/em&gt;, which will defer it.&lt;/p&gt;


	&lt;p&gt;Likewise, our &lt;em&gt;bind&lt;/em&gt; (&lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;) creates a composite &lt;em&gt;LazyIO&lt;/em&gt;
action from another &lt;em&gt;LazyIO&lt;/em&gt; action and a function that produces
a &lt;em&gt;LazyIO&lt;/em&gt; action given the result of the first action.  The
composite action and the first action are deferred.  (Deferring
the action produced by &lt;em&gt;fmb&lt;/em&gt; would be redundant because the
value it produces is the value of the composite action, which
is already deferred.)&lt;/p&gt;


	&lt;p&gt;&lt;strong&gt;Exercise:&lt;/strong&gt;  Is &lt;em&gt;LazyIO&lt;/em&gt; really a monad?  Prove (or disprove) that
it satisfies the monad laws.&lt;/p&gt;


	&lt;p&gt;To see the effect of our monad, let&amp;#8217;s apply its definitions to the
following simple lazy IO action.&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;runLazy&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;x&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='num'&gt;5&lt;/span&gt;
    &lt;span class='varid'&gt;deferIO&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;print&lt;/span&gt; &lt;span class='varid'&gt;x&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;With a little expansion, application, and simplification (try
it), we arrive at the following:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;unsafeInterleaveIO&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;x&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;unsafeInterleaveIO&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='num'&gt;5&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
    &lt;span class='varid'&gt;print&lt;/span&gt; &lt;span class='varid'&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Note that the entire, composite IO action is deferred, as are the
actions upon which it depends, which is exactly what we want.&lt;/p&gt;


	&lt;p&gt;Now we can return to housecleaning.  With our new monad, we can banish
both &lt;em&gt;unsafeInterleaveIO&lt;/em&gt; and our custom, lazy version of
&lt;em&gt;unfoldTreeM&lt;/em&gt; from our otherwise uncluttered code.&lt;/p&gt;


	&lt;p&gt;Step one of the housecleaning is to modify &lt;em&gt;fsTraverseStep&lt;/em&gt; to use the
&lt;em&gt;LazyIO&lt;/em&gt; monad instead of explicit calls to &lt;em&gt;unsafeInterleaveIO&lt;/em&gt;:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='keyword'&gt;import&lt;/span&gt; &lt;span class='conid'&gt;LazyIO&lt;/span&gt;   &lt;span class='comment'&gt;-- add to module imports at top of code&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;LazyIO&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='varid'&gt;dnode&lt;/span&gt;&lt;span class='keyglyph'&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;node&lt;/span&gt;&lt;span class='layout'&gt;)&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;name&lt;/span&gt;     &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;deferIO&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;fsVisit&lt;/span&gt; &lt;span class='varid'&gt;dnode&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
    &lt;span class='varid'&gt;children&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;deferIO&lt;/span&gt; &lt;span class='layout'&gt;(&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='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='varid'&gt;name&lt;/span&gt;&lt;span class='layout'&gt;,&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;Step two is to rewrite &lt;em&gt;fsTraverse&lt;/em&gt; in terms of &lt;em&gt;runLazy&lt;/em&gt; and the
plain-old &lt;em&gt;unfoldTreeM&lt;/em&gt; provided by the &lt;em&gt;Data.Tree&lt;/em&gt; library:&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;p&lt;/span&gt; &lt;span class='varid'&gt;n&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;runLazy&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;p&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt;&lt;span class='varid'&gt;n&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Step three is to remove &lt;em&gt;lazyUnfoldTreeM&lt;/em&gt; from our code: &lt;em&gt;Snip&lt;/em&gt;!&lt;/p&gt;


	&lt;p&gt;And we&amp;#8217;re done.  The code:&lt;/p&gt;


	&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://community.moertel.com/~thor/directory-printing/tlist-slow-lazy-final.hs"&gt;tlist-slow-lazy-final.hs&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="http://community.moertel.com/~thor/directory-printing/LazyIO.hs"&gt;LazyIO.hs&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;


	&lt;p&gt;To compile, here&amp;#8217;s the command to use:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;ghc -O2 -o tlist-slow-lazy-final --make tlist-slow-lazy-final.hs
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Give the program a test run and convince yourself that it works as
advertised.&lt;/p&gt;


	&lt;h3&gt;Why unsafeInterleaveIO is &amp;#8220;unsafe&amp;#8221;&lt;/h3&gt;


	&lt;p&gt;Now let&amp;#8217;s get back to the &amp;#8220;unsafe&amp;#8221; part of &lt;em&gt;unsafeInterleaveIO&lt;/em&gt;.
What&amp;#8217;s that about?&lt;/p&gt;


	&lt;p&gt;Haskell programmers label as &amp;#8220;unsafe&amp;#8221; those functions that allow you
to circumvent Haskell&amp;#8217;s safety guarantees.  It&amp;#8217;s a blunt way of
letting you know that, if you use one of these functions, you can
shoot yourself in the foot if your aim is bad.  Thus, by using an
&amp;#8220;unsafe&amp;#8221; function, you place the burden of proving that your code is
safe upon yourself.&lt;/p&gt;


	&lt;p&gt;As we have already seen, we can use &lt;em&gt;unsafeInterleaveIO&lt;/em&gt; to get around
the side-effect ordering guarantees that Haskell&amp;#8217;s &lt;em&gt;IO&lt;/em&gt; monad normally
provides.  If we&amp;#8217;re not careful, this could come back to bite us.&lt;/p&gt;


	&lt;p&gt;Using &lt;em&gt;unsafeInterleaveIO&lt;/em&gt; on an action effectively removes the action
from the normal world of &lt;em&gt;IO&lt;/em&gt; actions and places it into its own
parallel world, where it can run on its own schedule.  Creating these
parallel worlds is safe as long as we don&amp;#8217;t attempt to do anything
that depends upon the ordering of effects across worlds.&lt;/p&gt;


	&lt;p&gt;For example, take a look at this small Haskell program that
will let us compare the output of a simple test under normal
and unsafe-interleaved I/O sequencing:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='comment'&gt;{- file: unsafe-demo.hs -}&lt;/span&gt;

&lt;span class='keyword'&gt;module&lt;/span&gt; &lt;span class='conid'&gt;Main&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;System&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Environment&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;IO&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Unsafe&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='keyword'&gt;if&lt;/span&gt; &lt;span class='varid'&gt;args&lt;/span&gt; &lt;span class='varop'&gt;==&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='str'&gt;"lazy"&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;
        &lt;span class='keyword'&gt;then&lt;/span&gt; &lt;span class='varid'&gt;doTest&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;unsafeInterleaveIO&lt;/span&gt; &lt;span class='varid'&gt;getLine&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
        &lt;span class='keyword'&gt;else&lt;/span&gt; &lt;span class='varid'&gt;doTest&lt;/span&gt; &lt;span class='varid'&gt;getLine&lt;/span&gt;

&lt;span class='varid'&gt;doTest&lt;/span&gt; &lt;span class='varid'&gt;myGetLine&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;line1&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;myGetLine&lt;/span&gt;    &lt;span class='comment'&gt;-- (1)&lt;/span&gt;
    &lt;span class='varid'&gt;line2&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;myGetLine&lt;/span&gt;    &lt;span class='comment'&gt;-- (2)&lt;/span&gt;
    &lt;span class='varid'&gt;putStrLn&lt;/span&gt; &lt;span class='varid'&gt;line2&lt;/span&gt;
    &lt;span class='varid'&gt;putStrLn&lt;/span&gt; &lt;span class='varid'&gt;line1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;The &lt;em&gt;main&lt;/em&gt; function checks the command line to see if we have passed
the &amp;#8220;lazy&amp;#8221; argument.  If so, it passes (&lt;em&gt;unsafeInterleaveIO&lt;/em&gt; &lt;em&gt;getLine&lt;/em&gt;)
to &lt;em&gt;doTest&lt;/em&gt;; otherwise, it passes the straight &lt;em&gt;getLine&lt;/em&gt; function.&lt;/p&gt;


	&lt;p&gt;The &lt;em&gt;doTest&lt;/em&gt; function, in turn, constructs an &lt;em&gt;IO&lt;/em&gt; action that reads
two lines from the standard input using whichever version of &lt;em&gt;getLine&lt;/em&gt;
we have provided.  First it reads &lt;em&gt;line1&lt;/em&gt;, then &lt;em&gt;line2&lt;/em&gt;.  Finally, it
prints the lines in the opposite order: first &lt;em&gt;line2&lt;/em&gt;, then &lt;em&gt;line1&lt;/em&gt;.&lt;/p&gt;


	&lt;p&gt;Let&amp;#8217;s test this program on the following two-line file:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;$ cat onetwo.txt
one
two
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;Ready?  First, let&amp;#8217;s run the straight-&lt;em&gt;IO&lt;/em&gt; version:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;$ runhaskell unsafe-demo.hs &amp;lt; onetwo.txt
two
one
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;As we would expect, the program read the lines from the file and printed
them in reverse order.&lt;/p&gt;


	&lt;p&gt;Now, let&amp;#8217;s pass the &amp;#8220;lazy&amp;#8221; flag, which wraps the &lt;em&gt;getLine&lt;/em&gt; calls
with &lt;em&gt;unsafeInterleaveIO&lt;/em&gt;:&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;$ runhaskell unsafe-demo.hs lazy &amp;lt; onetwo.txt
one
two
&lt;/code&gt;&lt;/pre&gt;

	&lt;p&gt;For some reason, when we printed &lt;em&gt;line2&lt;/em&gt; we got the first
line of the file, not the second.  Likewise, when we printed &lt;em&gt;line1&lt;/em&gt;,
we got the second line.  What happened?&lt;/p&gt;


	&lt;p&gt;What happened is that in lines (1) and (2) of the code, &lt;em&gt;myGetLine&lt;/em&gt;
evaluated to (&lt;em&gt;unsafeInterleaveIO&lt;/em&gt; &lt;em&gt;getLine&lt;/em&gt;).  Thus &lt;em&gt;line1&lt;/em&gt; and
&lt;em&gt;line2&lt;/em&gt; were bound to placeholder values that were connected to
deferred &lt;em&gt;getLine&lt;/em&gt; actions, each existing in its own parallel
world and utterly unconnected to each other or to the main, normal
&lt;em&gt;IO&lt;/em&gt; world.  The &lt;em&gt;only&lt;/em&gt; thing, then, that could determine the order in
which the two actions were performed was the order in which their
values were consumed.  And, because we printed &lt;em&gt;line2&lt;/em&gt; first, its
deferred &lt;em&gt;getLine&lt;/em&gt; action was the first to be performed, and so it
read the &lt;em&gt;first&lt;/em&gt; line of the input from the file.  By the time we
printed &lt;em&gt;line1&lt;/em&gt;, causing its deferred &lt;em&gt;getLine&lt;/em&gt; action to be
performed, the first line of the file had already been consumed, and
so the second line was returned.&lt;/p&gt;


	&lt;p&gt;Oops.&lt;/p&gt;


	&lt;p&gt;After this demonstration, we might wonder whether our lazy
tree-building code is safe.  After all, we&amp;#8217;re using
&lt;em&gt;unsafeInterleaveIO&lt;/em&gt; like crazy behind the scenes.  Let&amp;#8217;s
think about it.&lt;/p&gt;


	&lt;p&gt;Recall that, once detached from the main &lt;em&gt;IO&lt;/em&gt; world, the only
thing that determines when a deferred action is performed is
when its value is consumed.  That knowledge gives us the key
to unlock the safety argument for our tree-building code.&lt;/p&gt;


	&lt;p&gt;Recall the type of &lt;em&gt;fsTraverse&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;The action that &lt;em&gt;fsTraverse&lt;/em&gt; produces returns a &lt;em&gt;DirTree&lt;/em&gt; value,
which is a type synonym for a &lt;em&gt;Tree&lt;/em&gt; of &lt;em&gt;DentName&lt;/em&gt; values (file
names).  The &lt;em&gt;Tree&lt;/em&gt; data type has only one constructor:&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='keyglyph'&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='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;Each &lt;em&gt;Node&lt;/em&gt; represents a tree: a root label and a sub-forest of
children trees.  Because of this representation, we cannot visit a
node in a tree without first having visited all of its ancestors.  In
order to get to a child node in the tree, for example, we must go
through the &lt;em&gt;subForest&lt;/em&gt; value of its parent.  Attempting to consume that
value will force the attached, deferred action to be performed.  That
action, in turn, will fetch the contents of the parent&amp;#8217;s directory and
package each item, including the child we&amp;#8217;re trying to get, into its
own &lt;em&gt;Node&lt;/em&gt;, the &lt;em&gt;root&lt;/em&gt; and &lt;em&gt;subForest&lt;/em&gt; of which will contain deferred
actions for scanning further into the file-system hierarchy.  Thus a
top-down sequencing will be imposed on the deferred &lt;em&gt;IO&lt;/em&gt; actions
attached to those nodes.  Fortunately, we &lt;em&gt;want&lt;/em&gt; a top-down traversal
because that&amp;#8217;s the way we must descend directory hierarchies in the
file system.  Therefore, we have good reason to be assured of the
safety of using &lt;em&gt;unsafeInterleaveIO&lt;/em&gt; for this particular application.&lt;/p&gt;


	&lt;p&gt;For other applications, however, we must repeat this safety analysis
anew, lest we allow the parallel words of our deferred actions to
collide in unpleasant ways.  &lt;em&gt;Caveat coder.&lt;/em&gt;&lt;/p&gt;


	&lt;h3&gt; Review and next time&lt;/h3&gt;


	&lt;p&gt;In this installment of our ever-lengthening series of articles on
directory printing, we revised our refactored directory-printing code
to restore the efficiency of our original, monolithic implementation.
In doing so, we experimented with lazy I/O and deferring &lt;em&gt;IO&lt;/em&gt; actions
by using the &lt;em&gt;unsafeInterleaveIO&lt;/em&gt; function.&lt;/p&gt;


	&lt;p&gt;At first we inserted a customized version of the library function
&lt;em&gt;unfoldTreeM&lt;/em&gt; into our code to effect the desired laziness, but a
little monad hacking allowed us to get rid of this intrusion.  When we
were done, we not only had a lean, lazy directory printer but also a
new (and potentially unsafe) &lt;em&gt;LazyIO&lt;/em&gt; library, which might prove
useful in the future.&lt;/p&gt;


	&lt;p&gt;We also looked at the kind of problems that &lt;em&gt;unsafeInterleaveIO&lt;/em&gt; can
cause if we are not careful.  Nevertheless, we were able to argue
that our particular use of this function is safe: the &lt;em&gt;Tree&lt;/em&gt; data
structure ensures that we must consume tree nodes in the top-down
order in which our directory-scanning must occur.&lt;/p&gt;


	&lt;p&gt;Still, our directory-printing code has problems.  We haven&amp;#8217;t yet
improved its lackluster error-handling policy.  Nor have we made the
directory-scanning code amenable to uses beyond our immediate needs
for directory printing.  Next time, we will solve both of these
problems.&lt;/p&gt;</description>
      <pubDate>Wed, 28 Mar 2007 15:40:00 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:ebfb6644-c73e-4f86-af5c-c3fe026b515d</guid>
      <author>Tom Moertel</author>
      <link>http://blog.moertel.com/articles/2007/03/28/directory-tree-printing-in-haskell-part-three-lazy-i-o</link>
      <category>haskell</category>
      <category>haskell</category>
      <category>trees</category>
      <category>io</category>
      <category>directory_tree_series</category>
      <category>laziness</category>
      <category>lazy</category>
      <trackback:ping>http://blog.moertel.com/articles/trackback/420</trackback:ping>
    </item>
    <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>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