Random Hacks: Refactoring probability distributions, part 1: PerhapsT
http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions
en-us40Technology and Other Fun Stuff"Refactoring probability distributions, part 1: PerhapsT" by Robert<p>With regards to what Dan P said, there is a quantum IO monad:</p>
<p>http://www.cs.nott.ac.uk/~txa/talks/qnet06.pdf</p>Fri, 04 Apr 2008 10:37:48 +0000urn:uuid:21ae612c-60af-4f15-9c99-8491883fcf93
http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions#comment-569
"Refactoring probability distributions, part 1: PerhapsT" by Eric<p>The good news: You can make Data.Map a monad.</p>
<p>The bad news: It’s <a href="http://www.randomhacks.net/articles/2007/03/15/data-set-monad-haskell-macros">really ugly</a>.</p>
<p>I would be delighted if this were fixed in a future version of Haskell.</p>Sun, 06 May 2007 19:54:12 +0000urn:uuid:c7cd218b-7f47-4edf-afb0-cf81dc4027c7
http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions#comment-430
"Refactoring probability distributions, part 1: PerhapsT" by Samer<p>I’ve been playing around this a bit and I keep coming up against the same problem – this is possibly not the right place to ask as it’s more general than just probability monads, but since we’re already discussing this…</p>
<p>Anyway, the problem is that I want to declare a type constructor as an instance of some class, but to implement the class methods, I need constraints on the type argument. Eg, if I want to implement a discrete probability monad using a Data.Map to manage the distribution, I might have</p>
<blockquote>
<p>import qualified Data.Map as Map</p>
</blockquote>
<blockquote>
<p>newtype ProbMap a = <span class="caps">PM </span>(Map.Map a Double)</p>
</blockquote>
<p>When I come to declare an instance of Monad, I need to use some Map functions:</p>
<blockquote>
<p>instance Monad ProbMap where
return x = <span class="caps">PM </span>(Map.singleton x 1)</p>
</blockquote>
<p>This doesn’t compile because Map requires the type of x to be an instance of Ord. But the type of x doesn’t appear anywhere in the instance declaration. How can I say that ProbMap is a Monad
only for types a such that (Ord a)?</p>Sun, 06 May 2007 15:11:54 +0000urn:uuid:f8ba4929-b92e-4db2-b1b7-92d5daba795b
http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions#comment-427
"Refactoring probability distributions, part 1: PerhapsT" by Eric<p>Yeah, monad transformers are sweet, though <a href="http://www.randomhacks.net/articles/2007/03/05/three-things-i-dont-understand-about-monads">they confuse me bit at times</a>.</p>
<p>Dan and I have discussed a version of <code>WriterT</code> which (a) has a more sensible name, and (b) only supports <code>tell</code>. The rest of the <code>WriterT</code> API is fairly pernicious in this case.</p>
<p>Another cool fact is that you can replace the product with (almost?) any metric space, and experiment with things like <a href="http://www.survey.ntua.gr/main/labs/rsens/DeCETI/IRIT/MSI-FUSION/node93.html">possibility theory</a>.</p>Sun, 06 May 2007 09:32:08 +0000urn:uuid:e4d12945-0547-4f4c-8e24-19a64531e59f
http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions#comment-426
"Refactoring probability distributions, part 1: PerhapsT" by Samer<p>This is interesting stuff – I must say monad transformers still seem a bit like magic to me! Following on from what Dan said about using WriterT, it seems you don’t even need to declare a Monoid instance for probabilities – you can just use the Product type from Data.Monoid. You can also parameterise by the type of probabilities and get, eg, rational probabilities for free. Remarkably, you get all of this from a
type synonym:</p>
<blockquote>
<p>type ProbMT p = WriterT (Product p) ([])</p>
</blockquote>
<p>(Like I said – magic!) With this you can write, eg,</p>
<blockquote>
<p>lift [1,2,3] >>= \x->tell (Product (1/3))>>return x :: ProbMT Rational Int</p>
</blockquote>
<p>to get an exact distribution by thirds over {1,2,3}. (Note that tell is used to reweight a value.)</p>Sun, 06 May 2007 05:12:45 +0000urn:uuid:9aff7b01-2e6a-48e0-8b0a-7b0590b11588
http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions#comment-425
"Refactoring probability distributions, part 1: PerhapsT" by Porges<p>I don’t quite understand all the code, but this is cool :)</p>
<blockquote>...I’m still a little bit confused about how join would work…</blockquote>
<p>Wouldn’t it be along the lines of (where P = Perhaps to make it fit better):</p>
<code>join P (P x p1) p2 = P x (p1 * p2)</code>Sat, 28 Apr 2007 16:52:12 +0000urn:uuid:c3e3d9ba-114e-4876-8955-99f94cc04e2b
http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions#comment-410
"Refactoring probability distributions, part 1: PerhapsT" by Eric<p><i>Quantum computation perhaps?</i></p>
<p>Very tempting. But it’s pretty far down my list, and besides, I’m still a little bit confused about how <code>join</code> would work, and what to do about mixed states.</p>
<p>I think that this is really your area of expertise. :-)</p>Thu, 22 Feb 2007 00:09:54 +0000urn:uuid:0dc9e773-b858-4f4b-a507-8c6b2705e810
http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions#comment-303
"Refactoring probability distributions, part 1: PerhapsT" by Dan P<blockquote>
<p>generalize <span class="caps">PFP</span> in various directions.</p>
</blockquote>
<p>Quantum computation perhaps?</p>
<blockquote>
<p>I suspect there’s a lot more WriterT monads lurking out there</p>
</blockquote>
<p>Yes, so many that I think that ‘Writer’ is a terrible name as that really only captures one specific use.</p>Wed, 21 Feb 2007 19:37:26 +0000urn:uuid:9550fc29-72bb-40b8-b650-0c3466bd695d
http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions#comment-300
"Refactoring probability distributions, part 1: PerhapsT" by Eric<p>I had noticed the underlying monoid structure of Perhaps, but I hadn’t connected it to WriterT. Thanks!</p>
<p>I discovered this particular monad transformer while trying to generalize <span class="caps">PFP</span> in various directions. There was a <a href="http://en.wikipedia.org/wiki/Code_smell">nasty smell</a> in my code, and this is what fell out of the refactoring.</p>
<p>I suspect there’s a lot more WriterT monads lurking out there—monoids are such a basic structure that they can’t help but turn up again and again.</p>Wed, 21 Feb 2007 15:04:20 +0000urn:uuid:b31830e2-fdb7-4a7d-9cae-f743453f3011
http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions#comment-299
"Refactoring probability distributions, part 1: PerhapsT" by Dan P<p>Refactorings of monads are cool and this is a really nice example.</p>
<p>But even cooler is when you refactor into already existing monads. I think Perhaps is simply Writer Prob and PerhapsT Prob is WriterT Prob. You need to define Prob to be a Float but with lazy multiplication so that when evaluating 0*x, 0 is returned without evaluating x. You also need to make Prob an instance of Monoid so that mappend is ordinary multiplication.</p>
<p>With this, I think your definition Monad (PerhapsT m) comes for free from the definition of WriterT. (At least I’ve written some code to do this and it seems to work.)</p>
<p>Curiously I was coming at something related from a different direction. I’ve been toying with writing yet another monad tutorial where monads are considered to be a form of tainting. You can use this to ‘quarantine’ code you consider tainted from clean code (in the same way the IO monad sequesters impure code). I was going to approach the Writer monad with something that looks remarkably like your Perhaps monad, ie. as a number attached to some data that gives a level of trust. But I didn’t make the neat connection with probability theory that you’ve just made.</p>Wed, 21 Feb 2007 14:26:51 +0000urn:uuid:d938180a-41cb-4bee-8b94-5bf34cdfbfc5
http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions#comment-298
Refactoring probability distributions, part 1: PerhapsT<p>(Warning: This article is a bit more technical than most of my stuff. It assumes prior knowledge of monads and monad transformers.)</p>
<p>Martin Erwig and Steve Kollmansberger wrote <a href="http://web.engr.oregonstate.edu/~erwig/pfp/">PFP</a>, a really sweet Haskell library for computing with probabilities. To borrow their example:</p>
<div class="typocode"><pre><code class="typocode_haskell "><span class='hs-definition'>die</span> <span class='hs-keyglyph'>::</span> <span class='hs-conid'>Dist</span> <span class='hs-conid'>Int</span>
<span class='hs-definition'>die</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>uniform</span> <span class='hs-keyglyph'>[</span><span class='hs-num'>1</span><span class='hs-keyglyph'>..</span><span class='hs-num'>6</span><span class='hs-keyglyph'>]</span>
</code></pre></div>
<p>If we roll a die, we get the expected distribution of results:</p>
<div class="typocode"><pre><code class="typocode_default ">> die
1 16.7%
2 16.7%
3 16.7%
4 16.7%
5 16.7%
6 16.7%</code></pre></div>
<p>If you haven’t seen PFP before, I strongly encourage you to <a href="http://web.engr.oregonstate.edu/~erwig/pfp/">check it out</a>. You can use it to solve all sorts of probability puzzles.</p>
<p>Anyway, I discovered an interesting way to implement PFP using <a href="http://uebb.cs.tu-berlin.de/~magr/pub/Transformers.en.html">monad transformers</a>. Here’s what it looks like:</p>
<div class="typocode"><pre><code class="typocode_haskell "><span class='hs-keyword'>type</span> <span class='hs-conid'>Dist</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>PerhapsT</span> <span class='hs-layout'>(</span><span class='hs-conid'>[]</span><span class='hs-layout'>)</span>
<span class='hs-definition'>uniform</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>weighted</span> <span class='hs-varop'>.</span> <span class='hs-varid'>map</span> <span class='hs-layout'>(</span><span class='hs-keyglyph'>\</span><span class='hs-varid'>x</span> <span class='hs-keyglyph'>-></span> <span class='hs-layout'>(</span><span class='hs-varid'>x</span><span class='hs-layout'>,</span> <span class='hs-num'>1</span><span class='hs-layout'>)</span><span class='hs-layout'>)</span>
</code></pre></div>
<p>In other words, <code>Dist</code> can be written by adding some semantics to the standard list monad.</p>
<h3>Perhaps: A less specific version of Maybe</h3>
<p>First, let’s define a simple probability type:</p>
<div class="typocode"><pre><code class="typocode_haskell "><span class='hs-keyword'>newtype</span> <span class='hs-conid'>Prob</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>P</span> <span class='hs-conid'>Float</span>
<span class='hs-keyword'>deriving</span> <span class='hs-layout'>(</span><span class='hs-conid'>Eq</span><span class='hs-layout'>,</span> <span class='hs-conid'>Ord</span><span class='hs-layout'>,</span> <span class='hs-conid'>Num</span><span class='hs-layout'>)</span>
<span class='hs-keyword'>instance</span> <span class='hs-conid'>Show</span> <span class='hs-conid'>Prob</span> <span class='hs-keyword'>where</span>
<span class='hs-varid'>show</span> <span class='hs-layout'>(</span><span class='hs-conid'>P</span> <span class='hs-varid'>p</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>show</span> <span class='hs-varid'>intPart</span> <span class='hs-varop'>++</span> <span class='hs-str'>"."</span> <span class='hs-varop'>++</span> <span class='hs-varid'>show</span> <span class='hs-varid'>fracPart</span> <span class='hs-varop'>++</span> <span class='hs-str'>"%"</span>
<span class='hs-keyword'>where</span> <span class='hs-varid'>digits</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>round</span> <span class='hs-layout'>(</span><span class='hs-num'>1000</span> <span class='hs-varop'>*</span> <span class='hs-varid'>p</span><span class='hs-layout'>)</span>
<span class='hs-varid'>intPart</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>digits</span> <span class='hs-varop'>`div`</span> <span class='hs-num'>10</span>
<span class='hs-varid'>fracPart</span> <span class='hs-keyglyph'>=</span> <span class='hs-varid'>digits</span> <span class='hs-varop'>`mod`</span> <span class='hs-num'>10</span>
</code></pre></div>
<p>Thanks to the <code>deriving (Num)</code> declaration, we can treat <code>Prob</code> like any other numeric type.</p>
<p>We can now define <code>Perhaps</code>, which represents a value with an associated probability:</p>
<div class="typocode"><pre><code class="typocode_haskell "><span class='hs-keyword'>data</span> <span class='hs-conid'>Perhaps</span> <span class='hs-varid'>a</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>Perhaps</span> <span class='hs-varid'>a</span> <span class='hs-conid'>Prob</span>
<span class='hs-keyword'>deriving</span> <span class='hs-layout'>(</span><span class='hs-conid'>Show</span><span class='hs-layout'>)</span>
</code></pre></div>
<p>Now, this is just a generalization of Haskell’s built-in <code>Maybe</code> type, which treats a value as either present (probability 1) or absent (probability 0). All we’ve added is a range of possibilities in between: <code>Perhaps x 0.5</code> represents a 50% chance of having a value.</p>
<p>Note that there’s one small trick here: When the probability of a value is 0, we may not actually know it! But because Haskell is a lazy language, we can write:</p>
<div class="typocode"><pre><code class="typocode_haskell "><span class='hs-conid'>Perhaps</span> <span class='hs-varid'>undefined</span> <span class='hs-num'>0</span>
</code></pre></div>
<p>We’ll need a convenient way to test for this case, to make sure we don’t try to use any undefined values:</p>
<div class="typocode"><pre><code class="typocode_haskell "><span class='hs-definition'>neverHappens</span> <span class='hs-layout'>(</span><span class='hs-conid'>Perhaps</span> <span class='hs-keyword'>_</span> <span class='hs-num'>0</span><span class='hs-layout'>)</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>True</span>
<span class='hs-definition'>neverHappens</span> <span class='hs-keyword'>_</span> <span class='hs-keyglyph'>=</span> <span class='hs-conid'>False</span>
</code></pre></div>
<p>So <code>Perhaps</code> is just like <code>Maybe</code>. As it turns out, they’re both monads, and they both have an associated monad transformer.</p><p><a href="http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions">Read More</a></p>Wed, 21 Feb 2007 08:06:00 +0000urn:uuid:d54b7853-2d74-4a8e-96f3-b0c0c47c4c2bEric Kidd
http://www.randomhacks.net/articles/2007/02/21/refactoring-probability-distributions
HaskellMonadsMathProbabilityProbabilityMonadshttp://www.randomhacks.net/articles/trackback/297