<?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 lazy</title>
    <link>http://blog.moertel.com/articles/tag/lazy?tag=lazy</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>
  </channel>
</rss>
