<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.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>Random Hacks: Haskell: Queues without pointers</title>
    <link>http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Technology and Other Fun Stuff</description>
    <item>
      <title>"Haskell: Queues without pointers" by Eric Kidd</title>
      <description>&lt;p&gt;kumma: &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt; are easy enough (and fast enough, too).  But to implement a queue, you also need some sort of &amp;#8220;enqueue&amp;#8221; operation.  And that can get really slow, if you&amp;#8217;re not careful.&lt;/p&gt;


	&lt;p&gt;Specifically, consider the following implementation:&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;Queue&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;Queue&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='varid'&gt;a&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;

&lt;span class='varid'&gt;newQueue&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;Queue&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt;

&lt;span class='varid'&gt;deq&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;Queue&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='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;error&lt;/span&gt; &lt;span class='str'&gt;"Empty queue"&lt;/span&gt;
&lt;span class='varid'&gt;deq&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;Queue&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;x&lt;/span&gt;&lt;span class='conop'&gt;:&lt;/span&gt;&lt;span class='varid'&gt;xs&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;x&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt;&lt;span class='varid'&gt;xs&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;

&lt;span class='varid'&gt;enq&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;Queue&lt;/span&gt; &lt;span class='varid'&gt;xs&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;x&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;Queue&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;xs&lt;/span&gt; &lt;span class='varop'&gt;++&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;Here, &lt;code&gt;deq&lt;/code&gt; uses &lt;code&gt;head&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt; (in the form of the &lt;code&gt;(x:xs)&lt;/code&gt; pattern match), and it runs in constant time, no matter how big the queue.&lt;/p&gt;


	&lt;p&gt;But &lt;code&gt;enq&lt;/code&gt; uses &lt;code&gt;++&lt;/code&gt;, which is seriously slow, because it makes a complete copy of &lt;code&gt;xs&lt;/code&gt; every time we add an item to the queue. If we keep (say) an average of 1,000 items in the queue, that means we need to copy a 1,000 item list every time we call &lt;code&gt;enq&lt;/code&gt;.&lt;/p&gt;


	&lt;p&gt;The alternate implementation you see above doesn&amp;#8217;t have this problem. In our example, it can &lt;code&gt;enq&lt;/code&gt; an item in a single operation 999 times out of 1000, and only reallocate the entire list 1 time in a 1000, instead of every single time. On average, that works out to O(1) time. For 10 lines of Haskell, it&amp;#8217;s actually a pretty decent queue.&lt;/p&gt;</description>
      <pubDate>Mon, 01 Sep 2008 06:47:15 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:b55e9dfa-54fa-477d-b007-651626ec6be8</guid>
      <link>http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers#comment-580</link>
    </item>
    <item>
      <title>"Haskell: Queues without pointers" by kumma</title>
      <description>&lt;p&gt;Are tail and head too complex for use or what?&lt;/p&gt;</description>
      <pubDate>Mon, 01 Sep 2008 04:24:42 -0400</pubDate>
      <guid isPermaLink="false">urn:uuid:8b4e7406-d46c-4169-a201-d1903ba39aae</guid>
      <link>http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers#comment-579</link>
    </item>
    <item>
      <title>"Haskell: Queues without pointers" by Eric</title>
      <description>&lt;p&gt;Well, as with all things, it&amp;#8217;s a tradeoff: More hassle for some problems, less hassle for others.&lt;/p&gt;


	&lt;p&gt;I could easily implement a conventional queue in Haskell, using the IO monad. But purely functional data structures offer a potentially valuable feature: Cheap undo.&lt;/p&gt;


	&lt;p&gt;I once wrote a set of C++ classes which supported a &amp;#8220;undo&amp;#8221; function&amp;mdash;you could revert to any earlier version of the data structure.  All the application&amp;#8217;s data was stored in these structures, so implementing application-level &amp;#8220;undo&amp;#8221; was a snap. But as you can imagine, the C++ classes in question weren&amp;#8217;t pretty&amp;#8212;they had to support the ability to back out any modification.&lt;/p&gt;


	&lt;p&gt;If I had used a purely functional data structure, saving previous states would have been trivial&amp;#8212;just hold onto a reference.&lt;/p&gt;


	&lt;p&gt;Cheap &amp;#8220;undo&amp;#8221; has important consequences in other real-world problems, too. Consider a Sudoku solver: Every time we&amp;#8217;re forced to make a guess, we need to save the state of our program. If we encounter a dead-end, we can backtrack to the saved state.&lt;/p&gt;


	&lt;p&gt;Another good example is a compiler&amp;#8217;s symbol table. In a language with lambdas, we may have functions several layers deep, each with its own lexical scope. Every time we leave a nested function, we need to revert to an earlier version of the symbol table.&lt;/p&gt;


	&lt;p&gt;So these are a few examples of when a purely functional data structure might simplify an application considerably. And in many cases, the price of using a purely functional data structure isn&amp;#8217;t that high: This example requires only 10 lines of Haskell, which compares quite nicely to a conventional (templated) queue in C++.&lt;/p&gt;</description>
      <pubDate>Tue, 27 Feb 2007 07:51:04 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:48b73ad1-f13c-4b8b-b251-2d33c1404f2d</guid>
      <link>http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers#comment-317</link>
    </item>
    <item>
      <title>"Haskell: Queues without pointers" by someone</title>
      <description>&lt;p&gt;If Haskell requires such convoluted acrobatics for a basic data type, how could anyone take it seriously?&lt;/p&gt;</description>
      <pubDate>Tue, 27 Feb 2007 05:45:46 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:7a7969f1-e7fc-4e5a-b569-1a7a6097a24c</guid>
      <link>http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers#comment-316</link>
    </item>
    <item>
      <title>"Haskell: Queues without pointers" by Easy things hard...</title>
      <description>&lt;p&gt;Ah, Haskell&amp;#8230;  Making easy things hard and (some) hard things easy since 1996&amp;#8482;.&lt;/p&gt;</description>
      <pubDate>Wed, 21 Feb 2007 23:18:45 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:5ff40c7b-213a-4f41-a4e6-13f4cd222e18</guid>
      <link>http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers#comment-301</link>
    </item>
    <item>
      <title>"Haskell: Queues without pointers" by Michael</title>
      <description>&lt;p&gt;&lt;i&gt;[Y]ou actually need to do a bit more work to get non-amortized O(1) queues, at least in a lazy language.&lt;/i&gt;&lt;/p&gt;


	&lt;p&gt;That&amp;#8217;s the point I was trying to make, though apparently not clearly.  In order to get rid of the point cost of a &lt;code&gt;rev&lt;/code&gt; you need to make sure you do them lazily, and only at need&amp;#8212;but also sufficiently often that you have enough operations over which to amortize their cost.&lt;/p&gt;


	&lt;p&gt;One way to interpret Okazaki&amp;#8217;s algorithm is to insure that, each time you &lt;code&gt;deq&lt;/code&gt;, you take a step of appending the reverse of the tails to the heads, and of course you memoize the results so that you retain the persistence (if you only care about ephemeral queues, this is of course somewhat easier).  The trick is to make sure you don&amp;#8217;t wait &amp;#8220;too long,&amp;#8221; which you can do by starting a new rev/append as soon as tails is one position longer than heads.  That&amp;#8217;s not the only solution, but it makes the analysis work out nicely.&lt;/p&gt;</description>
      <pubDate>Fri, 09 Feb 2007 17:40:25 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:a677d611-8c96-4921-9e7b-f6c93b37e90c</guid>
      <link>http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers#comment-287</link>
    </item>
    <item>
      <title>"Haskell: Queues without pointers" by Eric</title>
      <description>&lt;p&gt;Michael Chermside: Thanks! I fixed it.&lt;/p&gt;</description>
      <pubDate>Fri, 09 Feb 2007 09:19:02 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:fe199f2f-ab9c-4354-8834-2bd3c733c7aa</guid>
      <link>http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers#comment-285</link>
    </item>
    <item>
      <title>"Haskell: Queues without pointers" by Eric</title>
      <description>&lt;p&gt;According to Okazaki, you actually need to do a bit more work to get non-amortized O(1) queues, at least in a lazy language. He keeps a list of values which need to be forced, and forces one during every operation.&lt;/p&gt;


	&lt;p&gt;I&amp;#8217;d have to work through his example by hand to truly understand the issues involved. I find it hard to reason about when work actually occurs in a lazy language.&lt;/p&gt;


	&lt;p&gt;But, yeah&amp;#8212;awesome book.&lt;/p&gt;</description>
      <pubDate>Fri, 09 Feb 2007 09:00:34 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:6b9bfe04-274f-4e0b-8a67-181615d53227</guid>
      <link>http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers#comment-284</link>
    </item>
    <item>
      <title>"Haskell: Queues without pointers" by Michael Chermside</title>
      <description>&lt;p&gt;Interesting posting! Thanks.&lt;/p&gt;


	&lt;p&gt;Your link to Okaski&amp;#8217;s thesis is incorrect. The correct link is http://www.cs.cmu.edu/~rwh/theses/okasaki.pdf&lt;/p&gt;</description>
      <pubDate>Fri, 09 Feb 2007 09:00:14 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:fb6db6fd-983f-4262-b6e1-c4eb6ecb557e</guid>
      <link>http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers#comment-283</link>
    </item>
    <item>
      <title>"Haskell: Queues without pointers" by Michael</title>
      <description>&lt;p&gt;Generally when you implement this structure, you also want to provide a &lt;code&gt;front&lt;/code&gt; operation, so that you can peek at the front item without removing it (that also permits you to have &lt;code&gt;deq&lt;/code&gt; just yield a new queue, rather than a pair, which is a bit cleaner).&lt;/p&gt;


	&lt;p&gt;To make that work, never permit the heads list to go empty unless the whole queue is empty; it&amp;#8217;s a fairly trivial modification.&lt;/p&gt;


	&lt;p&gt;With a bit more playing, you can actually get the worst-case down to O(1), provided you are willing to do a little more juggling.  Hint:  Don&amp;#8217;t wait till the heads list gets empty to move the tails, and think about interleaving append with reverse.  The constant factors are worse, but not by a lot.&lt;/p&gt;


	&lt;p&gt;I second your recommendation of Okazaki&amp;#8217;s book.  It is well worth a close reading.&lt;/p&gt;</description>
      <pubDate>Fri, 09 Feb 2007 08:45:05 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:c3068e0d-f1c6-419b-af01-3e77cedac833</guid>
      <link>http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers#comment-282</link>
    </item>
    <item>
      <title>Haskell: Queues without pointers</title>
      <description>&lt;p&gt;Haskell has been tying my brain in knots.  Sure, it keeps teaching me all
