<?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: Tag Recommended</title>
    <link>http://www.randomhacks.net/articles/tag/Recommended?tag=Recommended</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Technology and Other Fun Stuff</description>
    <item>
      <title>Bayes' rule in Haskell, or why drug tests don't work</title>
      <description>&lt;p&gt;Part 3 of Refactoring Probability Distributions.&lt;br /&gt;
&lt;small&gt;(Part 1: &lt;a href="http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions"&gt;PerhapsT&lt;/a&gt;,
Part 2: &lt;a href="http://www.randomhacks.net/articles/2007/02/21/randomly-sampled-distributions"&gt;Sampling functions&lt;/a&gt;)&lt;/small&gt;&lt;/p&gt;

&lt;blockquote&gt;
    &lt;p&gt;&lt;i&gt;A very senior Microsoft developer who moved to Google told
    me that Google works and thinks at a higher level of abstraction than
    Microsoft. &amp;#8220;Google uses Bayesian filtering the way Microsoft uses the if
    statement,&amp;#8221; he said.&lt;/i&gt; -&lt;a href="http://www.joelonsoftware.com/oldnews/pages/October2005.html"&gt;Joel Spolsky&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I really love this quote, because it&amp;#8217;s &lt;a href="http://weblog.raganwald.com/archives/2005_10_01_archive.html"&gt;insanely provocative&lt;/a&gt;
to any language designer.  What &lt;i&gt;would&lt;/i&gt; a programming language look
like if Bayes&amp;#8217; rule were as simple as an &lt;code&gt;if&lt;/code&gt; statement?&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s start with a toy problem, and refactor it until Bayes&amp;#8217; rule is baked
right into our programming language.&lt;/p&gt;

&lt;p&gt;Imagine, for a moment, that we&amp;#8217;re in charge of administering drug tests for
a small business.  We&amp;#8217;ll represent each employee&amp;#8217;s test results (and drug use) as follows:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-keyword'&gt;data&lt;/span&gt; &lt;span class='hs-conid'&gt;Test&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-conid'&gt;Pos&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;|&lt;/span&gt; &lt;span class='hs-conid'&gt;Neg&lt;/span&gt;
  &lt;span class='hs-keyword'&gt;deriving&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Show&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-conid'&gt;Eq&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;

&lt;span class='hs-keyword'&gt;data&lt;/span&gt; &lt;span class='hs-conid'&gt;HeroinStatus&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-conid'&gt;User&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;|&lt;/span&gt; &lt;span class='hs-conid'&gt;Clean&lt;/span&gt;
  &lt;span class='hs-keyword'&gt;deriving&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Show&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-conid'&gt;Eq&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Assuming that 0.1% of our employees have used heroin recently, and that our test is 99%
accurate, we can model the testing process as follows:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-definition'&gt;drugTest1&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;::&lt;/span&gt; &lt;span class='hs-conid'&gt;Dist&lt;/span&gt; &lt;span class='hs-varid'&gt;d&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;d&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;HeroinStatus&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-conid'&gt;Test&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;span class='hs-definition'&gt;drugTest1&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-keyword'&gt;do&lt;/span&gt;
  &lt;span class='hs-varid'&gt;heroinStatus&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='hs-varid'&gt;percentUser&lt;/span&gt; &lt;span class='hs-num'&gt;0.1&lt;/span&gt;
  &lt;span class='hs-varid'&gt;testResult&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;&amp;lt;-&lt;/span&gt;
    &lt;span class='hs-keyword'&gt;if&lt;/span&gt; &lt;span class='hs-varid'&gt;heroinStatus&lt;/span&gt; &lt;span class='hs-varop'&gt;==&lt;/span&gt; &lt;span class='hs-conid'&gt;User&lt;/span&gt;
      &lt;span class='hs-keyword'&gt;then&lt;/span&gt; &lt;span class='hs-varid'&gt;percentPos&lt;/span&gt; &lt;span class='hs-num'&gt;99&lt;/span&gt;
      &lt;span class='hs-keyword'&gt;else&lt;/span&gt; &lt;span class='hs-varid'&gt;percentPos&lt;/span&gt; &lt;span class='hs-num'&gt;1&lt;/span&gt;
  &lt;span class='hs-varid'&gt;return&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;heroinStatus&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-varid'&gt;testResult&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;

