Random HacksRandom code snippets, projects and musings about software from Eric Kidd, a developer and entrepreneur.
http://www.randomhacks.net/
enProving sorted lists correct using the Coq proof assistant
<p>About 15 years ago, I was hanging out at the MIT AI Lab, and there was an
ongoing seminar on the <a href="https://coq.inria.fr/">Coq proof assistant</a>. The idea was that you
wouldn't have to guess whether your programs were correct; you could
<em>prove</em> that they worked correctly. </p>
<p>The were just two little problems:</p>
<ol>
<li>It looked ridiculously intimidating.</li>
<li>Rumor said that it took a grad student all summer to implement and prove
the <a href="https://en.wikipedia.org/wiki/Greatest_common_divisor">greatest common divisor</a> algorithm, which sounded rather
impractical.</li>
</ol><p>So I decided to stick to Lispy languages, which is what I was officially
supposed to be hacking on, anyway, and I never did try to sit in on the
seminar.</p>
<h2>Taking another look</h2>
<p>I should have taken a look much sooner. This stuff provides even more
twisted fun than Haskell! Also, projects like the
<a href="http://compcert.inria.fr/">CompCert C compiler</a> are impressive: Imagine a C compiler where
every optimization has been proven correct.</p>
<p>Even better, we can write code in Coq, prove it correct, then export it to
Haskell or several other functional languages.</p>
<p>Here's an example Coq proof. Let's start with a basic theorem that says
"If we know <code>A</code> is true, and we know <code>B</code> is true, then we know <code>A /\ B</code>
(both <code>A</code> and <code>B</code>) is true."</p>
<div class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="kn">Theorem</span> <span class="n">basic_conj</span> <span class="o">:</span> <span class="k">forall</span> <span class="o">(</span><span class="nc">A</span> <span class="nc">B</span> <span class="o">:</span> <span class="kt">Prop</span><span class="o">),</span>
<span class="nc">A</span> <span class="o">-></span> <span class="nc">B</span> <span class="o">-></span> <span class="nc">A</span> <span class="o">/\</span> <span class="nn">B</span><span class="p">.</span>
<span class="nn">Proof</span><span class="p">.</span>
<span class="c">(* Give names to our inputs. *)</span>
<span class="k">intros</span> <span class="nc">A</span> <span class="nc">B</span> <span class="nc">H_A_True</span> <span class="nn">H_B_True</span><span class="p">.</span>
<span class="c">(* Specify that we want to prove each half</span>
<span class="c"> of /\ separately. *)</span>
<span class="k">split</span><span class="o">.</span>
<span class="o">-</span> <span class="k">apply</span> <span class="nn">H_A_True</span><span class="p">.</span> <span class="c">(* Prove the left half. *)</span>
<span class="o">-</span> <span class="k">apply</span> <span class="nn">H_B_True</span><span class="p">.</span> <span class="c">(* Prove the right half. *)</span>
<span class="kn">Qed</span><span class="o">.</span>
</code></pre></div>
<p>But Coq proofs are intended to be read interactively, using a tool like
<a href="https://coq.inria.fr/V8.1/refman/Reference-Manual016.html">CoqIDE</a> or Emacs <a href="http://proofgeneral.inf.ed.ac.uk/">Proof General</a>. Let me walk you through how this
proof would really look.</p>
<div class="highlight"><pre><code class="language-coq" data-lang="coq"><span class="kn">Proof</span><span class="o">.</span>
</code></pre></div>
<p>At this point, the right-hand pane will show the theorem that we're trying
to prove:</p>
<div class="highlight"><pre><code class="language-text" data-lang="text">1 subgoals, subgoal 1 (ID 1)
============================
forall A B : Prop, A -> B -> A /\ B
</code></pre></div>
<p>
<a href="http://www.randomhacks.net/2015/07/19/proving-sorted-lists-correct-using-coq-proof-assistent/">Read more…</a>
</p>
Sun, 19 Jul 2015 07:39:19 -0400
http://www.randomhacks.net/2015/07/19/proving-sorted-lists-correct-using-coq-proof-assistent/
http://www.randomhacks.net/2015/07/19/proving-sorted-lists-correct-using-coq-proof-assistent/"Build Your Own Probability Monads" paper back online
<p>I noticed the other day that my "Build Your Own Probability Monads" paper
had disappeared. It's <a href="http://www.randomhacks.net/probability-monads/">back online with a new introduction</a>:</p>
<blockquote>
<p>Several years back, I wrote a series of blog posts about probability
monads inspired by this quote:</p>
<blockquote>
<p><em>A very senior Microsoft developer who moved to Google told
me that Google works and thinks at a higher level of abstraction than
Microsoft. "Google uses Bayesian filtering the way Microsoft uses the if
statement," he said.</em> -<a href="http://www.joelonsoftware.com/oldnews/pages/October2005.html">Joel Spolsky</a></p>
</blockquote>
<p>My goal was to make it easier to reason about evidence by combining Bayes'
Rule and probability monads:</p>
<div class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="nf">fluStatusGivenPositiveTest</span> <span class="ow">=</span> <span class="kr">do</span>
<span class="n">fluStatus</span> <span class="ow"><-</span> <span class="n">percentWithFlu</span> <span class="mi">10</span>
<span class="n">testResult</span> <span class="ow"><-</span> <span class="kr">if</span> <span class="n">fluStatus</span> <span class="o">==</span> <span class="kt">Flu</span>
<span class="kr">then</span> <span class="n">percentPositive</span> <span class="mi">70</span>
<span class="kr">else</span> <span class="n">percentPositive</span> <span class="mi">10</span>
<span class="n">guard</span> <span class="p">(</span><span class="n">testResult</span> <span class="o">==</span> <span class="kt">Pos</span><span class="p">)</span>
<span class="n">return</span> <span class="n">fluStatus</span>
</code></pre></div>
</blockquote>
<p>You can find links to the original blog posts, the paper, and the source
code from Hac 07 on the <a href="http://www.randomhacks.net/probability-monads/">new overview page</a>, which is intended to be
the official, long-term home for this work.</p>
Wed, 21 May 2014 00:00:00 -0400
http://www.randomhacks.net/2014/05/21/build-your-own-probability-monads-paper-back-online/
http://www.randomhacks.net/2014/05/21/build-your-own-probability-monads-paper-back-online/Derivatives of algebraic data structures: An excellent tutorial
<p>Last month, the folks at Lab49 explained <a href="http://blog.lab49.com/archives/3011">how to compute the derivative of a data structure</a>. This is a great example of how to write about mathematical subjects for a casual audience: They draw analogies to well-known programming languages, they follow a single, well-chosen thread of explanation, and there's a clever payoff at the end.</p>
<p>The Lab49 blog post is, of course, based on two <a href="http://strictlypositive.org/diff.pdf">classic</a> <a href="http://strictlypositive.org/Dissect.pdf">papers</a> by Conor McBride, and Huet's original paper <a href="http://www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/huet-zipper.pdf">The Zipper</a>.</p>
<p>If you're interested in real-world applications of this technique, there's a great explanation in the final chapter of <a href="http://learnyouahaskell.com/zippers">Learn You a Haskell for Great Good</a>. If you're interested in some deeper mathematical connections, see the <a href="http://lambda-the-ultimate.org/node/1957">discussion at Lambda the Ultimate</a>.</p>
Fri, 20 May 2011 20:01:00 -0400
http://www.randomhacks.net/2011/05/20/derivatives-of-algebraic-data-structures-an-excellent-tutorial/
http://www.randomhacks.net/2011/05/20/derivatives-of-algebraic-data-structures-an-excellent-tutorial/What do these fixed points have in common?
<p>A question asked while standing in the shower: What do all of the following have in common?</p>
<ol>
<li>
<a href="http://en.wikipedia.org/wiki/Banach_fixed_point_theorem">Banach</a> and <a href="http://en.wikipedia.org/wiki/Brouwer_fixed_point_theorem">Brouwer fixed points</a>. If you're in Manhattan, and you crumple up a map of Manhattan and place it on the ground, at least one point on your map will be exactly over the corresponding point on the ground. (This is true even if your map is <em>larger</em> than life.)</li>
<li>The fixed points computed by the <a href="http://en.wikipedia.org/wiki/Fixed_point_combinator">Y combinator</a>, which is used to construct anonymous recursive functions in the lambda calculus.</li>
<li>The <a href="http://en.wikipedia.org/wiki/Nash_equilibrium">Nash equilibrium</a>, which is the stable equilibrium of a multi-player game (and one of the key ideas of economics). See also this lovely—if metaphorical—<a href="http://www.scottaaronson.com/blog/?p=418">rant by Scott Aaronson</a>.</li>
<li>The <a href="http://en.wikipedia.org/wiki/Eigenvector">eigenvectors of a matrix</a>, which will still point in the same direction after multiplication by the matrix.</li>
</ol><p>At what level of abstraction are all these important ideas really just the same idea? If we strip everything down to <a href="http://en.wikipedia.org/wiki/Abstract_nonsense">generalized abstract nonsense</a>, is there a nice simple formulation that covers all of the above?</p>
<p>(I can't play with this shiny toy today; I have to work.)</p>
Thu, 12 May 2011 12:09:00 -0400
http://www.randomhacks.net/2011/05/12/what-do-fixed-points-have-in-common/
http://www.randomhacks.net/2011/05/12/what-do-fixed-points-have-in-common/Ubiquitous Hoogle
<p><a href="https://wiki.mozilla.org/Labs/Ubiquity/Ubiquity_0.1_User_Tutorial">Ubiquity</a> is an experimental Firefox plugin. It's a "graphical command line" similar to <a href="http://en.wikipedia.org/wiki/Quicksilver_(software)">QuickSilver</a> on the Macintosh.</p>
<p>You can easily add your own commands to Ubiquity. The following article shows how to create a <a href="http://www.haskell.org/hoogle/">Hoogle</a> search command that looks up <a href="http://www.haskell.org/">Haskell</a> functions by name or by type signature.</p>
<p><img src="http://www.randomhacks.net/files/ubiq-hoogle-putStr.png" width="534" height="407" alt="Searching for putStr"></p>
<p>You can press <strong>Return</strong> or click on one the links in the preview.</p>
<p>
<a href="http://www.randomhacks.net/2008/09/01/ubiquitous-hoogle/">Read more…</a>
</p>
Mon, 01 Sep 2008 10:33:00 -0400
http://www.randomhacks.net/2008/09/01/ubiquitous-hoogle/
http://www.randomhacks.net/2008/09/01/ubiquitous-hoogle/Probability monads at Hac 07 II
<p>From October 5-7, I'll be at the <a href="http://www.haskell.org/haskellwiki/Hac_2007_II">Haskell Hackathon in Freiburg</a>.</p>
<p>I'll be working on probability monads, attempting to turn my <a href="http://www.randomhacks.net/articles/2007/04/19/robot-localization-particle-system-monad">various blog articles</a> into a real Haskell library.</p>
<p>Some resources:</p>
<ul>
<li>
<a href="https://github.com/emk/haskell-probability-monads">Control.Monad.Probability source code</a>: This is the as-yet-unreconstructed code from my blog posts.</li>
<li>
<a href="http://www.randomhacks.net/files/build-your-own-probability-monads.pdf">Draft paper on probability monads</a>: This paper explains the theory, and it has terser implementations of some ideas.</li>
</ul><p>If you were a peer reviewer, or gave me feedback on the paper, my humble thanks--and apologies. I haven't had a chance to revise the paper yet, and so your feedback is not yet included.</p>
<p>See you at the Hackathon!</p>
Tue, 02 Oct 2007 07:50:00 -0400
http://www.randomhacks.net/2007/10/02/probability-monads-at-hac-07-ii/
http://www.randomhacks.net/2007/10/02/probability-monads-at-hac-07-ii/Freiburg in October: Scheme, Dylan, and probability monads
<p>Good morning! I'll be in Freiburg for several events this October, including <a href="http://cufp.galois.com/">CUFP</a> and the <a href="http://haskell.org/haskellwiki/Hac_2007_II">Haskell Hackathon</a>.</p>
<h4>Commercial Users of Functional Programming (CUFP)</h4>
<p>On October 4th, I'll be speaking at <a href="http://cufp.galois.com/">CUFP ’07</a>, describing the use of Scheme in a real-world multimedia engine. Some likely topics:</p>
<ol>
<li>How we switched to Scheme (and why refactoring is your friend).</li>
<li>How our artists learned to program in Scheme (it's all about the tools).</li>
<li>The tension between functional programming and objects: Can we have both?</li>
</ol><h4>Dylan Beer Night</h4>
<p>Once upon a time, I dreamt of <a href="http://www.cs.dartmouth.edu/reports/abstracts/TR2001-404/">generic functions</a> and built RPMs for <a href="http://web.archive.org/web/19990125095755/http://www.gwydiondylan.org/">Gwydion Dylan</a>.</p>
<p>Some current and former Dylan hackers are hoping to meet in Freiburg, most likely on October 4th. If you're at ICFP or one of the workshops, we'd love to hear from you.</p>
<h4>Haskell Hackathon</h4>
<p>I'll be at the <a href="http://haskell.org/haskellwiki/Hac_2007_II">Haskell Hackathon</a> from Friday to Sunday.</p>
<p>Perhaps it's time to whip <a href="http://www.randomhacks.net/darcs/probability/">Control.Monad.Probability</a> into shape?</p>
Tue, 18 Sep 2007 12:36:00 -0400
http://www.randomhacks.net/2007/09/18/freiburg-in-october/
http://www.randomhacks.net/2007/09/18/freiburg-in-october/Bowling in Haskell: A response to Ron Jeffries
<p>Bowling is a <a href="http://en.wikipedia.org/wiki/Ten-pin_bowling#Scoring">tricky game to score</a>. It's just complicated enough
to act as a good programming exercise. And Ron Jeffries has performed this
exercise many times, in <a href="http://www.xprogramming.com/xpmag/acsBowling.htm" title="Adventures in C#: The Bowling Game">C#</a>, <a href="http://www.xprogramming.com/xpmag/BowlingForSmalltalkII.htm" title="DBC: Bowling For Smalltalk II">Smalltalk</a>, and <a href="http://www.google.com/search?q=+site:www.xprogramming.com+jeffries+bowling" title="More bowling">other
languages</a>. He's been searching for a tidy and elegant solution, one
which makes the rules of bowling as clear as possible.</p>
<p>In the past, though, Jeffries has been a bit skeptical of <a href="http://www.xprogramming.com/xpmag/dbcHaskellBowling.htm" title="Haskell Bowling">Haskell
implementations of bowling</a>:</p>
<blockquote>The recursive [Haskell] solution, however, is questionable on
more fundamental grounds. A game of bowling consists of ten frames, not
less or more, and the "ten-ness" of the game is not represented in the
recursive solutions at all. Even if we let that slide, the recursive
solutions make it a bit hard to understand what's going on.</blockquote>
<p>Let's see if we can do better. No knowledge of bowling is required--if we
do this right, our program should be at least as clear as an
<a href="http://en.wikipedia.org/wiki/Ten-pin_bowling#Scoring">English-language version of the rules</a>.</p>
<p>Along the way, we'll encounter lazy lists, an interesting recursion combinator, and <a href="http://www.haskell.org/hoogle/">Hoogle</a>, the Haskell search engine.</p>
<h3>The rules of bowling</h3>
<p>In bowling, we roll balls down a lane, trying to knock down pins. If we
know how many pins we knock down with each ball, we can compute the final
score. So our program looks something like this:</p>
<div class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="c1">-- Pins knocked down by each ball.</span>
<span class="kr">type</span> <span class="kt">Balls</span> <span class="ow">=</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span>
<span class="c1">-- Number of points scored.</span>
<span class="kr">type</span> <span class="kt">Score</span> <span class="ow">=</span> <span class="kt">Int</span>
<span class="nf">scoreGame</span> <span class="ow">::</span> <span class="kt">Balls</span> <span class="ow">-></span> <span class="kt">Score</span>
<span class="nf">scoreGame</span> <span class="n">balls</span> <span class="ow">=</span> <span class="o">???</span>
</code></pre></div>
<p>But how do we implement <code>scoreGame</code>?</p>
<h3>Scoring a frame</h3>
<p>A bowling game is divided into 10 <b>frames</b>. Ordinary frames consist of 1 or 2 balls. The 10th frame may have an additional 1 or 2 bonus balls, which we discuss below.</p>
<p>To score an individual frame, we need to do two things: (1) calculate the score for our frame, and (2) figure out where the next frame
starts. Our scoring function will return both pieces of information:</p>
<div class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="c1">-- Score one frame and return the rest.</span>
<span class="nf">scoreFrame</span> <span class="ow">::</span> <span class="kt">Balls</span> <span class="ow">-></span> <span class="p">(</span><span class="kt">Score</span><span class="p">,</span> <span class="kt">Balls</span><span class="p">)</span>
</code></pre></div>
<p>If we knock down all 10 pins with the first ball in a frame
(<code>x1</code>), we call it a <b>strike</b>, and move on to the next
frame immediately. But we also get a bonus---we're allowed to count balls
<code>y1</code> and <code>y2</code> from the <i>next</i> frame towards
<i>this</i> frame's score:</p>
<div class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="nf">scoreFrame</span> <span class="p">(</span><span class="n">x1</span><span class="kt">:</span> <span class="n">y1</span><span class="kt">:</span><span class="n">y2</span><span class="kt">:</span><span class="n">ys</span><span class="p">)</span> <span class="o">|</span> <span class="n">x1</span> <span class="o">==</span> <span class="mi">10</span> <span class="ow">=</span>
<span class="p">(</span><span class="n">x1</span><span class="o">+</span><span class="n">y1</span><span class="o">+</span><span class="n">y2</span><span class="p">,</span> <span class="n">y1</span><span class="kt">:</span><span class="n">y2</span><span class="kt">:</span><span class="n">ys</span><span class="p">)</span> <span class="c1">-- Strike</span>
</code></pre></div>
<p>If we knock down all the pins using <i>two</i> balls (<code>x1</code> and
<code>x2</code>), we call it a <b>spare</b>. And we get to count one ball from
the next frame as our bonus:</p>
<div class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="nf">scoreFrame</span> <span class="p">(</span><span class="n">x1</span><span class="kt">:</span><span class="n">x2</span><span class="kt">:</span> <span class="n">y1</span><span class="kt">:</span><span class="n">ys</span><span class="p">)</span> <span class="o">|</span> <span class="n">x1</span><span class="o">+</span><span class="n">x2</span> <span class="o">==</span> <span class="mi">10</span> <span class="ow">=</span>
<span class="p">(</span><span class="n">x1</span><span class="o">+</span><span class="n">x2</span><span class="o">+</span><span class="n">y1</span><span class="p">,</span> <span class="n">y1</span><span class="kt">:</span><span class="n">ys</span><span class="p">)</span> <span class="c1">-- Spare</span>
</code></pre></div>
<p>If we don't manage to knock all 10 pins with two balls, we call it an <b>open
frame</b>. And we don't get any bonus:</p>
<div class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="nf">scoreFrame</span> <span class="p">(</span><span class="n">x1</span><span class="kt">:</span><span class="n">x2</span><span class="kt">:</span> <span class="n">ys</span><span class="p">)</span> <span class="ow">=</span>
<span class="p">(</span><span class="n">x1</span><span class="o">+</span><span class="n">x2</span><span class="p">,</span> <span class="n">ys</span><span class="p">)</span> <span class="c1">-- Open frame</span>
</code></pre></div>
<p>What happens if we have a strike or a spare in the 10th frame?
We get to roll our bonus balls anyway. Conventionally, these extra balls
are recorded as part of the 10th frame (making it 3 balls long), but
they're really just phantom balls hanging off the end of the game.</p>
<p>Next, we need to turn <code>scoreFrame</code> into a recursive function.</p>
<p>
<a href="http://www.randomhacks.net/2007/04/28/bowling-in-haskell/">Read more…</a>
</p>
Sat, 28 Apr 2007 11:53:00 -0400
http://www.randomhacks.net/2007/04/28/bowling-in-haskell/
http://www.randomhacks.net/2007/04/28/bowling-in-haskell/Robot localization using a particle system monad
<p><b>Refactoring Probability Distributions:</b>
<a href="http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions" title="PerhapsT">part 1</a>, <a href="http://www.randomhacks.net/articles/2007/02/21/randomly-sampled-distributions" title="Random sampling">part 2</a>, <a href="http://www.randomhacks.net/articles/2007/02/22/bayes-rule-and-drug-tests" title="Bayes' rule">part 3</a>, <a href="http://www.randomhacks.net/articles/2007/03/03/smart-classification-with-haskell" title="Bayesian classification">part 4</a>, <b>part 5</b></p>
<p>Welcome to the 5th (and final) installment of <i>Refactoring Probability
Distributions!</i> Today, let's begin with an example from
<a href="http://seattle.intel-research.net/people/jhightower/pubs/fox2003bayesian/fox2003bayesian.pdf" title="Fox and colleagues, 2003">Bayesian Filters for Location Estimation</a> (PDF), an excellent paper by Fox and colleagues.</p>
<p>In their example, we have a robot in a hallway with 3 doors. Unfortunately, we don't know <i>where</i> in the hallway the robot is located:</p>
<p style="text-align: center"><img src="http://www.randomhacks.net/files/robot-door-1.png" title="Robot at
first door"></p>
<p>The vertical black lines are "particles." Each particle represents a
possible location of our robot, chosen at random along the hallway. At
first, our particles are spread along the entire hallway (the top
row of black lines). Each particle begins life with a weight of 100%, represented by the height of the black line.</p>
<p>Now imagine that our robot has a "door sensor," which currently tells us that
we're in front of a door. This allows us to rule out any particle which is located <i>between</i> doors. </p>
<p>So we multiply the weight of each particle by 100% (if it's in front of a door) or 0% (if it's between doors), which gives us the lower row of particles. If our sensor was less accurate, we might use 90% and 10%, respectively.</p>
<p>What would this example look like in Haskell? We <i>could</i> build a
giant list of particles (with weights), but that would require us to do a
lot of bookkeeping by hand. Instead, we use a <a href="http://www.randomhacks.net/articles/2007/03/12/monads-in-15-minutes" title="Monads in 15 minutes">monad</a> to hide all the
details, allowing us to work with a single particle at a time.</p>
<div class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="nf">localizeRobot</span> <span class="ow">::</span> <span class="kt">WPS</span> <span class="kt">Int</span>
<span class="nf">localizeRobot</span> <span class="ow">=</span> <span class="kr">do</span>
<span class="c1">-- Pick a random starting location.</span>
<span class="c1">-- This will be our first particle.</span>
<span class="n">pos1</span> <span class="ow"><-</span> <span class="n">uniform</span> <span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">299</span><span class="p">]</span>
<span class="c1">-- We know we're at a door. Particles</span>
<span class="c1">-- in front of a door get a weight of</span>
<span class="c1">-- 100%, others get 0%.</span>
<span class="kr">if</span> <span class="n">doorAtPosition</span> <span class="n">pos1</span>
<span class="kr">then</span> <span class="n">weight</span> <span class="mi">1</span>
<span class="kr">else</span> <span class="n">weight</span> <span class="mi">0</span>
<span class="c1">-- ...</span>
</code></pre></div>
<p>What happens if our robot drives forward?</p>
<p>
<a href="http://www.randomhacks.net/2007/04/19/robot-localization-particle-system-monad/">Read more…</a>
</p>
Thu, 19 Apr 2007 20:43:00 -0400
http://www.randomhacks.net/2007/04/19/robot-localization-particle-system-monad/
http://www.randomhacks.net/2007/04/19/robot-localization-particle-system-monad/How to make Data.Set a monad
<p><b>...and how to fake Lisp macros with Template Haskell</b></p>
<p>(I wrote this article in response to <a href="http://www.randomhacks.net/articles/2007/03/05/three-things-i-dont-understand-about-monads#comment-329">a comment</a> by
<a href="http://sigfpe.blogspot.com/">sigfpe</a>. You may find it pretty dry reading, unless you want to
build <a href="http://en.wikipedia.org/wiki/Domain-specific_programming_language">domain-specific languages</a> in Haskell. Proceed at your own
risk.)</p>
<p>Haskell's built-in <code>Monad</code> type has some serious limitations. We can fix those limitations using a number of advanced Haskell techniques, including <a href="http://www.haskell.org/th/">Template Haskell</a>, Haskell's closest equivalent to Lisp macros.</p>
<p>We can illustrate the limitations of <code>Monad</code> with an example from math. In <a href="http://en.wikipedia.org/wiki/Set_theory">set theory</a>, we can define a set by specifying how to compute each
element:</p>
<blockquote>
{ <i>xy</i> : <i>x</i> ∈ {1,2,4}, <i>y</i> ∈ {1,2,4} }
</blockquote>
<p>We can read this as, "the set of all <i>xy</i>, where <i>x</i>
is one of {1,2,4}, and <i>y</i> is one of {1,2,4}." To calculate the
answer, we first multiply together all the possible combinations:</p>
<blockquote>
1×1=1, 1×2=2, 1×4=4, 2×1=2, 2×2=4, 2×4=8, 4×1=4, 4×2=8, 4×4=16
</blockquote>
<p>We then collect up the answers, and---because we're working with sets--we
throw away the duplicates:</p>
<blockquote>
{1,2,4,8,16}
</blockquote>
<p>Can we do the same thing in Haskell? Well, using Haskell's <a href="http://www.randomhacks.net/articles/2007/03/12/monads-in-15-minutes">list
monad</a>, we can write:</p>
<div class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="nf">listExample</span> <span class="ow">=</span> <span class="kr">do</span>
<span class="n">x</span> <span class="ow"><-</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span><span class="p">]</span>
<span class="n">y</span> <span class="ow"><-</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span><span class="p">]</span>
<span class="n">return</span> <span class="p">(</span><span class="n">x</span><span class="o">*</span><span class="n">y</span><span class="p">)</span>
</code></pre></div>
<p>But when we run this, Haskell gives us lots of duplicate values:</p>
<div class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="o">></span> <span class="n">listExample</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">8</span><span class="p">,</span><span class="mi">16</span><span class="p">]</span>
</code></pre></div>
<p>Our problem: We're using lists (which can contain duplicate
values) to represent sets (which can't). Can we fix this by switching to
Haskell's <code>Data.Set</code>?</p>
<div class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="kr">import</span> <span class="k">qualified</span> <span class="nn">Data.Set</span> <span class="k">as</span> <span class="n">S</span>
<span class="c1">-- This doesn't work.</span>
<span class="nf">setExample</span> <span class="ow">=</span> <span class="kr">do</span>
<span class="n">x</span> <span class="ow"><-</span> <span class="kt">S</span><span class="o">.</span><span class="n">fromList</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span><span class="p">]</span>
<span class="n">y</span> <span class="ow"><-</span> <span class="kt">S</span><span class="o">.</span><span class="n">fromList</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span><span class="p">]</span>
<span class="n">return</span> <span class="p">(</span><span class="n">x</span><span class="o">*</span><span class="n">y</span><span class="p">)</span>
</code></pre></div>
<p>Unfortunately, this code fails spectacularly. A Haskell monad is required
to work for <i>any</i> types <code>a</code> and <code>b</code>:</p>
<div class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="kr">class</span> <span class="kt">Monad</span> <span class="n">m</span> <span class="kr">where</span>
<span class="n">return</span> <span class="ow">::</span> <span class="n">a</span> <span class="ow">-></span> <span class="n">m</span> <span class="n">a</span>
<span class="n">fail</span> <span class="ow">::</span> <span class="kt">String</span> <span class="ow">-></span> <span class="n">m</span> <span class="n">a</span>
<span class="p">(</span><span class="o">>>=</span><span class="p">)</span> <span class="ow">::</span> <span class="n">m</span> <span class="n">a</span> <span class="ow">-></span> <span class="p">(</span><span class="n">a</span> <span class="ow">-></span> <span class="n">m</span> <span class="n">b</span><span class="p">)</span> <span class="ow">-></span> <span class="n">m</span> <span class="n">b</span>
</code></pre></div>
<p>But <code>Data.Set</code> only works for some types. Specifically, it
requires that values of type <code>a</code> can be ordered:</p>
<div class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="kr">data</span> <span class="p">(</span><span class="kt">Ord</span> <span class="n">a</span><span class="p">)</span> <span class="ow">=></span> <span class="kt">Set</span> <span class="n">a</span> <span class="ow">=</span> <span class="o">...</span>
</code></pre></div>
<p>As it turns out, we can make <code>Data.Set</code> into a monad. But be
warned: The solution involves some pretty ugly Haskell abuse.</p>
<p>
<a href="http://www.randomhacks.net/2007/03/15/data-set-monad-haskell-macros/">Read more…</a>
</p>
Thu, 15 Mar 2007 22:29:00 -0400
http://www.randomhacks.net/2007/03/15/data-set-monad-haskell-macros/
http://www.randomhacks.net/2007/03/15/data-set-monad-haskell-macros/