sorts of amazing things, but it&amp;#8217;s also forcing me to relearn the basics.&lt;/p&gt;

&lt;p&gt;Right now, I&amp;#8217;m trying to implement simple data structures in Haskell. It&amp;#8217;s
challenging, because most typical data structures are based on updating pointers,
and Haskell doesn&amp;#8217;t allow that.  (Well, I could cheat, and use the IO
monad, but where&amp;#8217;s the fun in that?)&lt;/p&gt;

&lt;p&gt;Fortunately, Chris Okasaki has done some amazing research into Haskell (and
ML) data structures.  He wrote an excellent &lt;a href="http://www.cs.cmu.edu/~rwh/theses/okasaki.pdf" title="Purely Functional Data Structures (thesis)"&gt;thesis&lt;/a&gt;, which was later
published as &lt;a href="http://www.amazon.com/Purely-Functional-Structures-Chris-Okasaki/dp/0521663504" title="Purely Functional Data Structures (book)"&gt;Purely Functional Data Structures&lt;/a&gt;.  It&amp;#8217;s a
comprehensive book, well-written, and highly recommended for anyone programming
in a functional language.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s take a look at a data structure from Okasaki&amp;#8217;s book: A purely
functional queue. It&amp;#8217;s competitive with a traditional queue (on average, at least), but it doesn&amp;#8217;t use any pointers.&lt;/p&gt;