&lt;span class='hs-comment'&gt;-- Some handy distributions.&lt;/span&gt;
&lt;span class='hs-definition'&gt;percentUser&lt;/span&gt; &lt;span class='hs-varid'&gt;p&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;percent&lt;/span&gt; &lt;span class='hs-varid'&gt;p&lt;/span&gt; &lt;span class='hs-conid'&gt;User&lt;/span&gt; &lt;span class='hs-conid'&gt;Clean&lt;/span&gt;
&lt;span class='hs-definition'&gt;percentPos&lt;/span&gt; &lt;span class='hs-varid'&gt;p&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;percent&lt;/span&gt; &lt;span class='hs-varid'&gt;p&lt;/span&gt; &lt;span class='hs-conid'&gt;Pos&lt;/span&gt; &lt;span class='hs-conid'&gt;Neg&lt;/span&gt;

&lt;span class='hs-comment'&gt;-- A weighted distribution with two elements.&lt;/span&gt;
&lt;span class='hs-definition'&gt;percent&lt;/span&gt; &lt;span class='hs-varid'&gt;p&lt;/span&gt; &lt;span class='hs-varid'&gt;x1&lt;/span&gt; &lt;span class='hs-varid'&gt;x2&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt;
  &lt;span class='hs-varid'&gt;weighted&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;x1&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-varid'&gt;p&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;x2&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt; &lt;span class='hs-num'&gt;100&lt;/span&gt;&lt;span class='hs-comment'&gt;-&lt;/span&gt;&lt;span class='hs-varid'&gt;p&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code is based our &lt;a href="http://www.randomhacks.net/articles/2007/02/21/randomly-sampled-distributions"&gt;FDist monad&lt;/a&gt;, which is in turn based on
&lt;a href="http://web.engr.oregonstate.edu/~erwig/pfp/"&gt;PFP&lt;/a&gt;.  Don&amp;#8217;t worry if it seems slightly mysterious; you can think of the
&amp;ldquo;&lt;code&gt;&amp;lt;-&lt;/code&gt;&amp;#8221; operator as choosing an element from a probability
distribution.&lt;/p&gt;

