<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Tom Moertel's Weblog: Directory-tree printing in Haskell, part three: lazy I/O</title>
    <link>http://blog.moertel.com/articles/2007/03/28/directory-tree-printing-in-haskell-part-three-lazy-i-o</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 three: lazy I/O" by petekaz</title>
      <description>&lt;p&gt;Could one replace &amp;#8216;runLazy&amp;#8217; with &amp;#8216;unLazyIO&amp;#8217; in the bind definition below?  Eventually one must call runLazy to extract the IO action out, so do we need to call it here as well?&lt;/p&gt;


	&lt;p&gt;ma &amp;gt;&amp;gt;= fmb = LazyIO $ runLazy ma &amp;gt;&amp;gt;= unLazyIO . fmb&lt;/p&gt;</description>
      <pubDate>Sun, 16 Sep 2007 21:06:56 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:7d8c3eec-dc39-4325-b46f-5e75aa8f7d50</guid>
      <link>http://blog.moertel.com/articles/2007/03/28/directory-tree-printing-in-haskell-part-three-lazy-i-o#comment-582</link>
    </item>
    <item>
      <title>"Directory-tree printing in Haskell, part three: lazy I/O" by James Graves</title>
      <description>&lt;p&gt;Great series of posts Tom.  Thanks.  It is an easily approachable subject, and the iterative refinement follows the path that we imperative programmers must follow while learning Haskell.&lt;/p&gt;


	&lt;p&gt;Please keep up the good work.&lt;/p&gt;</description>
      <pubDate>Wed, 04 Apr 2007 20:29:37 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:4c721ffb-35bb-41a9-b718-a24c93184d4c</guid>
      <link>http://blog.moertel.com/articles/2007/03/28/directory-tree-printing-in-haskell-part-three-lazy-i-o#comment-435</link>
    </item>
    <item>
      <title>"Directory-tree printing in Haskell, part three: lazy I/O" by petekaz</title>
      <description>&lt;p&gt;Would these more elegant fusions be similar to the left-fold enumerators described by Oleg?&lt;/p&gt;


	&lt;p&gt;&lt;a href="http://okmij.org/ftp/Haskell/misc.html#fold"&gt;http://okmij.org/ftp/Haskell/misc.html#fold&lt;/a&gt;-stream&lt;/p&gt;


	&lt;p&gt;In my limited time with Haskell, I&amp;#8217;ve found lazy IO (getContents/readFile) to be very handy at times allowing me to program in a very straightforward stream-like manner.&lt;/p&gt;


	&lt;p&gt;However, my naivety recently got me into trouble while using lazy IO to parse a directory full of emails.  As soon as the directory exceeded 1024 files, my program exceeded the maximum allowed open files and crashed.  It seems that GHC does not close file handles that are not being used anymore until a garbage collection occurs, and by that point it was too late for my program.&lt;/p&gt;


	&lt;p&gt;What did I learn?  Lazy IO can be handy at times as long as one realizes they lose a certain amount of control of resource allocation/deallocation.  I was referred to Oleg&amp;#8217;s paper on the left-fold enumerator which seems like it would be a good alternative to the lazy IO approach described in this installment.&lt;/p&gt;


	&lt;p&gt;With that said, I really enjoyed this article on how to interleave IO actions.  I&amp;#8217;m sure I&amp;#8217;ll apply this technique at some point.&lt;/p&gt;


	&lt;p&gt;Finally, one last question, do these IO cheats rely on unsafePerformIO?  I remember reading a comment on the implementation of GHC on multi-processor machines where the use of this function could be non-deterministic (see section 3.5 of &lt;a href="http://research.microsoft.com/"&gt;http://research.microsoft.com/&lt;/a&gt;~tharris/papers/2005-haskell.pdf) resulting in IO actions being executed twice.  Does the interleave function fall into this category?&lt;/p&gt;


	&lt;p&gt;Thanks,
Pete&lt;/p&gt;</description>
      <pubDate>Thu, 29 Mar 2007 18:52:33 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:94f9dd71-7f8b-4336-8464-96dfd0cd7232</guid>
      <link>http://blog.moertel.com/articles/2007/03/28/directory-tree-printing-in-haskell-part-three-lazy-i-o#comment-425</link>
    </item>
    <item>
      <title>"Directory-tree printing in Haskell, part three: lazy I/O" by Tom Moertel</title>
      <description>&lt;p&gt;Phil and Ithika,&lt;/p&gt;


	&lt;p&gt;Thanks for your questions, which accurately forecast the topic for the next article.  &lt;code&gt;;-)&lt;/code&gt;&lt;/p&gt;


	&lt;p&gt;As a preview, yes, you can fuse the scanning and the printing. The solution in the &lt;a href="http://blog.moertel.com/articles/2007/02/22/a-simple-directory-tree-printer-in-haskell"&gt;first article&lt;/a&gt; does just that.  But more elegant fusions are possible, as I hope to explore shortly.&lt;/p&gt;


	&lt;p&gt;Cheers! &amp;#8212;Tom&lt;/p&gt;</description>
      <pubDate>Thu, 29 Mar 2007 10:25:15 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:4890606e-fe6e-4e43-9249-e5cf1716ea54</guid>
      <link>http://blog.moertel.com/articles/2007/03/28/directory-tree-printing-in-haskell-part-three-lazy-i-o#comment-424</link>
    </item>
    <item>
      <title>"Directory-tree printing in Haskell, part three: lazy I/O" by Ithika</title>
      <description>&lt;p&gt;Phil, I wondering that myself.&lt;/p&gt;


	&lt;p&gt;Tom, is it beyond the realms of ugliness to have the top level function feed results a bit at a time from filesystem to pretty-printer? Or is it not so easy to do?&lt;/p&gt;</description>
      <pubDate>Thu, 29 Mar 2007 08:57:32 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:78f5b95f-4f93-410d-a952-8a443c8df927</guid>
      <link>http://blog.moertel.com/articles/2007/03/28/directory-tree-printing-in-haskell-part-three-lazy-i-o#comment-423</link>
    </item>
    <item>
      <title>"Directory-tree printing in Haskell, part three: lazy I/O" by Phil Armstrong</title>
      <description>&lt;p&gt;Can&amp;#8217;t you fuse the scanning of the directory structure with the printing &amp;#38; thereby avoid the need for unsafeperformIO?&lt;/p&gt;</description>
      <pubDate>Thu, 29 Mar 2007 08:12:02 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:d8f30a56-6f43-4d74-b881-e8f0542c1d81</guid>
      <link>http://blog.moertel.com/articles/2007/03/28/directory-tree-printing-in-haskell-part-three-lazy-i-o#comment-422</link>
    </item>
  </channel>
</rss>