&lt;h3&gt;The queue API&lt;/h3&gt;

&lt;p&gt;A queue is a list of values to process.  We can add values to the back of
the queue, and retrieve values from the front.&lt;/p&gt;

&lt;p&gt;In Haskell, we often begin by writing down the types of our functions.  If we&amp;#8217;re lucky, the
code will fill itself in almost automatically.  The first part of our API
is easy:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='comment'&gt;-- A queue holding values of type 'a'.&lt;/span&gt;
&lt;span class='keyword'&gt;data&lt;/span&gt; &lt;span class='conid'&gt;Queue&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varop'&gt;???&lt;/span&gt;

&lt;span class='comment'&gt;-- Create a new queue.&lt;/span&gt;
&lt;span class='varid'&gt;newQueue&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Queue&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt;

&lt;span class='comment'&gt;-- Test whether a queue is empty.&lt;/span&gt;
&lt;span class='varid'&gt;empty&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Queue&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;Bool&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The next part is a bit trickier.  Since we can&amp;#8217;t update our actual
&lt;code&gt;Queue&lt;/code&gt; values, our data structure is read-only!  We can work
around this by making &lt;code&gt;enq&lt;/code&gt; and &lt;code&gt;deq&lt;/code&gt; return an
updated version of the &lt;code&gt;Queue&lt;/code&gt;:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='comment'&gt;-- Add an item to the back of the queue,&lt;/span&gt;
&lt;span class='comment'&gt;-- returning the updated queue.&lt;/span&gt;
&lt;span class='varid'&gt;enq&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Queue&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='varid'&gt;a&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Queue&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt;