&lt;p&gt;Running our drug test shows every possible combination of the two
variables:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;exact&lt;/span&gt; &lt;span class='hs-varid'&gt;drugTest1&lt;/span&gt;
&lt;span class='hs-keyglyph'&gt;[&lt;/span&gt;&lt;span class='hs-conid'&gt;Perhaps&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;User&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;&lt;span class='hs-conid'&gt;Pos&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-num'&gt;0.1&lt;/span&gt;&lt;span class='hs-varop'&gt;%&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;
 &lt;span class='hs-conid'&gt;Perhaps&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;User&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;&lt;span class='hs-conid'&gt;Neg&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-num'&gt;0.0&lt;/span&gt;&lt;span class='hs-varop'&gt;%&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;
 &lt;span class='hs-conid'&gt;Perhaps&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Clean&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;&lt;span class='hs-conid'&gt;Pos&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-num'&gt;1.0&lt;/span&gt;&lt;span class='hs-varop'&gt;%&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;
 &lt;span class='hs-conid'&gt;Perhaps&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Clean&lt;/span&gt;&lt;span class='hs-layout'&gt;,&lt;/span&gt;&lt;span class='hs-conid'&gt;Neg&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-num'&gt;98.9&lt;/span&gt;&lt;span class='hs-varop'&gt;%&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you look carefully, we have a problem.  Most of the employees who test
positive are actually clean!  Let&amp;#8217;s tweak our code a bit, and try to zoom
in on the positive test results.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2007/02/22/bayes-rule-and-drug-tests"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 22 Feb 2007 18:11:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:fffdc914-d289-4ac8-bde5-7bbb02451aeb</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2007/02/22/bayes-rule-and-drug-tests</link>
      <category>Haskell</category>
      <category>Math</category>
      <category>Monads</category>
      <category>Probability</category>
      <category>Recommended</category>
      <category>ProbabilityMonads</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/306</trackback:ping>
    </item>
    <item>
      <title>Map fusion: Making Haskell 225% faster</title>
      <description>&lt;p&gt;&lt;b&gt;Or, how to optimize MapReduce, and when folds are faster than loops&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Purely functional programming might actually be worth the pain, if you care about large-scale optimization.&lt;/p&gt;

&lt;p&gt;Lately, I&amp;#8217;ve been studying how to speed up parallel algorithms.  Many
parallel algorithms, such as Google&amp;#8217;s &lt;a href="http://labs.google.com/papers/mapreduce.html"&gt;MapReduce&lt;/a&gt;, have two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, you transform the data by mapping one or more functions over each value.&lt;/li&gt;
&lt;li&gt;Next, you repeatedly merge the transformed data, &amp;#8220;reducing&amp;#8221; it down to a
final result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unfortunately, there&amp;#8217;s a couple of nasty performance problems lurking here.  We &lt;i&gt;really&lt;/i&gt; want to combine all those steps into a single pass, so that we can eliminate temporary working data. But we don&amp;#8217;t always want to do this optimization by hand&amp;#8212;it would be better if the compiler could do it for us.&lt;/p&gt;

&lt;p&gt;As it turns out, Haskell is an amazing testbed for this kind of
optimization. Let&amp;#8217;s build a simple model, show where it breaks, and then
crank the performance &lt;i&gt;way&lt;/i&gt; up.&lt;/p&gt;

&lt;h3&gt;Trees, and the performance problems they cause&lt;/h3&gt;

&lt;p&gt;We&amp;#8217;ll use single-threaded trees for our testbed. They&amp;#8217;re simple enough to demonstrate the basic idea, and they can be generalized to parallel systems. (If you want know how, check out the papers at the end of this article.)&lt;/p&gt;

&lt;p&gt;A tree is either empty, or it is
a node with a left child, a value and a right child:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-keyword'&gt;data&lt;/span&gt; &lt;span class='hs-conid'&gt;Tree&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt;
            &lt;span class='hs-keyglyph'&gt;|&lt;/span&gt; &lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Tree&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Tree&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
  &lt;span class='hs-keyword'&gt;deriving&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Show&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here&amp;#8217;s a sample tree containing three values:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-definition'&gt;tree&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-varid'&gt;left&lt;/span&gt; &lt;span class='hs-num'&gt;2&lt;/span&gt; &lt;span class='hs-varid'&gt;right&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
  &lt;span class='hs-keyword'&gt;where&lt;/span&gt; &lt;span class='hs-varid'&gt;left&lt;/span&gt;  &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt; &lt;span class='hs-num'&gt;1&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
        &lt;span class='hs-varid'&gt;right&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt; &lt;span class='hs-num'&gt;3&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can use &lt;code&gt;treeMap&lt;/code&gt; to apply a function to every value in a
tree, creating a new tree:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-definition'&gt;treeMap&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;::&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;a&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-conid'&gt;Tree&lt;/span&gt; &lt;span class='hs-varid'&gt;a&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-conid'&gt;Tree&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt;

&lt;span class='hs-definition'&gt;treeMap&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt;
&lt;span class='hs-definition'&gt;treeMap&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt;
  &lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;treeMap&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;treeMap&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using &lt;code&gt;treeMap&lt;/code&gt;, we can build various functions that manipulate
trees:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-comment'&gt;-- Double each value in a tree.&lt;/span&gt;
&lt;span class='hs-definition'&gt;treeDouble&lt;/span&gt; &lt;span class='hs-varid'&gt;tree&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;treeMap&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varop'&gt;*&lt;/span&gt;&lt;span class='hs-num'&gt;2&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-varid'&gt;tree&lt;/span&gt;

&lt;span class='hs-comment'&gt;-- Add one to each value in a tree.&lt;/span&gt;
&lt;span class='hs-definition'&gt;treeIncr&lt;/span&gt; &lt;span class='hs-varid'&gt;tree&lt;/span&gt;   &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;treeMap&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varop'&gt;+&lt;/span&gt;&lt;span class='hs-num'&gt;1&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-varid'&gt;tree&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What if we want to add up all the values in a tree?  Well, we could write a
simple recursive sum function:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-definition'&gt;treeSum&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-num'&gt;0&lt;/span&gt;
&lt;span class='hs-definition'&gt;treeSum&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt;
  &lt;span class='hs-varid'&gt;treeSum&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt; &lt;span class='hs-varop'&gt;+&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-varop'&gt;+&lt;/span&gt; &lt;span class='hs-varid'&gt;treeSum&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But for reasons that will soon become clear, it&amp;#8217;s much better to refactor
the recursive part of &lt;code&gt;treeSum&lt;/code&gt; into a reusable
&lt;code&gt;treeFold&lt;/code&gt; function (&amp;#8220;fold&amp;#8221; is Haskell&amp;#8217;s name for &amp;#8220;reduce&amp;#8221;):&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-definition'&gt;treeFold&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt; &lt;span class='hs-conid'&gt;Empty&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt;
&lt;span class='hs-definition'&gt;treeFold&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-conid'&gt;Node&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt;
  &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;treeFold&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;treeFold&lt;/span&gt; &lt;span class='hs-varid'&gt;f&lt;/span&gt; &lt;span class='hs-varid'&gt;b&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;

&lt;span class='hs-definition'&gt;treeSum&lt;/span&gt; &lt;span class='hs-varid'&gt;t&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;=&lt;/span&gt; &lt;span class='hs-varid'&gt;treeFold&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-keyglyph'&gt;\&lt;/span&gt;&lt;span class='hs-varid'&gt;l&lt;/span&gt; &lt;span class='hs-varid'&gt;x&lt;/span&gt; &lt;span class='hs-varid'&gt;r&lt;/span&gt; &lt;span class='hs-keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='hs-varid'&gt;l&lt;/span&gt;&lt;span class='hs-varop'&gt;+&lt;/span&gt;&lt;span class='hs-varid'&gt;x&lt;/span&gt;&lt;span class='hs-varop'&gt;+&lt;/span&gt;&lt;span class='hs-varid'&gt;r&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt; &lt;span class='hs-num'&gt;0&lt;/span&gt; &lt;span class='hs-varid'&gt;t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we can double all the values in a tree, add 1 to each, and sum up the
result:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='hs-definition'&gt;treeSum&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;treeIncr&lt;/span&gt; &lt;span class='hs-layout'&gt;(&lt;/span&gt;&lt;span class='hs-varid'&gt;treeDouble&lt;/span&gt; &lt;span class='hs-varid'&gt;tree&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;&lt;span class='hs-layout'&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But there&amp;#8217;s a very serious problem with this code.  Imagine that we&amp;#8217;re
working with a million-node tree.  The two calls to &lt;code&gt;treeMap&lt;/code&gt;
(buried inside &lt;code&gt;treeIncr&lt;/code&gt; and &lt;code&gt;treeDouble&lt;/code&gt;) will each
create a &lt;i&gt;new&lt;/i&gt; million-node tree.  Obviously, this will kill our performance,
and it will make our garbage collector cry.&lt;/p&gt;

&lt;p&gt;Fortunately, we can do a lot better than this, thanks to some funky GHC
extensions.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sat, 10 Feb 2007 09:55:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:9e56e1ad-e953-4a5a-89ff-e4708979f952</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2007/02/10/map-fusion-and-haskell-performance</link>
      <category>Haskell</category>
      <category>Performance</category>
      <category>Recommended</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/290</trackback:ping>
    </item>
    <item>
      <title>Why Ruby is an acceptable LISP</title>
      <description>&lt;p&gt;Years ago, I looked at Ruby and decided to ignore it. Ruby wasn&amp;#8217;t as
popular as Python, and it wasn&amp;#8217;t as powerful as LISP. So why should I
bother?&lt;/p&gt;

&lt;p&gt;Of course, we could turn those criteria around.  What if Ruby were more
popular than &lt;em&gt;LISP&lt;/em&gt;, and more powerful than &lt;em&gt;Python&lt;/em&gt;?  Would that be enough
to make Ruby interesting?&lt;/p&gt;

&lt;p&gt;Before answering this question, we should decide what makes LISP so
powerful.  Paul Graham has &lt;a href="http://www.paulgraham.com/icad.html" title="Revenge of the Nerds"&gt;written eloquently&lt;/a&gt; about LISP&amp;#8217;s virtues.  But, for the sake of argument, I&amp;#8217;d like to boil them down to two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;LISP is a dense functional language.  &lt;/li&gt;
&lt;li&gt;LISP has programmatic macros.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As it turns out, Ruby compares well as a functional language, and it fakes
macros better than I&amp;#8217;d thought.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2005/12/03/why-ruby-is-an-acceptable-lisp"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sat, 03 Dec 2005 11:30:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:080ade06-c978-4e36-8847-44c22d1bc9b9</guid>
      <author>Eric Kidd</author>
      <link>http://www.randomhacks.net/articles/2005/12/03/why-ruby-is-an-acceptable-lisp</link>
      <category>Ruby</category>
      <category>LISP</category>
      <category>Macros</category>
      <category>Recommended</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/77</trackback:ping>
    </item>
    <item>
      <title>McCarthy's Ambiguous Operator</title>
      <description>    &lt;p&gt;Back in 1961, John McCarthy (the inventor of LISP) &lt;a href='http://citeseer.ist.psu.edu/mccarthy63basis.html'&gt;described&lt;/a&gt;
    an interesting mathematical operator called &lt;code&gt;amb&lt;/code&gt;.  Essentially,
    &lt;code&gt;amb&lt;/code&gt; &lt;i&gt;hates&lt;/i&gt; to be called with no arguments, and can look
    into the future to keep that from happening.  Here's how it might look
    in Ruby.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2005/10/11/amb-operator"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Tue, 11 Oct 2005 00:00:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:8d29ae6d-81d0-4f48-a6cb-80187e1821a7</guid>
      <author>Eric</author>
      <link>http://www.randomhacks.net/articles/2005/10/11/amb-operator</link>
      <category>Ruby</category>
      <category>Continuations</category>
      <category>Hacks</category>
      <category>Recommended</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/70</trackback:ping>
    </item>
    <item>
      <title>The Missing Future</title>
      <description>    &lt;p&gt;I'm a 27-year-old programmer.  When I'm 55--in 2031--I want to
    still be a programmer.  And in 2031, I want to love my job as much as I
    do today.  What will 2031 look like?  Right now, two groups are
    offering their visions for the future: Microsoft and the open source
    movement.  A third group is conspicuously silent: small, independent
    developers.  What do the Microsoft and open source futures look like?
    Will the independent developers speak up?  Which future should I fight
    for?  My choices, and the choices of hundreds of thousands of people
    like me, will help determine which future we get.  So let's take a look
    and start talking.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2003/06/22/the-missing-future"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sun, 22 Jun 2003 00:00:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:4850619a-c73a-4e14-8837-0a2b6d722954</guid>
      <author>Eric</author>
      <link>http://www.randomhacks.net/articles/2003/06/22/the-missing-future</link>
      <category>Recommended</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/60</trackback:ping>
    </item>
    <item>
      <title>Lightweight Languages 2 Conference</title>
      <description>    &lt;p&gt;This Saturday, I attended the &lt;a href='http://ll2.ai.mit.edu/'&gt;LL2&lt;/a&gt; conference at MIT.  LL2 is
    dedicated to "lightweight" programming languages, a delibrately loose
    category including (1) any pleasant, easy-to-use scripting language and
    (2) any academic language which makes it easier to prototype and write
    software quickly.  LL2 is a small, informal workshop with audience
    participation.  The attendees are a diverse bunch, and enjoy goring
    each other's sacred cows.  You have been warned.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2002/11/10/ll2"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sun, 10 Nov 2002 00:00:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:1ba35c19-00f7-4d43-9ade-9b06e2adb4e1</guid>
      <author>Eric</author>
      <link>http://www.randomhacks.net/articles/2002/11/10/ll2</link>
      <category>Recommended</category>
      <category>Conference</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/41</trackback:ping>
    </item>
    <item>
      <title>Bayesian Whitelisting: Finding the Good Mail Among the Spam</title>
      <description>    &lt;p&gt;The biggest challenge with spam filtering is reducing false
    positives--that is, finding the good mail among the spam.  Even the
    best spam filters occasionally mistake legitimate e-mail for spam.  For
    example, in some &lt;a href='/stories/2002/09/22/trainable-spam-filter-testing' title='How To Test a Trainable Spam Filter'&gt;recent
    tests&lt;/a&gt;, &lt;a href='http://bogofilter.sourceforge.net/'&gt;&lt;code&gt;bogofilter&lt;/code&gt;&lt;/a&gt;
    processed 18,000 e-mails with only 34 false positives.  Unfortunately,
    several of these false positives were urgent e-mails from former
    clients.  This unpleasant mistake wasn't necessary--the most important
    of these false positives could have been avoided with an automatic
    whitelisting system.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2002/09/29/bayesian-whitelisting"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sun, 29 Sep 2002 00:00:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:4e3b83e6-1f2a-48d4-9f65-5e691ab45838</guid>
      <author>Eric</author>
      <link>http://www.randomhacks.net/articles/2002/09/29/bayesian-whitelisting</link>
      <category>Spam</category>
      <category>Hacks</category>
      <category>Python</category>
      <category>Recommended</category>
      <category>Probability</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/38</trackback:ping>
    </item>
    <item>
      <title>How To Test a Trainable Spam Filter</title>
      <description>    &lt;p&gt;Ever since Paul Graham published &lt;a href='http://www.paulgraham.com/spam.html'&gt;A Plan for Spam&lt;/a&gt;,
    "trainable" spam filters have become the latest fashion.  These filters
    train themselves to know the characteristics of your personal e-mail.
    Supposedly, this extra knowledge allows them to make fewer mistakes,
    and makes them harder to fool.  But do these filters actually work?  In
    this article, I try out Eric Raymond's &lt;a href='/stories/2002/09/13/bogofilter' title='Bogofilter: A New Spam Filter'&gt;bogofilter&lt;/a&gt;, a trainable Bayesian spam filter,
    and describe the steps required to evaluate such a filter
    accurately.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2002/09/22/trainable-spam-filter-testing"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sun, 22 Sep 2002 00:00:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:c227afc7-6562-4fc5-8b77-7624df1aed2e</guid>
      <author>Eric</author>
      <link>http://www.randomhacks.net/articles/2002/09/22/trainable-spam-filter-testing</link>
      <category>Spam</category>
      <category>Hacks</category>
      <category>Recommended</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/31</trackback:ping>
    </item>
    <item>
      <title>Why Hygienic Macros Rock</title>
      <description>    &lt;p&gt;I've recently been reading a lot of &lt;a href='http://www.paulgraham.com/articles.html'&gt;excellent essays on
    programming language design&lt;/a&gt; by Paul Graham.  Paul and I agree about
    a number of things: (1) LISP is beautiful and powerful family of
    languages, even by modern standards, (2) all existing dialects of LISP
    are lacking a certain something, and (3) programmatic macros are a Good
    Idea.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.randomhacks.net/articles/2002/09/13/hygienic-macros"&gt;Read More&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Fri, 13 Sep 2002 00:00:00 +0000</pubDate>
      <guid isPermaLink="false">urn:uuid:213d6f83-48bd-4661-a3eb-98fb3784f7da</guid>
      <author>Eric</author>
      <link>http://www.randomhacks.net/articles/2002/09/13/hygienic-macros</link>
      <category>Macros</category>
      <category>LISP</category>
      <category>Hacks</category>
      <category>Recommended</category>
      <trackback:ping>http://www.randomhacks.net/articles/trackback/24</trackback:ping>
    </item>
  </channel>
</rss>