&lt;span class='comment'&gt;-- Remove an item from the front of the&lt;/span&gt;
&lt;span class='comment'&gt;-- queue, returning the item and the&lt;/span&gt;
&lt;span class='comment'&gt;-- updated queue.&lt;/span&gt;
&lt;span class='varid'&gt;deq&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Queue&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='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='conid'&gt;Queue&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;We could use this API as follows:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varid'&gt;q&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;newQueue&lt;/span&gt; &lt;span class='varop'&gt;`enq`&lt;/span&gt; &lt;span class='num'&gt;1&lt;/span&gt; &lt;span class='varop'&gt;`enq`&lt;/span&gt; &lt;span class='num'&gt;2&lt;/span&gt; &lt;span class='varop'&gt;`enq`&lt;/span&gt; &lt;span class='num'&gt;3&lt;/span&gt;
&lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;x&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt;&lt;span class='varid'&gt;q'&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;  &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;deq&lt;/span&gt; &lt;span class='varid'&gt;q&lt;/span&gt;  &lt;span class='comment'&gt;-- x is 1&lt;/span&gt;
&lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;y&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt;&lt;span class='varid'&gt;q''&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;deq&lt;/span&gt; &lt;span class='varid'&gt;q'&lt;/span&gt; &lt;span class='comment'&gt;-- y is 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, the tricky bit: Making it run quickly.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 08 Feb 2007 20:01:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:a6a7c29c-9cdd-41ab-8825-a998086a6bba</guid>
      <author>Eric Kidd</author>
      <link>http://www.newsdancer.com/articles/2007/02/08/haskell-queues-without-pointers</link>
      <category>Haskell</category>
      <trackback:ping>http://www.newsdancer.com/articles/trackback/280</trackback:ping>
    </item>
  </channel>
</rss>
