Renaissance Grrrl
http://winterkoninkje.dreamwidth.org/
Renaissance Grrrl - Dreamwidth StudiosFri, 23 Jun 2017 05:37:59 GMTLiveJournal / Dreamwidth Studioswinterkoninkjepersonalhttp://v.dreamwidth.org/8328681/518115Renaissance Grrrl
http://winterkoninkje.dreamwidth.org/
10086http://winterkoninkje.dreamwidth.org/113056.htmlFri, 23 Jun 2017 05:37:59 GMT"Spring semester" in review
http://winterkoninkje.dreamwidth.org/113056.html
<p>Hi all, long time no post. A lot has been going on, but I’m finally starting to get on top of things again. I’ve been meaning to write in a bit more depth about some of this, but that want for perfection has been the enemy of the writing anything at all. So, here’s a quick synopsis of what’s been going on in my neck of the woods.</p>
<p>Both of L’s parents passed away. <a href="http://winterkoninkje.dreamwidth.org/111728.html">We’ve known this was coming</a>, but it’s still hard of course. L was out there for a bit over a month taking care of her mom. They died very close together, so we ended up having a single combined service. I was out there for about a week helping to wrap things up before whisking L back home.</p>
<p>I finally got back the results of the genetics test. Turns out I don’t have Loeys–Dietz, or at least not the same genetic variant my mother did. But I definitely have <i>something</i>. So it’s back to the diagnostic swamp trying to figure out how to give it a name so that doctors’ll take it seriously. Current working hypothesis is hypermobility-type Ehlers–Danlos. Alas, “hypermobility-type” is medical jargon for “we have no idea what this is, but it kinda looks similar to the forms of Ehlers–Danlos we do know stuff about, so let’s call it that.” So, yeah, no medical tests to “prove” that’s what it is; just your usual game of convincing folks you have enough of the symptoms to match the syndrome.</p>
<p>I’ve been getting used to paying attention to <a href="http://winterkoninkje.dreamwidth.org/112269.html">my ADHD</a> and working with it rather than trying to plow through it. It helps a lot to recognize that it’s not a failing on my part (e.g., that I can’t focus on boring things for as long as other people) but rather just part of how I’m wired. That makes it a lot easier to stop beating myself up over things, and instead figure out better ways to work with my brain rather than trying to force it into a shape it won’t take. As I’ve gotten better at this I’ve finally started getting caught up on a bunch of things that’ve fallen to the wayside over the past few years.</p>
<p>For example, I’m slowly getting caught up on the backlog of bug reports and feature requests for my various Haskell packages. Mostly been focusing on <a href="https://github.com/wrengr/logfloat">logfloat</a> and <a href="https://github.com/wrengr/unification-fd">unification-fd</a> so far, but will make it around to the others in time. So, if you sent me an email about some bug or feature over the past few years and it seems to have fallen into the void, consider <a href="https://github.com/wrengr">filing a ticket</a>.</p>
<p>Still working on getting caught up to where I should be on my dissertation.</p>
<p>Work has also been going excellently. It’s all seekrit and nonsense, so I can’t say too much about it. But lately I’ve been doing a bunch of work on characterizing families of mathematical objects, and discovering their symmetries so we can exploit them to simplify and optimize things. So lots of mathy goodness going on. It’s a bit more geometric and combinatorial than my usual algebraic fare, but it’s the sort of stuff that arises from algebraic structures so it’s not too far from home base. (If that doesn’t make sense to you, maybe take a look at <a href="http://ozark.hendrix.edu/~yorgey/pub/thesis.pdf">Brent Yorgey’s thesis</a> to see an example of the connection between combinatorics and algebraic data types.) Plus, it helps that I’ve been getting to know some of the hella queer ladies who work in my building :)</p>
<p>In other health-y news, round about the time I got officially diagnosed with ADHD I had a bunch of friends going on about what the symptoms of allism (aka non-autism) are. Though I have a bunch of autistic friends, I’ve never really known much about what autism’s really like because all the literature is written by allistic folks, for allistic folks, so they’re all “patient has underdeveloped/insufficient blah” and I’m like “according to what baseline? How much blah does it take to count as having ‘sufficient’ blah? What are diagnostic details for measuring how much blah you really have?” So I finally got to hear some details from the autistic side of the fence, where people actually explain shit and elucidate the differences. And based on that: I’m hella not allistic. I can (and should! and have been meaning to!) write a whole separate post on this topic. I’m still not entirely sure I feel comfortable adopting “autistic” label (for reasons which are, themselves, further symptoms of autism), because my experiences don’t match up perfectly with some of the parts of what is traditionally called “autism”, but I’m absolutely non-allistic. I think the spectrum of non-allism is far larger and more diverse than allistic people currently believe, but —again— a post for another time.</p><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=113056" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/113056.htmldissertatinghaskell planetpersonalhaskelladhdloeys-dietzpublic0http://winterkoninkje.dreamwidth.org/111227.htmlSun, 08 Jan 2017 21:25:25 GMTANN: containers 0.5.9.1
http://winterkoninkje.dreamwidth.org/111227.html
<h3>containers 0.5.9.1</h3>
<p>The containers package contains efficient general-purpose implementations of various basic immutable container types. The declared cost of each operation is either worst-case or amortized, but remains valid even if structures are shared.</p>
<h3>Changes since 0.5.8.1 (2016-08-31)</h3>
<p>The headline change is adding <code>merge</code> and <code>mergeA</code> for <code>Data.IntMap</code>. The versions for <code>Data.Map</code> were introduced in 0.5.8.1, so this change restores parity between the interfaces. With this in place we hope this version will make it into GHC 8.2.</p>
<p>Other changes include:<ul>
<li>Add instances for <code>Data.Graph.SCC</code>: <code>Foldable</code>, <code>Traversable</code>, <code>Data</code>, <code>Generic</code>, <code>Generic1</code>, <code>Eq</code>, <code>Eq1</code>, <code>Show</code>, <code>Show1</code>, <code>Read</code>, and <code>Read1</code>.</li>
<li>Add lifted instances (from <code>Data.Functor.Classes</code>) for <code>Data.Sequence</code>, <code>Data.Map</code>, <code>Data.Set</code>, <code>Data.IntMap</code>, and <code>Data.Tree</code>. (Thanks to Oleg Grenrus for doing a lot of this work.)</li>
<li>Properly deprecate functions in <code>Data.IntMap</code> long documented as deprecated.</li>
<li>Rename several internal modules for clarity. Thanks to esoeylemez for starting this process.</li>
<li>Make <code>Data.Map.fromDistinctAscList</code> and <code>Data.Map.fromDistinctDescList</code> more eager, improving performance.</li>
<li>Plug space leaks in <code>Data.Map.Lazy.fromAscList</code> and <code>Data.Map.Lazy.fromDescList</code> by manually inlining constant functions.</li>
<li>Add <code>lookupMin</code> and <code>lookupMax</code> to <code>Data.Set</code> and <code>Data.Map</code> as total alternatives to <code>findMin</code> and <code>findMax</code>.</li>
<li>Add <code>(!?)</code> to <code>Data.Map</code> as a total alternative to <code>(!)</code>.</li>
<li>Avoid using <code>deleteFindMin</code> and <code>deleteFindMax</code> internally, preferring total functions instead. New implementations of said functions lead to slight performance improvements overall.</li>
</ul></p>
<h3>Links</h3>
<ul>
<li>Hackage: <a href="http://hackage.haskell.org/package/containers">http://hackage.haskell.org/package/containers</a></li>
<li>GitHub: <a href="https://github.com/haskell/containers">https://github.com/haskell/containers</a></li>
</ul>
<div class="btn-group">
<a href="https://twitter.com/share?via=wrengr&url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F111227.html&text=ANN%3A%20containers%200.5.9.1" class="btn" title="Share on Twitter" target="_blank">Twitter</a>
<a href="https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F111227.html&t=ANN%3A%20containers%200.5.9.1" class="btn" title="Share on Facebook" target="_blank">Facebook</a>
<a href="https://plus.google.com/share?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F111227.html" class="btn" title="Share on Google+" target="_blank">Google+</a>
<a href="http://www.tumblr.com/share/link?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F111227.html&name=ANN%3A%20containers%200.5.9.1" class="btn" title="Share on Tumblr" target="_blank">Tumblr</a>
<a href="http://wordpress.com/press-this.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F111227.html&t=ANN%3A%20containers%200.5.9.1&s=" class="btn" title="Share on WordPress" target="_blank">WordPress</a>
</div><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=111227" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/111227.htmlhaskell planetcode releasehaskellpublic0http://winterkoninkje.dreamwidth.org/108348.htmlWed, 27 Apr 2016 22:08:05 GMTHacking projects over the next few months
http://winterkoninkje.dreamwidth.org/108348.html
<p>Life’s been really hectic lately, but I’ve been getting (slowly) back into working on my Haskell packages. In particular, since the switch from darcs to github I’ve started getting more comments and feature requests, which is nice. Over the next half-year or so, here’s what I’ll be up to in my free time between work on the dissertation and work on <a href="https://github.com/hakaru-dev/hakaru">Hakaru</a>:</p>
<p><a href="https://github.com/haskell/containers"><b><i>containers</i></b></a> — I’ve been appointed one of the new co-maintainers of our favorite venerable library. I prolly won’t be doing any major work until autumn (as mentioned when I was appointed), but I’ve had a number of conversations with David Feuer about where to take things in terms of cleaning up some old maintenance cruft.</p>
<p><a href="https://github.com/wrengr/bytestring-trie"><b><i>bytestring-trie</i></b></a> — A few years back I started reimplementing my tries to use Bagwell’s Array Mapped Tries in lieu of Okasaki’s Big-Endian Patricia Tries, but then got stalled because life. I’ve started up on it again, and it’s just about ready to be released after a few more tweaks. Also, now that I’m working on it again I can finally clear out the backlog of API requests (sorry folks!).</p>
<p><a href="https://github.com/wrengr/exact-combinatorics"><b><i>exact-combinatorics</i></b></a> — A user recently pointed me towards a new fast implementation of factorial making waves lately. It’s not clear just yet whether it’ll be faster than the current implementation, but should be easy enough to get going and run some benchmarks.</p>
<p><a href="https://github.com/wrengr/unification-fd"><b><i>unification-fd</i></b></a> — This one isn’t hacking so much as dissemination. I have a backlog of queries about why things are the way they are, which I need to address; and I’ve been meaning to continue the <a href="http://winterkoninkje.dreamwidth.org/100478.html">tutorial</a> about how to use this library for your unification needs.</p>
<p><a href="https://github.com/wrengr/logfloat"><b><i>logfloat</i><b></b></b></a> — We’ve been using this a lot in Hakaru, and there are a few performance tweaks I think I can add. The main optimization area is trying to minimize the conditionals for detecting edge cases. The biggest issue has just been coming up with some decent benchmarks. The problem, of course, is that most programs making use of logfloats do a lot of other work too so it can be tricky to detect the actual effect of changes. I think this is something Hakaru can help a lot with since it makes it easy to construct all sorts of new models.</p><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=108348" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/108348.htmlhaskell planetcodinghaskellpublic0http://winterkoninkje.dreamwidth.org/104698.htmlFri, 30 Oct 2015 05:17:27 GMTLimitations of strongly-typed ABTs
http://winterkoninkje.dreamwidth.org/104698.html
<p><a href="http://winterkoninkje.dreamwidth.org/103978.html">Last time</a> I talked a bit about ABTs; in particular, I introduced the notion of strongly-typed ABTs (or "GABTs" if you prefer) and showed how we can extend the basic idea of ABTs to guarantee well-typedness in addition to well-aritiedness. However, I also made a note that ensuring this sort of well-typedness runs counter to what Neel and other CMUers often do. One of my colleagues here at IU noticed the reason, so I thought I'd write a bit more about it.</p>
<p>The issue at stake here is how general we can make our ABT library, to minimize the amount of boilerplate needed whenever inventing a new language. By encoding object-language type systems into the kinding of the ABT, we restrict the the possible object languages we can use the ABT implementation for (namely those object languages with type systems that can be embedded into whatever kinding the ABT has). To put a finer point on it, using the kinds presented in the previous post you cannot have binders in your type system. (<i>Edit 2016.02.29: <span><a href="#104698:fn1" name="104698:fnref1">actually the details are more complicated</a></span>.</i>) This means no System F, and no dependent types. This is unfortunate as the whole point of ABTs is to capture binding structure once and for all!</p>
<p>However, I'd like to reiterate that, for our purposes in Hakaru this limitation is no restriction. Hakaru is simply-typed, so there are no type-level binders in sight. Moreover, we do <em>a lot</em> of program transformations in Hakaru. By using GABTs we can have GHC verify that our program transformations will never produce Hakaru code which is ill-typed, and that our program transformations will always produce Hakaru code of an appropriate type (e.g., the same type as the input term, for things like partial evaluation; but we have a number of type-changing transformations too). Thus, even though our GABT library could not be reused for implementing languages with type-level binders, it still provides a substantial benefit for those languages without type-level binders.</p>
<p>Although our GABTs cannot handle type-level binders, that does not mean we're restricted to only working with simply typed languages. For example, intersection types are not usually thought of as "simple types"; but they do not require binders and so they're fine. More generally, <a href="http://www.indiana.edu/~iulg/moss/">Larry Moss</a> is engaged in a research project where he asks, "given infinite time, how far could Aristotle have gotten in logic?" By which he means, given the Aristotelian restriction to syllogistic logics (i.e., ones without the quantifiers introduced by Frege), what are the limits in what we can cover? It turns out that we can cover quite a lot. Some syllogistic logics go beyond the power of the "Peano–Frege" boundary: they can handle comparing cardinality of sets! A good pictorial summary of this line of research is on slide 2 of <a href="http://www.indiana.edu/~iulg/moss/sizes.pdf">this talk</a>; and a bit more about the complexity results is given in <a href="http://www.indiana.edu/~iulg/moss/beyondAristotle.pdf">this talk</a> (the requisite picture is on slide 39).</p>
<hr />
<p><a name="104698:fn1"><i>Edit 2016.02.29:</i></a> In actuality, there's nothing inherent in type theory that prohibits having type-level binders for our object language; it's a limitation in GHC. In particular, GHC doesn't allow lifting GADTs into data kinds. If we could lift GADTs, then we could simply use ABTs to define the syntax of object-language type expressions, and lift those to serve as the type indices for using ABTs to define the syntax of object-language term expressions. This stratified approach is sufficient to handle System F and any other non-dependent quantifiers. To go further and handle dependent quantifiers as well, we'd also need to be able to define the object-language's terms and types in a mutually inductive way.<a href="#104698:fnref1">↩</a></p>
<div class="btn-group">
<a href="https://twitter.com/share?via=wrengr&url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F104698.html&text=Limitations%20of%20strongly-typed%20ABTs" class="btn" title="Share on Twitter" target="_blank">Twitter</a>
<a href="https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F104698.html&t=Limitations%20of%20strongly-typed%20ABTs" class="btn" title="Share on Facebook" target="_blank">Facebook</a>
<a href="https://plus.google.com/share?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F104698.html" class="btn" title="Share on Google+" target="_blank">Google+</a>
<a href="http://www.tumblr.com/share/link?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F104698.html&name=Limitations%20of%20strongly-typed%20ABTs" class="btn" title="Share on Tumblr" target="_blank">Tumblr</a>
<a href="http://wordpress.com/press-this.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F104698.html&t=Limitations%20of%20strongly-typed%20ABTs&s=" class="btn" title="Share on WordPress" target="_blank">WordPress</a>
</div><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=104698" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/104698.htmlhakaruhaskellhaskell planetdesigndependent typescomputer sciencepublic0http://winterkoninkje.dreamwidth.org/103978.htmlMon, 21 Sep 2015 00:40:51 GMTAbstract Binding Trees in Hakaru
http://winterkoninkje.dreamwidth.org/103978.html
<p><i>Edit 2015.10.29: Be sure to also read <a href="http://winterkoninkje.dreamwidth.org/104698.html">the followup post</a> on the benefits and limitations of this approach compared to the usual untyped ABTs.</i></p>
<p>Earlier this year Neel Krishnaswami talked about abstract binding trees (ABTs) <a href="http://semantic-domain.blogspot.co.uk/2015/03/abstract-binding-trees.html">[part 1]</a> <a href="http://semantic-domain.blogspot.co.uk/2015/03/abstract-binding-trees-addendum.html">[part 2]</a>. IMO, the best way to think about ABTs is as a generalization of abstract syntax trees (ASTs), though this is not a perspective sanctioned by the CMUers I’ve talked to. CMUers oppose this way of phrasing things, in part, because the ABT libraries they’re familiar with make crucial use of the design pattern of <a href="http://web.cecs.pdx.edu/~sheard/papers/JfpPearl.ps">two-level types</a>; but I think the essential insights of ABTs and two-level types are quite different, and we ought to keep the benefits of these two techniques distinct.</p><p>Over the past year I’ve been working on the inferential language<sup><a href="#103978:fn1" name="103978:fnref1">1</a></sup> <a href="https://github.com/hakaru-dev/hakaru">Hakaru</a>, and in the new version of the compiler we’re using ABTs for our syntax trees. However, contrary to Neel’s stance against using strongly-typed internal representations for syntax, we extend the ABT approach to make use of GADTs to guarantee local well-typedness— since this in turn can be used to guarantee that program transformations are also well-typed. (If you don’t want those guarantees, then take a look at Jon Sterling’s <a href="http://hackage.haskell.org/package/abt">abt</a> library on Hackage<sup><a href="#103978:fn2" name="103978:fnref2">2</a></sup>.) In this post I’m going to present a simplified version of our architecture, and then talk about some of the extra stuff bringing it closer to our production architecture.</p><h3>First things first</h3><p>Since we want everything to be well-typed, we first must introduce some universe, <code class="sourceCode haskell"><span class="dt">U</span></code>, of all the types in our language. (In Haskell we can implement such a universe by using the <code>-XDataKinds</code> extension, so I’ll equivocate between calling <code class="sourceCode haskell"><span class="dt">U</span></code> a “universe” vs a “kind”.) For the rest of this post it doesn’t actually matter what lives in that universe<sup><a href="#103978:fn3" name="103978:fnref3">3</a></sup>, just so long as things match up when they need to. Since the choice of universe is irrelevant, we could abstract over <code class="sourceCode haskell"><span class="dt">U</span></code> by turning on the <code>-XPolyKinds</code> extension; but I avoid doing so below, just to help keep things more concrete.</p><h3>Implementing ASTs</h3><p>The simplest way of thinking about well-typed ASTs is that they capture the set of terms generated by a (typed) signature; that is, the fixed point of some <code class="sourceCode haskell"><span class="dt">Σ</span> <span class="ot">∷</span> <span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span></code>. Unpacking the type for <code class="sourceCode haskell"><span class="dt">Σ</span></code>, we have that every syntactic constructor <code class="sourceCode haskell"><span class="fv">s</span> ∈ <span class="dt">Σ</span></code> is associated with some arity (the length of the list), each argument to <code class="sourceCode haskell"><span class="fv">s</span></code> has some type in <code class="sourceCode haskell"><span class="dt">U</span></code> (the elements of the list), and applying <code class="sourceCode haskell"><span class="fv">s</span></code> to the right number of ASTs of the right types will generate a new AST with some type in <code class="sourceCode haskell"><span class="dt">U</span></code> (the second argument to <code class="sourceCode haskell"><span class="dt">Σ</span></code>).</p><p>To implement this fixed point we define an <code class="sourceCode haskell"><span class="dt">AST</span></code> type which is parameterized by its signature. To ensure well-aritiedness (and well-typedness) of our ASTs with respect to that signature, we’ll need to introduce a helper type <code class="sourceCode haskell"><span class="dt">SArgs</span></code><sup><a href="#103978:fn4" name="103978:fnref4">4</a></sup>. And to ensure that we obtain the <em>least</em> fixed-point of the signature, we’ll make everything strict.</p><div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">infix</span> <span class="dv">4</span> <span class="dc">:$</span>
<span class="kw">infixr</span> <span class="dv">5</span> <span class="dc">:*</span>
<span class="kw">data</span> <span class="dt">SArgs</span> <span class="ot">∷</span> (<span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span>) <span class="pt">→</span> <span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="pt">⭑</span> <span class="kw">where</span>
<span class="dc">End</span> <span class="ot">∷</span> <span class="dt">SArgs</span> <span class="bv">ast</span> <span class="dc">[]</span>
(<span class="dc">:*</span>) <span class="ot">∷</span> <span class="ot">!</span>(<span class="bv">ast</span> <span class="bv">u</span>)
<span class="pt">→</span> <span class="ot">!</span>(<span class="dt">SArgs</span> <span class="bv">ast</span> <span class="bv">us</span>)
<span class="pt">→</span> <span class="dt">SArgs</span> <span class="bv">ast</span> (<span class="bv">u</span> <span class="dc">:</span> <span class="bv">us</span>)
<span class="kw">data</span> <span class="dt">AST</span> <span class="ot">∷</span> (<span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span>) <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span> <span class="kw">where</span>
(<span class="dc">:$</span>) <span class="ot">∷</span> <span class="ot">!</span>(<span class="bv">σ</span> <span class="bv">us</span> <span class="bv">u</span>)
<span class="pt">→</span> <span class="ot">!</span>(<span class="dt">SArgs</span> (<span class="dt">AST</span> <span class="bv">σ</span>) <span class="bv">us</span>)
<span class="pt">→</span> <span class="dt">AST</span> <span class="bv">σ</span> <span class="bv">u</span></code></pre></div><h3>Implementing ABTs</h3><p>The problem with ASTs is that they have no notion of variables, and thus have no notion of variable binding. Naively we could implement binders like lambda-abstraction by having something like <code class="sourceCode haskell"><span class="dc">λ</span> <span class="ot">∷</span> <span class="dt">Σ</span> <span class="dc">[</span><span class="bv">u</span><span class="dc">,</span> <span class="bv">v</span><span class="dc">]</span> (<span class="bv">u</span> <span class="dc">:→</span> <span class="bv">v</span>)</code> but then we’d need to do a post-hoc check to ensure that the first argument to <code class="sourceCode haskell"><span class="dc">λ</span></code> is in fact a variable. To build that check into the datatype itself we’d have to move <code class="sourceCode haskell"><span class="dc">λ</span></code> into the definition of <code class="sourceCode haskell"><span class="dt">AST</span></code> (since the first argument is of type <code class="sourceCode haskell"><span class="dt">Variable</span> <span class="fv">u</span></code> rather than <code class="sourceCode haskell"><span class="dt">AST</span> <span class="dt">Σ</span> <span class="fv">u</span></code>). If lambda-abstraction were the only binder we had, that might not be so bad; but any real-world language has a plethora of binders, and this approach doesn’t scale.</p><p>The essential idea behind ABTs is to abstract over the notion of binding itself. Given a single uniform definition of what it means to be a binding form, we don’t have to worry about adding a bunch of ad-hoc constructors to our AST datatype. Moreover, we can then provide single uniform definitions for things which mess with variables and are homomorphic over the signature. Things like <a href="http://semantic-domain.blogspot.co.uk/2015/03/abstract-binding-trees.html">capture-avoiding substitution</a> and providing a <a href="http://comonad.com/reader/2014/fast-circular-substitution/">HOAS API for our first-order representation</a>.</p><p>The crucial step is to adjust our notion of what a signature contains. The basic signatures used above only contained applicative forms; i.e., things we can apply to locally-closed terms; i.e., what are called “functors” in the logic programming community. For ABTs we’ll want to allow our signatures to include any <a href="http://plato.stanford.edu/entries/generalized-quantifiers/">generalized quantifier</a>. That is, our signatures will now be of type <code class="sourceCode haskell"><span class="dt">Σ</span> <span class="ot">∷</span> <span class="dt">[</span><span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="dt">×</span> <span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span></code>. Previously, the arguments were indexed by <code class="sourceCode haskell"><span class="dt">U</span></code>; now, they’re indexed by <code class="sourceCode haskell"><span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="dt">×</span> <span class="dt">U</span></code>. The length of the list gives the number of variables being bound, the types in the list give the types of those variables, and the second component of the pair gives the type of the whole locally-open expression.</p><p>To implement this we need to extend our syntax tree to include variable bindings and variable uses:</p><div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span> <span class="dt">SArgs</span> <span class="ot">∷</span> (<span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span>) <span class="pt">→</span> <span class="dt">[</span><span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="dt">×</span> <span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="pt">⭑</span> <span class="kw">where</span>
<span class="dc">End</span> <span class="ot">∷</span> <span class="dt">SArgs</span> <span class="bv">abt</span> <span class="dc">[]</span>
(<span class="dc">:*</span>) <span class="ot">∷</span> <span class="ot">!</span>(<span class="bv">abt</span> <span class="bv">vs</span> <span class="bv">u</span>)
<span class="pt">→</span> <span class="ot">!</span>(<span class="dt">SArgs</span> <span class="bv">abt</span> <span class="bv">vus</span>)
<span class="pt">→</span> <span class="dt">SArgs</span> <span class="bv">abt</span> (<span class="dc">(</span><span class="bv">vs</span><span class="dc">,</span><span class="bv">u</span><span class="dc">)</span> <span class="dc">:</span> <span class="bv">vus</span>)
<span class="kw">data</span> <span class="dt">ABT</span> <span class="ot">∷</span> (<span class="dt">[</span><span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="dt">×</span> <span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span>) <span class="pt">→</span> <span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span> <span class="kw">where</span>
(<span class="dc">:$</span>) <span class="ot">∷</span> <span class="ot">!</span>(<span class="bv">σ</span> <span class="bv">vus</span> <span class="bv">u</span>)
<span class="pt">→</span> <span class="ot">!</span>(<span class="dt">SArgs</span> (<span class="dt">ABT</span> <span class="bv">σ</span>) <span class="bv">vus</span>)
<span class="pt">→</span> <span class="dt">ABT</span> <span class="bv">σ</span> <span class="dc">[]</span> <span class="bv">u</span>
<span class="dc">Var</span> <span class="ot">∷</span> <span class="ot">!</span>(<span class="dt">Variable</span> <span class="bv">v</span>)
<span class="pt">→</span> <span class="dt">ABT</span> <span class="bv">σ</span> <span class="dc">[]</span> <span class="bv">v</span>
<span class="dc">Bind</span> <span class="ot">∷</span> <span class="ot">!</span>(<span class="dt">Variable</span> <span class="bv">v</span>)
<span class="pt">→</span> <span class="ot">!</span>(<span class="dt">ABT</span> <span class="bv">σ</span> <span class="bv">vs</span> <span class="bv">u</span>)
<span class="pt">→</span> <span class="dt">ABT</span> <span class="bv">σ</span> (<span class="bv">v</span> <span class="dc">:</span> <span class="bv">vs</span>) <span class="bv">u</span></code></pre></div><p>Time for an example of how this all fits together. To add lambda-abstraction to our language we’d have <code class="sourceCode haskell"><span class="dc">λ</span> <span class="ot">∷</span> <span class="dt">Σ</span> <span class="dc">[</span><span class="dc">(</span><span class="dc">[</span><span class="bv">u</span><span class="dc">]</span><span class="dc">,</span><span class="bv">v</span><span class="dc">)</span><span class="dc">]</span> (<span class="bv">u</span> <span class="dc">:→</span> <span class="bv">v</span>)</code>: that is, the <code class="sourceCode haskell"><span class="dc">λ</span></code> constructor takes a single argument which is a locally-open term, binding a single variable of type <code class="sourceCode haskell"><span class="fv">u</span></code>, and whose body has type <code class="sourceCode haskell"><span class="fv">v</span></code>. So given some <code class="sourceCode haskell"><span class="fv">x</span> <span class="ot">∷</span> <span class="dt">Variable</span> <span class="fv">u</span></code> and <code class="sourceCode haskell"><span class="fv">e</span> <span class="ot">∷</span> <span class="dt">ABT</span> <span class="dt">Σ</span> <span class="dc">[]</span> <span class="fv">v</span></code> we’d have the AST <code class="sourceCode haskell">(<span class="dc">λ</span> <span class="dc">:$</span> <span class="dc">Bind</span> <span class="fv">x</span> <span class="fv">e</span> <span class="dc">:*</span> <span class="dc">End</span>) <span class="ot">∷</span> <span class="dt">ABT</span> <span class="dt">Σ</span> <span class="dc">[]</span> (<span class="fv">u</span> <span class="dc">:→</span> <span class="fv">v</span>)</code>.</p><h3>“Local” vs “global” well-typedness</h3><p>With the <code class="sourceCode haskell"><span class="dt">ABT</span></code> definition above, every term of type <code class="sourceCode haskell"><span class="dt">ABT</span> <span class="dt">Σ</span> <span class="fv">vs</span> <span class="fv">u</span></code> must be locally well-typed according to the signature <code class="sourceCode haskell"><span class="dt">Σ</span></code>. I keep saying “locally” well-typed because we only actually keep track of local binding information. This is an intentional design decision. But only tracking local well-typedness does have some downsides.</p><p>So what are the downsides? Where could things go wrong? Given a locally-closed term (i.e., either <code class="sourceCode haskell"><span class="dc">Var</span> <span class="fv">x</span></code> or <code class="sourceCode haskell"><span class="fv">f</span> <span class="dc">:$</span> <span class="fv">e</span></code>) any free variables that occur inside will not have their <code class="sourceCode haskell"><span class="dt">U</span></code>-types tracked by Haskell’s type system. This introduces some room for the compiler writer to break the connection between the types of a variable’s binder and its use. That is, under the hood, every variable is represented by some unique identifier like an integer or a string. Integers and strings aren’t <code class="sourceCode haskell"><span class="dt">U</span></code>-indexed Haskell types, thus it’s possible to construct a <code class="sourceCode haskell"><span class="dt">Variable</span> <span class="fv">u</span></code> and a <code class="sourceCode haskell"><span class="dt">Variable</span> <span class="fv">v</span></code> with the same unique identifier, even though <code class="sourceCode haskell"><span class="fv">u</span></code> and <code class="sourceCode haskell"><span class="fv">v</span></code> differ. We could then <code class="sourceCode haskell"><span class="dc">Bind</span></code> the <code class="sourceCode haskell"><span class="dt">Variable</span> <span class="fv">u</span></code> but <code class="sourceCode haskell"><span class="dc">Var</span></code> the <code class="sourceCode haskell"><span class="dt">Variable</span> <span class="fv">v</span></code>. In order to ensure global well-typedness we need to ensure this can’t happen.</p><p>One way is to keep track of global binding information, as we do in the paper presentation of languages. Unfortunately, to do this we’d need to teach Haskell’s typechecker about the structural rules of our language. Without a type-level implementation of sets/maps which respects all the axioms that sets/maps should, we’d be forced to do things like traverse our ASTs and rebuild them identically, but at different type indices. This is simply too hairy to stomach. Implementing the axioms ourselves is doubly so.</p><p>Or we could fake it, using <code class="sourceCode haskell"><span class="fn">unsafeCoerce</span></code> to avoid the extraneous traversals or the complicated pattern matching on axioms. But doing this we’d erase all guarantees that adding global binding information has to offer.</p><p>A third approach, and the one we take in Hakaru, is compartmentalize the places where variables can be constructed. The variable generation code must be part of our trusted code base, but unlike the <code class="sourceCode haskell"><span class="fn">unsafeCoerce</span></code> approach we can keep all the TCB code together in one spot rather than spread out across the whole compiler.</p><h3>Stratifying our data types</h3><p>The above definition of ABTs is a simplified version of what we actually use in Hakaru. For example, Hakaru has user-defined algebraic data types, so we also need case analysis on those data types. Alas, generic case analysis is not a generalized quantifier, thus we cannot implement it with <code class="sourceCode haskell">(<span class="dc">:$</span>)</code>. We could consider just adding case analysis to the <code class="sourceCode haskell"><span class="dt">ABT</span></code> definition, but then we’d start running into extensibility issues again. Instead, we can break the <code class="sourceCode haskell"><span class="dt">ABT</span></code> type apart into two types: one for capturing variable uses and bindings, and the other for whatever syntax we can come up with. Thus,</p><div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span> <span class="dt">Syntax</span> <span class="ot">∷</span> (<span class="dt">[</span><span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="dt">×</span> <span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span>) <span class="pt">→</span> (<span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span>) <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span> <span class="kw">where</span>
(<span class="dc">:$</span>) <span class="ot">∷</span> <span class="ot">!</span>(<span class="bv">σ</span> <span class="bv">vus</span> <span class="bv">u</span>)
<span class="pt">→</span> <span class="ot">!</span>(<span class="dt">SArgs</span> <span class="bv">abt</span> <span class="bv">vus</span>)
<span class="pt">→</span> <span class="dt">Syntax</span> <span class="bv">σ</span> <span class="bv">abt</span> <span class="bv">u</span>
<span class="kw">data</span> <span class="dt">ABT</span> <span class="ot">∷</span> (<span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span>) <span class="pt">→</span> <span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span> <span class="kw">where</span>
<span class="dc">Syn</span> <span class="ot">∷</span> <span class="ot">!</span>(<span class="dt">Syntax</span> <span class="bv">σ</span> (<span class="dt">ABT</span> <span class="bv">σ</span>) <span class="bv">u</span>)
<span class="pt">→</span> <span class="dt">ABT</span> <span class="bv">σ</span> <span class="dc">[]</span> <span class="bv">u</span>
<span class="dc">Var</span> <span class="ot">∷</span> <span class="ot">!</span>(<span class="dt">Variable</span> <span class="bv">v</span>)
<span class="pt">→</span> <span class="dt">ABT</span> <span class="bv">σ</span> <span class="dc">[]</span> <span class="bv">v</span>
<span class="dc">Bind</span> <span class="ot">∷</span> <span class="ot">!</span>(<span class="dt">Variable</span> <span class="bv">v</span>)
<span class="pt">→</span> <span class="ot">!</span>(<span class="dt">ABT</span> <span class="bv">σ</span> <span class="bv">vs</span> <span class="bv">u</span>)
<span class="pt">→</span> <span class="dt">ABT</span> <span class="bv">σ</span> (<span class="bv">v</span> <span class="dc">:</span> <span class="bv">vs</span>) <span class="bv">u</span></code></pre></div><p>Of course, since we’re going to be extending <code class="sourceCode haskell"><span class="dt">Syntax</span></code> with all our language-specific details, there’s not a whole lot of benefit to parameterizing over <code class="sourceCode haskell"><span class="bv">σ</span></code>. Thus, we can simplify the types considerably by just picking some concrete <code class="sourceCode haskell"><span class="dt">Σ</span></code> to plug in for <code class="sourceCode haskell"><span class="bv">σ</span></code>.</p><p>By breaking <code class="sourceCode haskell"><span class="dt">Syntax</span></code> apart from <code class="sourceCode haskell"><span class="dt">ABT</span></code> we can now extend our notion of syntax without worrying about the details of variable binding (which can be defined once and for all on <code class="sourceCode haskell"><span class="dt">ABT</span></code>). But we could still run into extensibility issues. In particular, often we want to separate the fixed-point portion of recursive types from their generating functor so that we can do things like add annotations at every node in the recursive data type. A prime example of such annotations is keeping track of free variables, as in Neel’s original post. To allow this form of extensibility we need to break up the <code class="sourceCode haskell"><span class="dt">ABT</span></code> type into two parts: the recursion, and the <code class="sourceCode haskell"><span class="dc">Syn</span></code>/<code class="sourceCode haskell"><span class="dc">Var</span></code>/<code class="sourceCode haskell"><span class="dc">Bind</span></code> view of the ABT.</p><div class="sourceCode"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span class="kw">data</span> <span class="dt">ABT</span> <span class="ot">∷</span> (<span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span>) <span class="pt">→</span> <span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span> <span class="kw">where</span>
<span class="dc">Unview</span> <span class="ot">∷</span> <span class="ot">!</span>(<span class="dt">View</span> <span class="bv">σ</span> (<span class="dt">ABT</span> <span class="bv">σ</span>) <span class="bv">vs</span> <span class="bv">u</span>) <span class="pt">→</span> <span class="dt">ABT</span> <span class="bv">σ</span> <span class="bv">vs</span> <span class="bv">u</span>
<span class="fn">view</span> <span class="ot">∷</span> <span class="dt">ABT</span> <span class="bv">σ</span> <span class="bv">vs</span> <span class="bv">u</span> <span class="pt">→</span> <span class="dt">View</span> <span class="bv">σ</span> (<span class="dt">ABT</span> <span class="bv">σ</span>) <span class="bv">vs</span> <span class="bv">u</span>
<span class="fn">view</span> (<span class="dc">Unview</span> <span class="bv">e</span>) <span class="ot">=</span> <span class="bv">e</span>
<span class="kw">data</span> <span class="dt">View</span> <span class="ot">∷</span> (<span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span>) <span class="pt">→</span> <span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="pt">→</span> <span class="dt">U</span> <span class="pt">→</span> <span class="pt">⭑</span> <span class="kw">where</span>
<span class="dc">Syn</span> <span class="ot">∷</span> <span class="ot">!</span>(<span class="dt">Syntax</span> <span class="bv">σ</span> <span class="bv">abt</span> <span class="bv">u</span>)
<span class="pt">→</span> <span class="dt">View</span> <span class="bv">σ</span> <span class="bv">abt</span> <span class="dc">[]</span> <span class="bv">u</span>
<span class="dc">Var</span> <span class="ot">∷</span> <span class="ot">!</span>(<span class="dt">Variable</span> <span class="bv">v</span>)
<span class="pt">→</span> <span class="dt">View</span> <span class="bv">σ</span> <span class="bv">abt</span> <span class="dc">[]</span> <span class="bv">v</span>
<span class="dc">Bind</span> <span class="ot">∷</span> <span class="ot">!</span>(<span class="dt">Variable</span> <span class="bv">v</span>)
<span class="pt">→</span> <span class="ot">!</span>(<span class="dt">View</span> <span class="bv">σ</span> <span class="bv">abt</span> <span class="bv">vs</span> <span class="bv">u</span>)
<span class="pt">→</span> <span class="dt">View</span> <span class="bv">σ</span> <span class="bv">abt</span> (<span class="bv">v</span> <span class="dc">:</span> <span class="bv">vs</span>) <span class="bv">u</span></code></pre></div><p>Now, to allow arbitrary annotations we’ll replace the data type <code class="sourceCode haskell"><span class="dt">ABT</span></code> with an equivalent type class. Each instance of the <code class="sourceCode haskell"><span class="dt">ABT</span></code> class defines some sort of annotations, and we can use the <code class="sourceCode haskell"><span class="fn">view</span></code> and <code class="sourceCode haskell"><span class="fn">unview</span></code> methods to move between the instance and the concrete <code class="sourceCode haskell"><span class="dt">View</span></code> type.</p><p>There’s one last form of extensibility we may want to add. Using fixed point combinators gives us a way of describing complete trees. A different way of introducing recursion is with free monads. The free-monad combinator is just like the fixed-point combinator, except that we have an additional type parameter for metavariables and we have a data constructor for using those metavariables instead of requiring the recursion to ground out with a complete syntax tree. The reasons why this might be nice to do are beyond the scope of this post, but the point is we might want to do that so we need to split the <code class="sourceCode haskell"><span class="dt">ABT</span></code> class into two parts: one for the recursion itself, and another for the annotations.</p><p>In the end, we have a four-level type: the <code class="sourceCode haskell"><span class="dt">Syntax</span></code>, the <code class="sourceCode haskell"><span class="dt">View</span></code>, the annotations, and the recursion.</p>
<hr />
<p><a name="103978:fn1">[1]</a> In the accepted/current parlance, Hakaru is a “probabilistic programming language”; but I and a number of other folks working on such languages have become disaffected with that term of late, since it’s not entirely clear what should and should not count as a “probabilistic” PL. Over the course of a number of discussions on the topic, I’ve settled on “inferential” PL as describing what is (or, at least what <em>I</em> find) interesting about “probabilistic” PL. I’ve been meaning to write a post about the subject, and hopefully this footnote will remind me to do so.<a href="#103978:fnref1">↩</a></p>
<p><a name="103978:fn2">[2]</a> N.B., the indexing used in that package is what we get if we erase/compactify the universe <code class="sourceCode haskell"><span class="dt">U</span></code>. That is: the erasure of <code class="sourceCode haskell"><span class="dt">U</span></code> is a singleton set; the erasure of <code class="sourceCode haskell"><span class="dt">[</span><span class="dt">U</span><span class="dt">]</span></code> is isomorphic to the Peano numbers; the erasure of <code class="sourceCode haskell"><span class="dt">[</span><span class="dt">[</span><span class="dt">U</span><span class="dt">]</span> <span class="dt">×</span> <span class="dt">U</span><span class="dt">]</span></code> is isomorphic to a list of Peano numbers; etc.<a href="#103978:fnref2">↩</a></p>
<p><a name="103978:fn3">[3]</a> Though at one point I assume we have functions, <code class="sourceCode haskell">(<span class="dc">:→</span>)</code>, just for the sake of an example.<a href="#103978:fnref3">↩</a></p>
<p><a name="103978:fn4">[4]</a> Ideally we’d be able to flatten this type to avoid all the overhead of the linked list implementation. In fact, the entire AST node of <code class="sourceCode haskell">(<span class="dc">:$</span>)</code> together with its <code class="sourceCode haskell"><span class="dt">SArgs</span></code> should be flattened. These nodes have the same general layout as the heap objects in the STG machine: a record with a pointer to the data constructor (i.e., element of the signature) followed by an appropriate number of arguments; and so in principle we ought to be able to implement them directly with a single STG heap object.<a href="#103978:fnref4">↩</a></p>
<div class="btn-group">
<a href="https://twitter.com/share?via=wrengr&url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F103978.html&text=Abstract%20Binding%20Trees%20in%20Hakaru" class="btn" title="Share on Twitter" target="_blank">Twitter</a>
<a href="https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F103978.html&t=Abstract%20Binding%20Trees%20in%20Hakaru" class="btn" title="Share on Facebook" target="_blank">Facebook</a>
<a href="https://plus.google.com/share?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F103978.html" class="btn" title="Share on Google+" target="_blank">Google+</a>
<a href="http://www.tumblr.com/share/link?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F103978.html&name=Abstract%20Binding%20Trees%20in%20Hakaru" class="btn" title="Share on Tumblr" target="_blank">Tumblr</a>
<a href="http://wordpress.com/press-this.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F103978.html&t=Abstract%20Binding%20Trees%20in%20Hakaru&s=" class="btn" title="Share on WordPress" target="_blank">WordPress</a>
</div><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=103978" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/103978.htmlhaskellhakarucomputer sciencecodingdesignhaskell planetdependent typespublic0http://winterkoninkje.dreamwidth.org/102451.htmlSun, 07 Jun 2015 01:04:43 GMTANN: bytestring-lexing 0.5.0
http://winterkoninkje.dreamwidth.org/102451.html
<h3>bytestring-lexing 0.5.0</h3>
<p>The bytestring-lexing package offers extremely efficient bytestring parsers for some common lexemes: namely integral and fractional numbers. In addition, it provides efficient serializers for (some of) the formats it parses.</p>
<p>As of version 0.3.0, bytestring-lexing offers the best-in-show parsers for integral values. (According to the Warp web server's benchmark of parsing the Content-Length field of HTTP headers.) And as of this version (0.5.0) it offers (to my knowledge) the best-in-show parser for fractional/floating numbers.</p>
<h3>Changes since 0.4.3 (2013-03-21)</h3>
<p>I've completely overhauled the parsers for fractional numbers.</p>
<p>The old <code>Data.ByteString.Lex.Double</code> and <code>Data.ByteString.Lex.Lazy.Double</code> modules have been removed, as has their reliance on Alex as a build tool. I know some users were reluctant to use bytestring-lexing because of that dependency, and forked their own version of bytestring-lexing-0.3.0's integral parsers. This is no longer an issue, and those users are requested to switch over to using bytestring-lexing.</p>
<p>The old modules are replaced by the new <code>Data.ByteString.Lex.Fractional</code> module. This module provides two variants of the primary parsers. The <code>readDecimal</code> and <code>readExponential</code> functions are very simple and should suffice for most users' needs. The <code>readDecimalLimited</code> and <code>readExponentialLimited</code> are variants which take an argument specifying the desired precision limit (in decimal digits). With care, the limited-precision parsers can perform far more efficiently than the unlimited-precision parsers. Performance aside, they can also be used to intentionally restrict the precision of your program's inputs.</p>
<h3>Benchmarks</h3>
<p>The Criterion output of the benchmark discussed below, <a href="http://community.haskell.org/~wren/bytestring-lexing/bench/html/readExponential-0.5.0_ereshkigal.html">can be seen here</a>. The main competitors we compare against are <a href="http://hackage.haskell.org/package/bytestring-lexing-0.4.3.3">the previous version of bytestring-lexing</a> (which already surpassed text and attoparsec/scientific) and <a href="https://hackage.haskell.org/package/bytestring-read-0.3.0">bytestring-read</a> which was the previous best-in-show.</p>
<p>The unlimited-precision parsers provide 3.3× to 3.9× speedup over the <code>readDouble</code> function from bytestring-lexing-0.4.3.3, as well as being polymorphic over all <code>Fractional</code> values. For <code>Float</code>/<code>Double</code>: these functions have essentially the same performance as bytestring-read on reasonable inputs (1.07× to 0.89×), but for inputs which have far more precision than <code>Float</code>/<code>Double</code> can handle these functions are much slower than bytestring-read (0.30× 'speedup'). However, for <code>Rational</code>: these functions provide 1.26× to 1.96× speedup compared to bytestring-read.</p>
<p>The limited-precision parsers do even better, but require some care to use properly. For types with infinite precision (e.g., <code>Rational</code>) we can pass in an 'infinite' limit by passing the length of the input string plus one. For <code>Rational</code>: doing so provides 1.5× speedup over the unlimited-precision parsers (and 1.9× to 3× speedup over bytestring-read), because we can avoid intermediate renormalizations. Whether other unlimited precision types would see the same benefit remains an open question.</p>
<p>For types with inherently limited precision (e.g., <code>Float</code>/<code>Double</code>), we could either pass in an 'infinite' limit or we could pass in the actual inherent limit. For types with inherently limited precision, passing in an 'infinite' limit degrades performance compared to the unlimited-precision parsers (0.51× to 0.8× 'speedup'). Whereas, passing in the actual inherent limit gives 1.3× to 4.5× speedup over the unlimited-precision parsers. They also provide 1.2× to 1.4× speedup over bytestring-read; for a total of 5.1× to 14.4× speedup over bytestring-lexing-0.4.3.3!</p>
<h3>Links</h3>
<ul>
<li>Homepage: <a href="http://code.haskell.org/~wren/">http://code.haskell.org/~wren/</a></li>
<li>Hackage: <a href="http://hackage.haskell.org/package/bytestring-lexing">http://hackage.haskell.org/package/bytestring-lexing</a></li>
<li>Darcs: <a href="http://community.haskell.org/~wren/bytestring-lexing">http://community.haskell.org/~wren/bytestring-lexing</a></li>
<li>Haddock: <a href="http://community.haskell.org/~wren/bytestring-lexing/dist/doc/html/bytestring-lexing/">Darcs version</a></li>
</ul>
<div class="btn-group">
<a href="https://twitter.com/share?via=wrengr&url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F102451.html&text=ANN%3A%20bytestring-lexing%200.5.0" class="btn" title="Share on Twitter" target="_blank">Twitter</a>
<a href="https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F102451.html&t=ANN%3A%20bytestring-lexing%200.5.0" class="btn" title="Share on Facebook" target="_blank">Facebook</a>
<a href="https://plus.google.com/share?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F102451.html" class="btn" title="Share on Google+" target="_blank">Google+</a>
<a href="http://www.tumblr.com/share/link?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F102451.html&name=ANN%3A%20bytestring-lexing%200.5.0" class="btn" title="Share on Tumblr" target="_blank">Tumblr</a>
<a href="http://wordpress.com/press-this.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F102451.html&t=ANN%3A%20bytestring-lexing%200.5.0&s=" class="btn" title="Share on WordPress" target="_blank">WordPress</a>
</div><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=102451" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/102451.htmlhaskellhaskell planetcode releasepublic0http://winterkoninkje.dreamwidth.org/100478.htmlFri, 12 Dec 2014 21:22:07 GMTUnification-fd tutorial (part 1/n)
http://winterkoninkje.dreamwidth.org/100478.html
<p>A while back I released the <i>unification-fd</i> library, which gives a generic implementation of first-order unification of non-cyclic terms. I've given a few talks on how the library is implemented and what optimizations it performs, but that's not the topic for today. Today, I'm going to talk about how to <i>use</i> it.</p>
<p>Unification is a widely useful operation and, consequently, comes in many different flavors. The version currently supported by the library is the sort used by logic programming languages like Prolog, Curry, Dyna, and MiniKanren; which is the same sort that's used for unification-based type inference algorithms like Hindley–Damas–Milner. Of these two examples, the logic programming example is the simpler one to discuss— at least for folks who've used a language like Prolog before. So let's start from there.</p>
<p><i>Caveat Emptor:</i> This post is something of a stream of consciousness. I've gotten a few requests for tutorials on how to use the library, but the requests weren't terribly specific about what problems people've had or what's been difficult to figure out. So I'm shooting in the dark as far as what folks need and how much background they have. I'm going to assume you're familiar with Prolog and the basics of what unification is and does.</p>
<p><i>Preemptive apology:</i> I started writing this post months and months (and months) ago, but unintentionally dropped it after running into a certain issue and then getting distracted and moving onto other things. Actually, this happened at least twice. I'm terribly sorry about that. So, apologies for not tackling the disjunction issue in this post. I'll come back to it later, but figured this post really needs to get out the door already.</p>
<h3>Logic Terms</h3>
<p>A <b><i>term</i></b>, in Prolog, is just a fancy name for a value of some algebraic data type. In most variants of Prolog there's no explicit definition of the ADT, no restriction on what the constructors are, and no type checking to ensure that subterms have a particular shape. That is, Prolog is what's called a single-sorted logic; in other words, Prolog is an untyped/unityped language. With <i>unification-fd</i> we can implement multi-sorted (aka typed) logics, but for this tutorial we're going to stick with Prolog's single-sorted approach.</p>
<p>Opening up <code>Control.Unification</code> we'll see a handful of types and type classes, followed by a bunch of operators. The <code>UTerm</code> data type captures the recursive structure of logic terms. (<code>UTerm</code> is the free monad, if you're familiar with that terminology.) That is, given some functor <code>t</code> which describes the constructors of our logic terms, and some type <code>v</code> which describes our logic variables, the type <code>UTerm t v</code> is the type of logic terms: trees with multiple layers of <code>t</code> structure and leaves of type <code>v</code>. For our single-sorted logic, here's an implementation of <code>t</code>:</p>
<blockquote><code><pre>data T a = T String [a]</pre></code></blockquote>
<p>The <code>String</code> gives the name of the term constructor, and the list gives the ordered sequence of subterms. Thus, the Prolog term <code>foo(bar,baz(X))</code> would be implemented as <code>UTerm$T "foo" [UTerm$T "bar" [], UTerm$T "baz" [UVar x]]</code>. If we're going to be building these terms directly, then we probably want to define some smart constructors to reduce the syntactic noise:</p>
<blockquote><code><pre>foo x y = UTerm$T "foo" [x,y]
bar = UTerm$T "bar" []
baz x = UTerm$T "baz" [x]</pre></code></blockquote>
<p>Now, we can implement the Prolog term as <code>foo bar (baz x)</code>. If you prefer a more Prolog-like syntax, you can use uncurried definitions for smart constructors that take more than one argument.</p>
<h3>Unifiable</h3>
<p>In order to use our <code>T</code> data type with the rest of the API, we'll need to give a <code>Unifiable</code> instance for it. Before we do that we'll have to give <code>Functor</code>, <code>Foldable</code>, and <code>Traversable</code> instances. These are straightforward and can be automatically derived with the appropriate language pragmas.</p>
<p>The <code>Unifiable</code> class gives one step of the unification process. Just as we only need to specify one level of the ADT (i.e., <code>T</code>) and then we can use the library's <code>UTerm</code> to generate the recursive ADT, so too we only need to specify one level of the unification (i.e., <code>zipMatch</code>) and then we can use the library's operators to perform the recursive unification, subsumption, etc.</p>
<p>The <code>zipMatch</code> function takes two arguments of type <code>t a</code>. The abstract <code>t</code> will be our concrete <code>T</code> type. The abstract <code>a</code> is polymorphic, which ensures that we can't mess around with more than one level of the term at once. If we abandon that guarantee, then you can think of it as if <code>a</code> is <code>UTerm T v</code>. Thus,<code>t a</code> means <code>T (UTerm T v)</code>; and <code>T (UTerm T v)</code> is essentially the type <code>UTerm T v</code> with the added guarantee that the values aren't in fact variables. Thus, the arguments to <code>zipMatch</code> are non-variable terms.</p>
<p>The <code>zipMatch</code> method has the rather complicated return type: <code>Maybe (t (Either a (a,a)))</code>. Let's unpack this a bit by thinking about how unification works. When we try to unify two terms, first we look at their head constructors. If the constructors are different, then the terms aren't unifiable, so we return <code>Nothing</code> to indicate that unification has failed. Otherwise, the constructors match, so we have to recursively unify their subterms. Since the <code>T</code> structures of the two terms match, we can return <code>Just t0</code> where <code>t0</code> has the same <code>T</code> structure as both input terms. Where we still have to recursively unify subterms, we fill <code>t0</code> with <code>Right(l,r)</code> values where <code>l</code> is a subterm of the left argument to <code>zipMatch</code> and <code>r</code> is the corresponding subterm of the right argument. Thus, <code>zipMatch</code> is a generalized zipping function for combining the shared structure and pairing up substructures. And now, the implementation:</p>
<blockquote><code><pre>instance Unifiable T where
zipMatch (T m ls) (T n rs)
| m /= n = Nothing
| otherwise =
T n <$> pairWith (\l r -> Right(l,r)) ls rs</pre></code></blockquote>
<p>Where <a href="http://hackage.haskell.org/package/list-extras-0.4.1.3/docs/Data-List-Extras-Pair.html"><code>list-extras:Data.List.Extras.Pair.pairWith</code></a> is a version of <code>zip</code> which returns <code>Nothing</code> if the lists have different lengths. So, if the names <code>m</code> and <code>n</code> match, and if the two arguments have the same number of subterms, then we pair those subterms off in order; otherwise, either the names or the lengths don't match, so we return <code>Nothing</code>.</p>
<h3>Feature Structures</h3>
<p>For the <code>T</code> example, we don't need to worry about the <code>Left</code> option. The reason it's there is to support feature structures and other sparse representations of terms. That is, consider the following type:</p>
<blockquote><code><pre>newtype FS k a = FS (Map k a)</pre></code></blockquote>
<p>Using this type, our logic terms are sets of key–subterm pairs. When unifying maps like these, what do we do if one argument has a binding for a particular key but the other argument does not? In the <code>T</code> example we assumed that subterms which couldn't be paired together (because the lists were different lengths) meant the unification must fail. But for <code>FS</code> it makes more sense to assume that terms which can't be paired up automatically succeed! That is, we'd like to assume that all the keys which are not explicitly present in the <code>Map k a</code> are implicitly present and each one is bound to a unique logic variable. Since the unique logic variables are implicit, there's no need to actually keep track of them, we'll just implicitly unify them with the subterm that can't be paired off.</p>
<p>This may make more sense if you see the <code>Unifiable</code> instance:</p>
<blockquote><code><pre>instance (Ord k) => Unifiable (FS k) where
zipMatch (FS ls) (FS rs) =
Just . FS $
unionWith (\(Left l) (Left r) -> Right(l,r))
(fmap Left ls)
(fmap Left rs)</pre></code></blockquote>
<p>We start off by mapping <code>Left</code> over both the <code>ls</code> and the <code>rs</code>. We then call <code>unionWith</code> to pair things up. For any given key, if both <code>ls</code> and <code>rs</code> specify a subterm, then these subterms will be paired up as <code>Right(l,r)</code>. If we have extra subterms from either <code>ls</code> or <code>rs</code>, however, then we keep them around as <code>Left l</code> or <code>Left r</code>. Thus, the <code>Unifiable</code> instance for <code>FS</code> performs a union of the <code>FS</code> structure, whereas the instance for <code>T</code> performs an intersection of <code>T</code> structure.</p>
<p>The <code>Left</code> option can be used in any situation where you can immediately resolve the unification of subterms, whereas the <code>Right</code> option says you still have work to do.<sup><a href="#100478:fn1" name="100478:fnref1">1</a></sup></p>
<h3>Logic Variables</h3>
<p>The library ships with two implementations of logic variables. The <code>IntVar</code> implementation uses <code>Int</code> as the names of variables, and uses an <code>IntMap</code> to keep track of the environment. The <code>STVar</code> implementation uses <code>STRef</code>s, so we can use actual mutation for binding logic variables, rather than keeping an explicit environment around. Of course, mutation complicates things, so the two implementations have different pros and cons.</p>
<p>Performing unification has the side effect of binding logic variables to terms. Thus, we'll want to use a monad in order to keep track of these effects. The <code>BindingMonad</code> type class provides the definition of what we need from our ambient monad. In particular, we need to be able to generate fresh logic variables, to bind logic variables, and to lookup what our logic variables are bound to. The library provides the necessary instances for both <code>IntVar</code> and <code>STVar</code>.</p>
<p>You can, of course, provide your own implementations of <code>Variable</code> and <code>BindingMonad</code>. However, doing so is beyond the scope of the current tutorial. For simplicity, we'll use the <code>IntVar</code> implementation below.</p>
<h3>Example Programs</h3>
<p>When embedding Prolog programs into Haskell, the main operators we want to consider are those in the section titled "Operations on two terms". These are structural equality (i.e., equality modulo substitution), structural equivalence (i.e., structural equality modulo alpha-variance), unification, and subsumption.</p>
<p>Consider the following Horn clause in Prolog:</p>
<blockquote><code><pre>example1(X,Y,Z) :- X = Y, Y = Z.</pre></code></blockquote>
<p>To implement this in Haskell we want a function which takes in three arguments, unifies the first two, and then unifies the second two. Thus,<sup><a href="#100478:fn2" name="100478:fnref2">2</a></sup></p>
<blockquote><code><pre>example1 x y z = do
x =:= y
y =:= z</pre></code></blockquote>
<p>To run this program we'd use one of the functions <code>runIntBindingT</code>, <code>evalIntBindingT</code>, or <code>execIntBindingT</code>, depending on whether we care about the binding state, the resulting logic term, or both. Of course, since the unifications may fail, we also need to run the underlying error monad, using something like <code>runErrorT</code><sup><a href="#100478:fn3" name="100478:fnref3">3</a>,</sup><sup><a href="#100478:fn4" name="100478:fnref4">4</a></sup>. And since these are both monad transformers, we'll need to use <code>runIdentity</code> or the like in order to run the base monad. Thus, the functions to execute the entire monad stack will look like:</p>
<blockquote><code><pre>-- Aliases to simplify our type signatures. N.B., the
-- signatures are not actually required to get things
-- to typecheck.
type PrologTerm = UTerm T IntVar
type PrologFailure = UnificationFailure T IntVar
type PrologBindingState = IntBindingState T
-- N.B., the @FallibleBindingMonad@ isn't yet a monad
-- for Prolog because it doesn't support backtracking.
type FallibleBindingMonad =
ErrorT PrologFailure (IntBindingT T Identity)
-- N.B., this definition runs into certain issues.
type PrologMonad =
ErrorT PrologFailure (IntBindingT T Logic)
runFBM
:: FallibleBindingMonad a
-> (Either PrologFailure a, PrologBindingState)
runFBM = runIdentity . runIntBindingT . runErrorT</pre></code></blockquote>
<p>Here are some more examples:</p>
<blockquote><code><pre>-- A helper function to reduce boilerplate. First we get
-- a free variable, then we embed it into @PrologTerm@,
-- and then we embed it into some error monad (for
-- capturing unification failures).
getFreeVar = lift (UVar <$> freeVar)
-- example2(X,Z) :- X = Y, Y = Z.
example2 x z = do
y <- getFreeVar
x =:= y
y =:= z
-- example3(X,Z) :- example1(X,Y,Z).
example3 x z = do
y <- getFreeVar
example1 x y z
-- example4(X) :- X = bar; X = backtrack.
example4 x = (x =:= bar) <|> (x =:= atom "backtrack")
</pre></code></blockquote>
<p>The complete code for this post can be found <a href="http://community.haskell.org/~wren/unification-fd/test/tutorial/tutorial1.hs">here online</a>, or at <code>./test/tutorial/tutorial1.hs</code> in the Darcs repo. Notably, there are some complications about the semantics of <code>example4</code>; it doesn't mean what you think it should mean. We'll tackle that problem and fix it later on in the tutorial series (in part 4 or thereabouts).</p>
<h3>Term Factoring and Clause Resolution Automata (CRAs)</h3>
<p>Note that for the above examples, the Haskell functions only execute the right-hand side of the Horn clause. In Prolog itself, there's also a process of searching through all the Horn clauses in a program and deciding which one to execute next. A naive way to implement that search process would be to have a list of all the Horn clauses and walk through it, trying to unify the goal with the left-hand side of each clause and executing the right-hand side if it matches. A more efficient way would be to compile all the right-hand sides into a single automaton, allowing us to match the goal against all the right-hand sides at once. (The idea here is similar to compiling a bunch of strings together into a trie or regex.)</p>
<p>Constructing optimal CRAs is NP-complete in general, though it's feasible if we have an arbitrary ordering of clauses (e.g., Prolog's top–down order for trying each clause). The <i>unification-fd</i> library does not implement any support for CRAs at present, though it's something I'd like to add in the future. For more information on this topic, see <a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.49.6932">Dawson et al. (1995) <i>Optimizing Clause Resolution: Beyond Unification Factoring</i></a> and <a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.56.2895">Dawson et al. (1996) <i>Principles and Practice of Unification Factoring</i></a>.</p>
<h3>Other operators</h3>
<p>In addition to unification itself, it's often helpful to have various other operators on hand.</p>
<p>One such operator is the subsumption operator. Whereas unification looks for a most-general substitution which when applied to both arguments yields terms which are structurally equal (i.e., <code>l =:= r</code> computes the most general <code>s</code> such that <code>s l === s r</code>), subsumption applies the substitution to only one side. That is, <code>l</code> subsumes <code>r</code> just in case <code>r</code> is a substitution instance of <code>l</code> (i.e., there exists a substitution <code>s</code> such that <code>s l === r</code>). The symbolic name <code>(<:=)</code> comes from the fact that when <code>l</code> subsumes <code>r</code> we also say that <code>l</code> is less defined<sup><a href="#100478:fn5" name="100478:fnref5">5</a></sup> than <code>r</code>. Subsumption shows up in cases where we have to hold <code>r</code> fixed for some reason, such as when implementing polymorphism or subtyping.</p>
<p>Other operators work on just one term, such as determining the free variables of a term, explicitly applying the ambient substitution to obtain a pure term, or cloning a term to make a copy where all the free variables have been renamed to fresh variables. These sorts of operators aren't used very often in logic programming itself, but are crucial for implementing logic programming languages.</p>
<h3>Conclusion</h3>
<p>Hopefully that gives a quick idea of how the library's API is set up. Next time I'll walk through an implementation of Hindley–Damas–Milner type inference, and then higher-ranked polymorphism à la <a href="http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/putting.pdf">Peyton Jones et al. (2011) <i>Practical type inference for arbitrary-rank types</i></a>. After that, I'll discuss the complications about backtracking choice I noticed when writing this post, and walk through how to fix them. If there's still interest after that, I can get into some of the guts of the library's implementation— like ranked path compression, maximal structure sharing, and so on.</p>
<p>If you have any particular questions you'd like me to address, drop me a line.</p>
<hr />
<p><a name="100478:fn1">[1]</a> Older versions of the library used the type <code>zipMatch :: forall a b. t a -> t b -> Maybe (t (a,b))</code> in order to ensure that we did in fact properly pair up subterms from the two arguments. Unfortunately I had to relax that guarantee in order to add support for feature structures. <a href="#100478:fnref1">↩</a></p>
<p><a name="100478:fn2">[2]</a> N.B., a more efficient implementation is:</p>
<blockquote><code><pre>example1' x y z = do
y' <- x =:= y
y' =:= z</pre></code></blockquote>
<p>The unification operator returns a new term which guarantees maximal structure sharing with both of its arguments. The implementation of unification makes use of observable structure sharing, so by capturing <code>y'</code> and using it in lieu of <code>y</code>, the subsequent unifications can avoid redundant work. <a href="#100478:fnref2">↩</a></p>
<p><a name="100478:fn3">[3]</a> The <code>ErrorT</code> transformer was deprecated by <i>transformers-0.4.1.0</i>, though it still works for this tutorial. Unfortunately, the preferred <code>ExceptT</code> does not work since <code>UnificationFailure</code> doesn't have a <code>Monoid</code> instance as of <i>unification-fd-0.9.0</i>. The definition of <code>UnificationFailure</code> already contains a hack to get it to work with <code>ErrorT</code>, but future versions of the library will remove that hack and will require users to specify their own monoid for combining errors. The <code>First</code> monoid captures the current behavior, though one may prefer to use other monoids such as a monoid that gives a trace of the full execution path, or witnesses for all the backtracks, etc. <a href="#100478:fnref3">↩</a></p>
<p><a name="100478:fn4">[4]</a> To be honest, I don't entirely recall why I had the error monad explicitly separated out as a monad transformer over the binding monad, rather than allowing these two layers to be combined. Since it's so awkward, I'm sure there was some reason behind it, I just failed to make note of why. If there turns out to be no decent reason for it, future versions of the library may remove this fine-grain distinction. <a href="#100478:fnref4">↩</a></p>
<p><a name="100478:fn5">[5]</a> The symbolic name for subsumption is chosen to reflect the meaning of more/less defined (rather than more/less grounded) so that the subsumption ordering coincides with the domain ordering (think of logic variables as being bottom). This is the standard direction for looking at subsumption; though, of course, we could always consider the dual ordering instead. <a href="#100478:fnref5">↩</a></p>
<div class="btn-group">
<a href="https://twitter.com/share?via=wrengr&url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F100478.html&text=Unification-fd%20tutorial%20(part%201%2Fn)" class="btn" title="Share on Twitter" target="_blank">Twitter</a>
<a href="https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F100478.html&t=Unification-fd%20tutorial%20(part%201%2Fn)" class="btn" title="Share on Facebook" target="_blank">Facebook</a>
<a href="https://plus.google.com/share?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F100478.html" class="btn" title="Share on Google+" target="_blank">Google+</a>
<a href="http://www.tumblr.com/share/link?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F100478.html&name=Unification-fd%20tutorial%20(part%201%2Fn)" class="btn" title="Share on Tumblr" target="_blank">Tumblr</a>
<a href="http://wordpress.com/press-this.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F100478.html&t=Unification-fd%20tutorial%20(part%201%2Fn)&s=" class="btn" title="Share on WordPress" target="_blank">WordPress</a>
</div><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=100478" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/100478.htmlhaskellunificationhaskell planetpublic0http://winterkoninkje.dreamwidth.org/98119.htmlTue, 05 Aug 2014 16:01:36 GMTImagine that this is not an academic debate
http://winterkoninkje.dreamwidth.org/98119.html
<p>A followup to <a href="http://winterkoninkje.dreamwidth.org/97625.html">my previous</a> <a href="http://www.reddit.com/r/haskell/comments/2cbbgf/the_comonadreader_letter_to_a_young_haskell/cjhlqln">[reddit version]</a>:</p>
<p>The examples are of limited utility. The problem is not a few bad apples or a few bad words; were that the case it would be easier to address. The problem is a subtle one: it's in the tone and tenor of conversation, it's in the things <i>not</i> talked about, in the implicitization of assumptions, and in a decentering of the sorts of communities of engagement that Haskell was founded on.</p>
<p>Back in 2003 and 2005, communities like Haskell Cafe were communities of praxis. That is, we gathered because we do Haskell, and our gathering was a way to meet others who do Haskell. Our discussions were centered on this praxis and on how we could improve our own doing of Haskell. Naturally, as a place of learning it was also a place of teaching— but teaching was never the goal, teaching was a necessary means to the end of improving our own understandings of being lazy with class. The assumptions implicit in the community at the time were that Haskell was a path to explore, and an obscure one at that. It is not The Way™ by any stretch of the imagination. And being a small community it was easy to know every person in it, to converse as you would with a friend not as you would online.</p>
<p>Over time the tone and nature of the Cafe changed considerably. It's hard to explain the shift without overly praising the way things were before or overly condemning the shift. Whereas the Cafe used to be a place for people to encounter one another on their solitary journeys, in time it became less of a resting stop (or dare I say: cafe) and more of a meeting hall. No longer a place to meet those who do Haskell, but rather a place for a certain communal doing of Haskell. I single the Cafe out only because I have the longest history with that community, but the same overall shift has occurred everywhere I've seen. Whereas previously it was a community of praxis, now it is more a community of educationalism. In the public spaces there is more teaching of Haskell than doing of it. There's nothing wrong with teaching, but when teaching becomes the thing-being-done rather than a means to an end, it twists the message. It's no longer people asking for help and receiving personal guidance, it's offering up half-baked monad tutorials to the faceless masses. And from tutorialization it's a very short path to proselytizing and evangelizing. And this weaponization of knowledge always serves to marginalize and exclude very specific voices from the community.</p>
<p>One class of voices being excluded is women. To see an example of this, consider the response to Doaitse Swierstra's comment at the 2012 Haskell Symposium. Stop thinking about the comment. The comment is not the point. The point is, once the problematic nature of the comment was raised, how did the community respond? If you want a specific example, this is it. The example is not in what Swierstra said, the example is in how the Haskell community responded to being called out. If you don't recall how this went down, <a href="http://www.reddit.com/r/haskell/comments/zxmzv/how_to_exclude_women_from_your_technical/">here's the reddit version</a>; though it's worth pointing out that there were many other conversations outside of reddit. A <i>very</i> small number of people acquitted themselves well. A handful of people knew how to speak the party line but flubbed it by mansplaining, engaging in flamewars, or allowing the conversation to be <a href="http://www.derailingfordummies.com/">derailed</a>. And a great many people were showing their asses all over the place. Now I want you to go through and read every single comment there, including the ones below threshold. I want you to read those comments and imagine that this is not an academic debate. Imagine that this is <i>your</i> life. Imagine that <i>you</i> are the unnamed party under discussion. That <i>your</i> feelings are the ones everyone thinks they know so much about. That you personally are the one each commenter is accusing of overreacting. Imagine that you are a woman, that you are walking down the street in the middle of the night in an unfamiliar town after a long day of talks. It was raining earlier so the streets are wet. You're probably wearing flats, but your feet still hurt. You're tired. Perhaps you had a drink over dinner with other conference-goers, or perhaps not. Reading each comment, before going on to the next one, stop and ask yourself: would <i>you</i> feel safe if this commenter decided to follow you home on that darkened street? Do you feel like this person can comprehend that you are a human being on that wet street? Do you trust this person's intentions in being around you late at night? And ask yourself, when some other commenter on that thread follows you home at night and rapes you in the hotel, do you feel safe going to the comment's author <a href="http://hypatia.ca/2014/08/05/what-you-can-do/">to tell them what happened?</a> Because none of this is academic. As a woman you go to conferences and this is how you are treated. And the metric of whether you can be around someone is not whether they seem interesting or smart or anything else, the metric is: do you feel safe? If you can understand anything about what this is like, then reading that thread will make you extremely uncomfortable. The problem is not that some person makes a comment. The problem is that masculinized communities are not safe for women. The problem is that certain modes of interaction are actively hostile to certain participants. The problem is finding yourself in an uncomfortable situation and knowing that noone has your back. Knowing that anyone who agrees with you will remain silent because they do not think you are worth the time and energy to bother supporting. Because that's what silence says. Silence says you are not worth it. Silence says you are not one of us. Silence says I do not think you are entirely human. And for all the upvotes and all the conversation my previous comment has sparked on twitter, irc, and elsewhere, I sure don't hear anyone <i>here</i> speaking up to say they got my back.</p>
<p>This is not a problem about women in Haskell. Women are just the go-to example, the example cis het middle-class educated able white men are used to engaging. Countless voices are excluded by the current atmosphere in Haskell communities. I know they are excluded because I personally watched them walk out the door after incidents like the one above, and I've been watching them leave for a decade. I'm in various communities for queer programmers, and many of the folks there use Haskell but none of them will come within ten feet of "official" Haskell communities. That aversion is even stronger in the transgender/genderqueer community. I personally know at least a dozen trans Haskellers, but I'm the only one who participates in the "official" Haskell community. Last fall I got hatemail from Haskellers for bringing up the violence against trans women of color on my blog, since that blog is syndicated to Planet Haskell. Again, when I brought this up, people would express their dismay in private conversations, but noone would say a damn thing in public nor even acknowledge that I had spoken. Ours has never been a great community for people of color, and when I talk to POC about Haskell I do not even consider directing them to the "official" channels. When Ken Shan gave the <a href="http://conway.rutgers.edu/~ccshan/wiki/blog/posts/haskell2013/">program chair report</a> at the Haskell symposium last year, there was a similarly unwholesome response as with Swierstra's comment the year before. A number of people have shared their experiences in response to Ken's call, but overwhelmingly people feel like their stories of being marginalized and excluded "don't count" or "aren't enough to mention". Stop. Think about that. A lot of people are coming forward to talk about how they've been made to feel uncomfortable, and <i>while telling those stories</i> they feel the need to qualify. While actively explaining their own experiences of racism, sexism, heterosexism, cissexism, ablism, sanism, etc, they feel the simultaneous need to point out that these experiences are not out of the ordinary. Experiencing bigotry is so within the ordinary that people feel like they're being a bother to even mention it. This is what I'm talking about. This is what I mean when I say that there is a growing miasma in our community. This is how racism and sexism and ablism work. It's not smacking someone on the ass or using the N-word. It's a pervasive and insidious tone in the conversation, a thousand and one not-so-subtle clues about who gets to be included and who doesn't. And yes the sexual assaults and slurs and all that factor in, but that's the marzipan on top of the cake. The cake is made out of assuming someone who dresses "like a rapper" can't be a hacker. The cake is made out of assuming that "mother" and "professional" are exclusive categories. The cake is made out of <a href="https://www.hackerschool.com/manual#sub-sec-social-rules">well-actuallys and feigned surprise</a>. And it works this way because this is how it avoids being called into question. So when you ask for specific examples you're missing the point. I can give examples, but doing so only contributes to the errant belief that bigotry happens in moments. Bigotry is not a moment. Bigotry is a sustained state of being that permeates one's actions and how one forms and engages with community. So knowing about that hatemail, or knowing about when I had to call someone out for sharing titty pictures on Haskell Cafe, or knowing about the formation of #nothaskell, or knowing about how tepid the response to Tim's article or Ken's report were, knowing about none of these specifics helps to engage with the actual problem.</p>
<div class="btn-group">
<a href="https://twitter.com/share?text=Imagine%20that%20this%20is%20not%20an%20academic%20debate&url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F98119.html&via=wrengr" class="btn" title="Share on Twitter" target="_blank">Twitter</a>
<a href="https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F98119.html&t=Imagine%20that%20this%20is%20not%20an%20academic%20debate" class="btn" title="Share on Facebook" target="_blank">Facebook</a>
<a href="https://plus.google.com/share?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F98119.html" class="btn" title="Share on Google+" target="_blank">Google+</a>
<a href="http://www.tumblr.com/share/link?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F98119.html&name=Imagine%20that%20this%20is%20not%20an%20academic%20debate" class="btn" title="Share on Tumblr" target="_blank">Tumblr</a>
<a href="http://wordpress.com/press-this.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F98119.html&t=Imagine%20that%20this%20is%20not%20an%20academic%20debate&s=" class="btn" title="Share on WordPress" target="_blank">WordPress</a>
</div><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=98119" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/98119.htmlhaskell planetdebunkingsilence culturehaskellsociologypublic2http://winterkoninkje.dreamwidth.org/97625.htmlMon, 04 Aug 2014 02:30:37 GMTOn my pulling away from Haskell communities
http://winterkoninkje.dreamwidth.org/97625.html
<p>Gershom Bazerman gave some excellent <a href="http://comonad.com/reader/2014/letter-to-a-young-haskell-enthusiast/">advice for activism and teaching</a>. His focus was on teaching Haskell and advocating for Haskell, but the advice is much more widely applicable and I recommend it to anyone interested in activism, social justice, or education. The piece has garnered a good deal of support <a href="http://www.reddit.com/r/haskell/comments/2cbbgf/the_comonadreader_letter_to_a_young_haskell/">on reddit</a>— but, some people have expressed their impression that Gershom's advice is targeting a theoretical or future problem, rather than a very concrete and very contemporary one. I gave <a href="http://www.reddit.com/r/haskell/comments/2cbbgf/the_comonadreader_letter_to_a_young_haskell/cjg4dub">a reply there</a> about how this is indeed a very real issue, not a wispy one out there in the distance. However, I know that a lot of people like me —i.e., the people who bear the brunt of these problems— tend to avoid reddit because it is an unsafe place for us, and I think my point is deserving of a wider audience. So I've decided to repeat it here:</p>
<p>This is a very real and current problem. (Regardless of whether things are less bad in Haskell communities than in other programming communities.) I used to devote a lot of energy towards teaching folks online about the ideas behind Haskell. However, over time, I've become disinclined to do so as these issues have become more prevalent. I used to commend Haskell communities for offering a safe and welcoming space, until I stopped feeling quite so safe and welcomed myself.</p>
<p>I do not say this to shame anyone here. I say it as an observation about why I have found myself pulling away from the Haskell community over time. It is not a deliberate act, but it is fact all the same. The thing is, if someone like me —who supports the ideology which gave rise to Haskell, who is well-educated on the issues at hand, who uses Haskell professionally, who teaches Haskell professionally, and most importantly: who takes joy in fostering understanding and in building communities— if someone like me starts instinctively pulling away, that's a problem.</p>
<p>There are few specific instances where I was made to feel unsafe directly, but for years there has been a growing ambiance which lets me know that I am not welcome, that I am not seen as being part of the audience. The ambiance (or should I say miasma?) is one that pervades most computer science and programming/tech communities, and things like dogmatic activism, dragon slaying, smarter-than-thou "teaching", anti-intellectualism, hyper-intellectualism, and talking over the people asking questions, are all just examples of the overarching problem of elitism and exclusion. The problem is not that I personally do not feel as welcomed as I once did, the problem is that many people do not feel welcome. The problem is not that my experience and expertise are too valuable to lose, it's that everyone's experience and expertise is too valuable to lose. The problem is not that I can't teach people anymore, it's that people need teachers and mentors and guides. And when the tenor of conversation causes mentors and guides to pull away, causes the silencing of experience and expertise, causes the exclusion and expulsion of large swaths of people, that always has an extremely detrimental impact on the community.</p>
<div class="btn-group">
<a href="https://twitter.com/share?text=On%20my%20pulling%20away%20from%20Haskell%20communities&url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F97625.html&via=wrengr" class="btn" title="Share on Twitter" target="_blank">Twitter</a>
<a href="https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F97625.html&t=On%20my%20pulling%20away%20from%20Haskell%20communities" class="btn" title="Share on Facebook" target="_blank">Facebook</a>
<a href="https://plus.google.com/share?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F97625.html" class="btn" title="Share on Google+" target="_blank">Google+</a>
<a href="http://www.tumblr.com/share/link?url=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F97625.html&name=On%20my%20pulling%20away%20from%20Haskell%20communities" class="btn" title="Share on Tumblr" target="_blank">Tumblr</a>
<a href="http://wordpress.com/press-this.php?u=http%3A%2F%2Fwinterkoninkje.dreamwidth.org%2F97625.html&t=On%20my%20pulling%20away%20from%20Haskell%20communities&s=" class="btn" title="Share on WordPress" target="_blank">WordPress</a>
</div><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=97625" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/97625.htmlhaskellsociologydebunkingcomputer sciencehaskell planetpublic0http://winterkoninkje.dreamwidth.org/90905.htmlThu, 06 Feb 2014 02:11:51 GMTSeminearrings
http://winterkoninkje.dreamwidth.org/90905.html
<p>So there was a discussion recently on the libraries mailing list about how to deal with <code>MonadPlus</code>. In particular, the following purported law fails all over the place: <code>x >> mzero = mzero</code>. The reason it fails is that we are essentially assuming that any "effects" that <code>x</code> has can be undone once we realize the whole computation is supposed to "fail". Indeed this rule is too strong to make sense for our general notion that <code>MonadPlus</code> provides a notion of choice or addition. I propose that the correct notion that <code>MonadPlus</code> should capture is that of a <b><i>right-seminearring</i></b>. (The name right-nearsemiring is also used in the literature.) Below I explain what the heck a (right-)seminearring is.</p>
<h3>Monoids</h3>
<p>First, I will assume you know what a monoid is. In particular, it's any <i>associative</i> binary operation with a distinguished element which serves as both <i>left- and right-identity</i> for the binary operation. These are ubiquitous and have become fairly well-known in the Haskell community of late. A prime example is <code>(+,0)</code> —that is, addition together with the zero element; for just about any any notion of "numbers". Another prime example is <code>(*,1)</code>— multiplication together with unit; again, for just about any notion of "numbers".</p>
<p>An important caveat regarding intuitions is that: both "addition" and "multiplication" of our usual notions of numbers turn out to be <i>commutative</i> monoids. For the non-commutative case, let's turn to regular expressions (regexes). First we have the notion of regex catenation, which captures the notion of sequencing: first we match one regex and then another; let's write this as <code>(*,1)</code> where here we take <code>1</code> to mean the regex which matches only the empty string. This catenation of strings is very different from multiplication of numbers because we can't swap things around. The regex <code>a*b</code> will first match <code>a</code> and <i>then</i> match <code>b</code>; whereas the regex <code>b*a</code> will match <code>b</code> first. Nevertheless, catenation (of strings/sequences/regexes/graphs/...) together with the empty element still forms a monoid because catenation is associative and catenating the empty element does nothing, no matter which side you catenate on.</p>
<p>Importantly, the non-deterministic choice for regexes also forms a monoid: <code>(+,0)</code> where we take <code>0</code> to be the absurd element. Notably, the <i>empty</i> element (e.g., the singleton set of strings, containing only the empty string) is distinct from the <i>absurd</i> element (e.g., the empty set of strings). We often spell <code>1</code> as <code>ε</code> and spell <code>0</code> as <code>∅</code>; but I'm going to stick with the arithmetical notation of <code>1</code> and <code>0</code>.</p>
<h3>Seminearrings</h3>
<p>Okay, so what the heck is a right-seminearring? <b>First,</b> we assume some ambient set of elements. They could be "numbers" or "strings" or "graphs" or whatever; but we'll just call them elements. <b>Second,</b> we assume we have a semigroup <code>(*)</code>— that is, our <code>*</code> operator is associative, and that's it. Semigroups are just monoids without the identity element. In our particular case, we're going to assume that <code>*</code> is <i>non-commutative</i>. Thus, it's going to work like catenation— except we don't necessarily have an empty element to work with. <b>Third,</b> we assume we have some monoid <code>(+,0)</code>. Our <code>+</code> operator is going to act like non-deterministic choice in regexes— but, we're not going to assume that it's commutative! That is, while it represents "choice", it's some sort of <i>biased choice</i>. Maybe we always try the left option first; or maybe we always try the right option first; or maybe we flip a biased coin and try the left option first only 80% of the time; whatever, the point is it's not entirely non-deterministic, so we can't simply flip our additions around. <b>Finally,</b> we require that our <code>(*)</code> semigroup distributes <i>from the right</i> over our <code>(+,0)</code> monoid (or conversely, that we can factor the monoid out from under the semigroup, again only factoring out parts that are on the <i>right</i>). That is, symbolically, we require the following two laws to hold:
<blockquote><p><code>0*x = 0</code><br />
<code>(x+y)*z = (x*z)+(y*z)</code></p></blockquote>
<p>So, what have we done here? Well, we have these two interlocking operations where "catenation" distributes over "choice". What the first law mean is that: (1) if we first do something absurd or impossible and then do <code>x</code>, well that's impossible. We'll never get around to doing <code>x</code> so we might as well just drop that part. The second law means: (2) if we first have a choice between <code>x</code> and <code>y</code> and then we'll catenate whichever one with <code>z</code>, this is the same as saying our choice is really between doing <code>x</code> followed by <code>z</code> vs doing <code>y</code> followed by <code>z</code>.</p>
<h3>MonadPlus</h3>
<p>Okay, so what does any of this have to do with <code>MonadPlus</code>? Intuitively, our <code>*</code> operator is performing catenation or sequencing of things. Monads are all about sequencing. So how about we use the monad operator <code>(>>)</code> as our "multiplication"! This does what we need it to since <code>(>>)</code> is associative, by the monad laws. In order to turn a monad into a <code>MonadPlus</code> we must define <code>mplus</code> (aka the <code>+</code> operator) and we must define a <code>mzero</code> (aka the <code>0</code> element). And the laws our <code>MonadPlus</code> instance must uphold are just the two laws about distributing/factoring on the right. In restating them below, I'm going to generalize the laws to use <code>(>>=)</code> in lieu of <code>(>>)</code>:</p>
<blockquote><p><code>mzero >>= f = mzero</code><br />
<code>(x `mplus` y) >>= f = (x >>= f) `mplus` (y >>= f)</code></p></blockquote>
<p>And the reason why these laws make sense are just as described before. If we're going to "fail" or do something absurd followed by doing something else, well we'll never get around to that something else because we've already "failed". And if we first make a choice and then end up doing the same thing regardless of the choice we made, well we can just push that continuation down underneath the choice.</p>
<p>Both of these laws make intuitive sense for what we want out of <code>MonadPlus</code>. And given that seminearrings are something which have shown up often enough to be named, it seems reasonable to assume that's the actual pattern we're trying to capture. The one sticking point I could see is my generalization to using <code>(>>=)</code>. In the second law, we allow <code>f</code> to be a function which "looks inside" the monad, rather than simply being some fixed monadic value <code>z</code>. There's a chance that some current <code>MonadPlus</code> implementations will break this law because of that insight. If so, then we can still back off to the weaker claim that <code>MonadPlus</code> should implement a right-seminearring <i>exactly</i>, i.e., with the <code>(>>)</code> operator as our notion of multiplication/catenation. <span style="text-decoration:line-through;">This I leave as an exercise for the reader.</span> This is discussed further in the addendum below.</p>
<p>Notably, from these laws it is impossible to derive <code>x*0 = 0</code>, aka <code>x >> mzero = mzero</code>. And indeed that is a stringent requirement to have, since it means we must be able to undo the "effects" of <code>x</code>, or else avoid doing those "effects" in the first place by looking into the future to know that we will eventually "fail". If we could look into the future to know we will fail, then we could implement backtracking search for logic programming in such a way that we <i>always pick the right answer.</i> Not just return results consistent with always choosing the right answer, which backtracking allows us to do; but rather, to always know the right answer beforehand and so never need to backtrack! If we satisfy the <code>x*0 = 0</code> law, then we could perform all the "search" during compile time when we're applying the rewrite rule associated with this law.</p>
<h3>Addendum</h3>
<p>There's a <a href="http://www.haskell.org/haskellwiki/MonadPlus">long history of debate</a> between proponents of the generalized distribution law I presented above, vs the so-called "catch" law. In particular, <code>Maybe</code>, <code>IO</code>, and <code>STM</code> obey the catch law but do not obey the generalized distribution law. To give an example, consider the following function:</p>
<blockquote><p><code>f a' = if a == a' then mzero else return a'</code></p></blockquote>
<p>Which is used in the following code and evaluation trace for the <code>Maybe</code> monad:</p>
<blockquote><p><code>mplus (return a) b >>= f</code><br />
<code>⟶ Just a >>= f</code><br />
<code>⟶ f a</code><br />
<code>⟶ if a == a then mzero else return a</code><br />
<code>⟶ mzero</code></p></blockquote>
<p>As opposed to the following code and evaluation trace:</p>
<blockquote><p><code>mplus (return a >>= f) (b >>= f)</code><br />
<code>⟶ mplus (f a) (b >>= f)</code><br />
<code>⟶ mplus mzero (b >>= f)</code><br />
<code>⟶ b >>= f</code></p></blockquote>
<p>But <code>b >>= f</code> is not guaranteed to be identical to <code>mzero</code>. The problem here is, as I suspected, because the generalized distribution law allows the continuation to "look inside". If we revert back to the non-generalized distribution law which uses <code>(>>)</code>, then this problem goes away— at least for the <code>Maybe</code> monad.</p>
<h3>Second Addendum (2014.02.06)</h3>
<p>Even though <code>Maybe</code> satisfies the non-generalized distributivity laws, it's notable that other problematic <code>MonadPlus</code> instances like <code>IO</code> fail even there! For example,</p>
<p>First consider <code>mplus a b >> (c >> mzero)</code>. Whenever <code>a</code> succeeds, we get that this is the same as <code>a >> c >> mzero</code>; and if <code>a</code> fails, then this is the same as <code>a' >> b >> c >> mzero</code> where <code>a'</code> is the prefix of <code>a</code> up until failure occurs.</p>
<p>Now instead consider <code>mplus (a >> c >> mzero) (b >> c >> mzero)</code>. Here, if <code>a</code> succeeds, then this is the same as <code>a >> c >> b >> c >> mzero</code>; and if <code>a</code> fails, then it's the same as <code>a' >> b >> c >> mzero</code>. So the problem is, depending on whether we distribute or not, the effects of <code>c</code> will occur once or twice.</p>
<p>Notably, the problem we're running into here is exactly the same one we started out with, the failure of <code>x >> mzero = mzero</code>. Were this law to hold for <code>IO</code> (etc) then we wouldn't run into the problem of running <code>c</code> once or twice depending on distributivity.</p></p><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=90905" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/90905.htmlsemi(near)ringsparsinghaskell planetmathshaskellpublic2http://winterkoninkje.dreamwidth.org/90440.htmlMon, 20 Jan 2014 23:22:00 GMTIn which a conundrum is discussed
http://winterkoninkje.dreamwidth.org/90440.html
<p>Now that the hectic chaos of last semester is past, I've been thinking of getting back into blogging again. I started tweeting a few months back, and since then I've gotten back into activism and gathered a new batch of friends who talk about all manner of interesting things. Both of these —friendships and activism— have long been motivating forces for my thinking and writing. And so, excited by tweeting, I've been wanting to write again in a less ephemeral medium.</p>
<p>But I face something of a conundrum.</p>
<p>When I started blogging it was always just a place for me to ramble out thoughts on whatever passes through my head. It was never my goal to keep the blog focused on any particular topic. After leaving Portland, and lacking the wide network of friends I was used to there, I dove headlong into the Haskell community. In addition, a few years back, I started working on a major Haskell project (from which most of my published Haskell code derives). So, for a time, the vast majority of my blogging was dominated by Haskell, which is why I signed up to be syndicated on Haskell Planet.</p>
<p>To be clear, I have no intention of leaving the Haskell community for the foreseeable future. I still use Haskell regularly, still teach it to others, etc. However, of late, my thoughts have been elsewhere. Computationally I've been focusing more on type theory and category theory themselves, rather than their uses and applications in Haskell per se. Linguistically I've been looking more into semantic issues, as well as some of the newer models for incorporating syntax into NLP. Sociologically I've been, as I said, thinking a lot more about social justice issues. Not to mention more casual things like reading, gaming, cooking, and all that.</p>
<p>Back when I joined the Planet it was pretty active and had lots of material which was only loosely related to Haskell; e.g., all the musicians and live coders who used Haskell for their work. I loved this wide-ranging view of Haskell, and this diversity is a big part of why I fell in love with the community. In such an environment, I think my blog fits well enough. However, over the years I've noticed the Planet becoming far more narrow and focused on code development alone. I think Phil Wadler is probably the only one who goes on about other stuff these days. Given this trend, it's not so clear that my ramblings would mesh well with the Planet as it stands.</p>
<p>So that's where I'm at. Not sure whether to quit syndicating to Haskell Planet, or to make a special filtered feed for the Haskell-only stuff, or what. If you have any opinions on the matter, please do comment. Otherwise I'll prolly just start writing and wait for people to complain.</p><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=90440" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/90440.htmlhaskellpublic1http://winterkoninkje.dreamwidth.org/86282.htmlSat, 03 Aug 2013 06:02:14 GMTMore commutative operators
http://winterkoninkje.dreamwidth.org/86282.html
<h3>The final API for (<κ)-commutative operators</h3>
<a href="http://winterkoninkje.dreamwidth.org/86236.html">Last time</a> I talked about generalizing the notion of quasi-unordered pairs to the notion of quasi-unordered multisets. The final API from last time was:
<blockquote><code><pre>type Confusion :: * → *
isSingleton :: Confusion a → Maybe a
size :: Confusion a → Cardinal
observe :: Ord r ⇒ (a → r) → Confusion a → [(r, Either (Confusion a) a)]</pre></code></blockquote>
<p>Now, every function of type <code>Confusion a → b</code> is guaranteed to be a (<κ)-commutative operator, where κ is implicitly given by the definition of <code>Confusion</code>. However, we have no way to construct the arguments to those functions! We need to add a function <b><code>confuse :: ∀λ. 0<λ<κ ⇒ Vector a λ → Confusion a</code></b> so that we can construct arguments for our (<κ)-commutative operators. Of course, rather than using bounded quantification and the <code>Vector</code> type, it'd be a lot easier to just define a type which incorporates this quantification directly:
<blockquote><code><pre>data BoundedList (a::*) :: Nat → * where
BLNil :: BoundedList a n
BLCons :: a → BoundedList a n → BoundedList a (1+n)
data NonEmptyBoundedList (a::*) :: Nat → * where
NEBLSingleton :: a → NonEmptyBoundedList a 1
NEBLCons :: a → NonEmptyBoundedList a n → NonEmptyBoundedList a (1+n)</pre></code></blockquote>
Now we have:
<blockquote><code><pre>confuse :: NonEmptyBoundedList a κ → Confusion a
type Commutative a b = Confusion a → b
runCommutative :: Commutative a b → NonEmptyBoundedList a κ → b
runCommutative f xs = f (confuse xs)</pre></code></blockquote></p>
<p>Ideally, we'd like to take this a step further and have a version of <code>runCommutative</code> which returns a variadic function of type <code>a → ... a → b</code> for the appropriate number of arguments. This way we'd be able to call them like regular curried functions rather than needing to call them as uncurried functions. There are a number of ways to do variadic functions in Haskell, but discussing them would take us too far afield. Naturally, implementing them will amount to taking advantage of the 4-tuple for folding over multisets, which was defined last time.</p>
<h3>Handling κ-commutative operators</h3>
<p>Continuing the theme, suppose we really want to handle the case of κ-commutative operators rather than (<κ)-commutative operators. For simplicity, let's restrict ourselves to finite κ, and let's pretend that Haskell has full dependent types. If so, then we can use the following API:
<blockquote><code><pre>type Confusion :: * → Nat → *
extractSingleton :: Confusion a 1 → a
size :: Confusion a n → Nat
size _ = n
data ConfusionList (r, a :: *) :: Nat → * where
CLNil :: ConfusionList r a 0
CLCons :: r → Confusion a n → ConfusionList r a m → ConfusionList r a (n+m)
observe :: Ord r ⇒ (a → r) → Confusion a n → ConfusionList r a n
confuse :: Vector a (1+n) → Confusion a (1+n)
type Commutative a n b = Confusion a n → b
runCommutative :: Commutative a n b → Vector a n → b
runCommutative f xs = f (confuse xs)</pre></code></blockquote></p><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=86282" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/86282.htmlmathshaskellpublic0http://winterkoninkje.dreamwidth.org/85357.htmlMon, 22 Jul 2013 07:14:06 GMTANN: data-fin
http://winterkoninkje.dreamwidth.org/85357.html
<h3>data-fin 0.1.0</h3>
<p>The data-fin package offers the family of totally ordered finite sets,
implemented as newtypes of <code>Integer</code>, etc. Thus, you get all the joys of:</p>
<blockquote><code><pre>data Nat = Zero | Succ !Nat
data Fin :: Nat -> * where
FZero :: (n::Nat) -> Fin (Succ n)
FSucc :: (n::Nat) -> Fin n -> Fun (Succ n)</pre></code></blockquote>
<p>But with the efficiency of native types instead of unary encodings.</p>
<h3> Notes </h3>
<p>I wrote this package for a linear algebra system I've been working on, but
it should also be useful for folks working on Agda, Idris, etc, who want
something more efficient to compile down to in Haskell. The package is
still highly experimental, and I welcome any and all feedback.</p>
<p>Note that we implement type-level numbers using [1] and [2], which works
fairly well, but not as nicely as true dependent types since we can't
express certain typeclass entailments. Once the constraint solver for
type-level natural numbers becomes available, we'll switch over to using
that.</p>
<p>[1] Oleg Kiselyov and Chung-chieh Shan. (2007) <a href="http://okmij.org/ftp/Haskell/types.html#binary-arithm">Lightweight static resources: Sexy types for embedded and systems programming</a>. <i>Proc. Trends in Functional Programming.</i> New York, 2–4 April 2007.</p>
<p>[2] Oleg Kiselyov and Chung-chieh Shan. (2004) <a href="http://okmij.org/ftp/Haskell/types.html#Prepose">Implicit configurations: or, type classes reflect the values of types</a>. <i>Proc. ACM SIGPLAN 2004 workshop on Haskell.</i> Snowbird, Utah, USA, 22 September 2004. pp.33–44.</p>
<h3>Links</h3>
<ul>
<li>Homepage: <a href="http://code.haskell.org/~wren/">http://code.haskell.org/~wren/</a></li>
<li>Hackage: <a href="http://hackage.haskell.org/package/data-fin">http://hackage.haskell.org/package/data-fin</a></li>
<li>Darcs: <a href="http://community.haskell.org/~wren/data-fin">http://community.haskell.org/~wren/data-fin</a></li>
<li>Haddock: <a href="http://community.haskell.org/~wren/data-fin/dist/doc/html/data-fin/">Darcs version</a></li>
</ul><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=85357" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/85357.htmlcode releasehaskellpublic1http://winterkoninkje.dreamwidth.org/85045.htmlSat, 20 Jul 2013 23:46:39 GMTNotions of powers
http://winterkoninkje.dreamwidth.org/85045.html
<p>Over on Reddit there's a discussion where one commenter admitted:
<blockquote>"the whole <code>(^)</code> vs <code>(^^)</code> vs <code>(**)</code> [distinction in Haskell] confuses me."</blockquote>
It's clear to me, but it's something they don't teach in primary school, and it's something most programming languages fail to distinguish. The main problem, I think, for both primary ed and for other PLs, is that they have an impoverished notion of what "numbers" could be, and this leads to artificially conflating things which should be kept distinct. I wrote a reply over on Reddit, hoping to elucidate the distinction, but I thought I should repeat it in more persistent venue so it can reach a wider audience.</p>
<p>First, let us recall the basic laws of powers:
<blockquote><code><pre>a^0 = e
a^1 = a
(a^x)^y = a^(x*y)
(a^x)*(a^y) = a^(x+y)
(a*b)^x = (a^x)*(b^x)</pre></code></blockquote></p>
<p>There are two very important things to notice here. First off, our underlying algebra (the <code>a</code>s and <code>b</code>s) only needs to have the notion of multiplication, <code>(*)</code>, with identity, <code>e</code>. Second, our powers (the <code>x</code>s and <code>y</code>s) have an entirely different structure; in particular, they form at least a semiring <code>(+,0,*,1)</code>. Moreover, if we're willing to give up some of those laws, then we can weaken these requirements. For example, if we get rid of <code>a^0 = e</code> then we no longer need our underlying algebra to be a monoid, being a semigroup is enough. And actually, we don't even need it to be a semigroup. We don't need full associativity, all we need for this to be consistent is <a href="http://en.wikipedia.org/wiki/Power_associativity">power-associativity</a>.</p>
<p>So we can go weaker and more abstract, but let's stick here for now. Any time we have a monoid, we get a notion of powers for free. This notion is simply iterating our multiplication, and we use the commutative semiring <code>(Natural,+,0,*,1)</code> in order to represent our iteration count. This is the notion of powers that Haskell's <code>(^)</code> operator captures. Unfortunately, since Haskell lacks a standard <code>Natural</code> type (or <code>Semiring</code> class), the type signature for <code>(^)</code> lies and says we could use <code>Integer</code> (or actually, <code>Num</code> which is the closest thing we have to <code>Ring</code>), but the documentation warns that negative powers will throw exceptions.</p>
<p>Moving on to the <code>(^^)</code> operator: suppose our monoid is actually a group, i.e. it has a notion of reciprocals. Now, we need some way to represent those reciprocals; so if we add subtraction to our powers (yielding the commutative ring <code>(Integer,+,-,0,*,1)</code>), we get the law <code>a^(-x) = 1/(a^x)</code>. The important thing here is to recognize that not all monoids form groups. For example, take the monoid of lists with concatenation. We do have a <code>(^)</code> notion of powers, which may be more familiar as the <code>replicate</code> function from the Prelude. But, what is the reciprocal of a string? what is the inverse of concatenation? The <code>replicate</code> function simply truncates things and treats negative powers as if they were zero, which is on par with <code>(^)</code> throwing exceptions. It is because not all monoids are groups that we need a notion of powers for monoids (i.e., <code>(^)</code>) which is different from the notion of powers for groups (i.e., <code>(^^)</code>). And though Haskell fails to do so, we can cleanly capture this difference in the type signatures for these operations.</p>
<p>Further up, we get another notion of powers which Haskell doesn't highlight; namely the notion of powers that arises from the field <code>(Rational,+,-,0,*,/,1)</code>. To get here, we take our group and add to it the ability to take roots. The fractions in powers are now taken to represent the roots, as in the law <code>a^(1/y) = root y a</code>. Again note that there's a vast discrepancy between our underlying algebra which has multiplication, reciprocals, and roots vs our powers which have addition, subtraction, multiplication, and division.</p>
<p>Pulling it back a bit, what if our monoid has a notion of roots, but does not have inverses? Here our powers form a semifield; i.e., a commutative semiring with multiplicative inverses; e.g., the non-negative rationals. This notion is rather obscure, so I don't fault Haskell for lacking it, though it's worth mentioning.</p>
<p>Finally, <code>(**)</code> is another beast altogether. In all the previous examples of powers there is a strong distinction between the underlying algebra and the powers over that algebra. But here, we get exponentiation; that is, our algebra has an <i>internal</i> notion of powers over <i>itself!</i> This is remarkably powerful and should not be confused with the basic notion of powers. Again, this is easiest to see by looking at where it fails. Consider multiplication of (square) matrices over some semiring. This multiplication is associative, so we can trivially implement <code>(^)</code>. Assuming our semiring is actually a commutative ring then almost all (though not all) matrices have inverses, so we can pretend to implement <code>(^^)</code>. For some elements we can even go so far as <a href="http://en.wikipedia.org/wiki/Square_root_of_a_matrix">taking roots</a>, though we run into the problem of there being multiple roots. But as for exponentiation? It's not even clear that <code>(**)</code> should be meaningful on matrices. Or rather, to the extent that it is meaningful, it's not clear that the result should be a matrix.</p>
<p>N.B., I refer to <code>(**)</code> as exponentials in contrast to <code>(^)</code>, <code>(^^)</code>, etc as powers, following the standard distinction in category theory and elsewhere. Do note, however, that this notion of exponentials is different again from the notion of the antilog <code>exp</code>, i.e. the inverse of <code>log</code>. The log and antilog are maps between additive monoids and multiplicative monoids, with all the higher structure arising from that. We can, in fact, give a notion of <a href="http://en.wikipedia.org/wiki/Matrix_exponential">antilog for matrices</a> if we assume enough about the elements of those matrices.</p><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=85045" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/85045.htmlhaskellmathspublic4http://winterkoninkje.dreamwidth.org/83994.htmlThu, 30 May 2013 00:05:57 GMTANN: prelude-safeenum
http://winterkoninkje.dreamwidth.org/83994.html
<h3>prelude-safeenum 0.1.0</h3>
<p>The prelude-safeenum package offers a safe alternative to the Prelude's <code>Enum</code> class in order to render it safe. While we're at it, we also generalize the notion of enumeration to support types which can only be enumerated in one direction.</p>
<h3>Description</h3>
<p>The prelude-safeenum package offers an alternative to the notion of enumeration provided by the Prelude. For now it is just a package, but the eventual goal is to be incorporated into haskell prime. Some salient characteristics of the new type-class hierarchy are:</p>
<dl>
<dt>Removes partial functions</dt>
<dd>The Haskell Language Report <a href="http://www.haskell.org/onlinereport/haskell2010/haskellch6.html#x13-1310006.3.4">section 6.3.4</a> defines <code>pred</code>, <code>succ</code>, <code>fromEnum</code>, and <code>toEnum</code> to be partial functions when the type is <code>Bounded</code>, but this is unacceptable. The new classes remove this problem by correcting the type signatures for these functions.</dd>
<dt>Generalizes the notion of enumeration</dt>
<dd>Rather than requiring that the type is linearly enumerable, we distinguish between forward enumeration (which allows for multiple predecessors) and backward enumeration (which allows for multiple successors).</dd>
<dt>Adds new functions: <code>enumDownFrom</code>, <code>enumDownFromTo</code></dt>
<dd>One of the big problems with the partiality of <code>pred</code> is that there is no safe way to enumerate downwards since in the border case <code>enumFromThen x (pred x)</code> will throw an error rather than evaluating to <code>[x]</code> as desired. These new functions remove this problem.</dd>
<dt>Removes the requirement...</dt>
<dd>...that the enumeration order coincides with the
<code>Ord</code> ordering (if one exists). Though, of course, it's advisable to keep
them in sync if possible, for your sanity.</dd>
<dt>Ensures that the notion of enumeration is well-defined</dt>
<dd>This much-needed rigor clarifies the meaning of enumeration. In addition, it rules out instances for <code>Float</code> and <code>Double</code> which are highly problematic and often confuse newcomers to Haskell. Unfortunately, this rigor does render the instance for <code>Ratio</code> problematic. However, <code>Ratio</code> instances <i>can</i> be provided so long as the base type is enumerable (and <code>Integral</code>, naturally); but they must be done in an obscure order that does not coincide with <code>Ord</code>.
<dt>The obscure order required for well-defined enumeration of <code>Ratio</code> is provided.</dt>
</dd></dl>
<h3>Links</h3>
<ul>
<li>Homepage: <a href="http://code.haskell.org/~wren/">http://code.haskell.org/~wren/</a></li>
<li>Hackage: <a href="http://hackage.haskell.org/package/prelude-safeenum">http://hackage.haskell.org/package/prelude-safeenum</a></li>
<li>Darcs: <a href="http://community.haskell.org/~wren/prelude-safeenum">http://community.haskell.org/~wren/prelude-safeenum</a></li>
<li>Haddock: <a href="http://community.haskell.org/~wren/prelude-safeenum/dist/doc/html/prelude-safeenum/">Darcs version</a></li>
</ul><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=83994" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/83994.htmlhaskellcode releasepublic0http://winterkoninkje.dreamwidth.org/81209.htmlMon, 07 Jan 2013 01:22:57 GMTFinite sets
http://winterkoninkje.dreamwidth.org/81209.html
<p>So, I just encountered a <a href="http://hackage.haskell.org/packages/archive/countable/0.1/doc/html/Data-Searchable.html#v:assemble">most delicious type</a> the other day:<p>
<blockquote><code><pre>class Finite a where
assemble :: Applicative f => (a -> f b) -> f (a -> b)</pre></code></blockquote>
<p>What's so nice about it is that the only way you can implement it is if the type <code>a</code> is in fact finite. (But see the notes.) So the questions are:</p>
<ul>
<li>Can you see why?</li>
<li>Can you figure out how to implement it for some chosen finite type?</li>
<li>Can you figure out how to implement it in general, given a list of all the values? (you may assume <code>Eq a</code> for this one)</li>
<li>Can you figure out how to get a list of all the values, given some arbitrary implementation of <code>assemble</code>?</li>
</ul>
<span class="cuttag_container"><span style="display: none;" id="span-cuttag___1" class="cuttag"></span><b>( <a href="http://winterkoninkje.dreamwidth.org/81209.html#cutid1">A trivial note</a> )</b><div style="display: none;" id="div-cuttag___1" aria-live="assertive"></div></span>
<span class="cuttag_container"><span style="display: none;" id="span-cuttag___2" class="cuttag"></span><b>( <a href="http://winterkoninkje.dreamwidth.org/81209.html#cutid2">A big note, also a hint perhaps</a> )</b><div style="display: none;" id="div-cuttag___2" aria-live="assertive"></div></span></p></p><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=81209" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/81209.htmlhaskellgeek snipingcodingpublic7http://winterkoninkje.dreamwidth.org/78333.htmlMon, 19 Mar 2012 15:58:48 GMTANN: unification-fd 0.7.0
http://winterkoninkje.dreamwidth.org/78333.html
<a name="20120319-top"></a>
<h3>unification-fd 0.7.0</h3>
<p>The unification-fd package offers generic functions for single-sorted first-order structural unification (think of programming in Prolog, or of the metavariables in type inference)<a href="#20120319-1">[1]</a><a href="#20120319-2">[2]</a>. The library <i>is</i> sufficient for implementing higher-rank type systems à la <a href="#20120319-spj">Peyton Jones, Vytiniotis, Weirich, Shields</a>, but bear in mind that unification variables are the metavariables of type inference— not the type-variables.</p>
<p>An effort has been made to make the package as portable as possible. However, because it uses the <code>ST</code> monad and the mtl-2 package it can't be H98 nor H2010. However, it only uses the following common extensions which should be well supported<a href="#20120319-3">[3]</a>:</p>
<blockquote><pre>Rank2Types
MultiParamTypeClasses
FunctionalDependencies -- Alas, necessary for type inference
FlexibleContexts -- Necessary for practical use of MPTCs
FlexibleInstances -- Necessary for practical use of MPTCs
UndecidableInstances -- For Show instances due to two-level types</pre></blockquote>
<h3>Changes (since 0.6.0)</h3>
<p>This release is another major API breaking release. Apologies, but things are a lot cleaner now and hopefully the API won't break again for a while. The biggest change is that the definition of terms has changed from the previous:</p>
<code><pre> data MutTerm v t
= MutVar !v
| MutTerm !(t (MutTerm v t))</pre></code>
<p>To the much nicer:</p>
<code><pre> data UTerm t v
= UVar !v
| UTerm !(t (UTerm t v))</pre></code>
<p>The old mnemonic of "mutable terms" was inherited from the code's previous life implementing a logic programming language; but when I was playing around with implementing a type checker I realized that the names don't really make sense outside of that original context. So the new mnemonic is "unification terms". In addition to being a bit shorter, it should help clarify the separation of concerns (e.g., between unification variables vs lambda-term variables, type variables, etc.).</p>
<p>The swapping of the type parameters is so that <code>UTerm</code> can have instances for <code>Functor</code>, <code>Monad</code>, etc. This change should've been made along with the re-kinding of variable types back in version 0.6.0, since the <code>UTerm</code> type is the free monad generated by <code>t</code>. I've provided all the category theoretic instances I could imagine some plausible reason for wanting. Since it's free, there are a bunch more I haven't implemented since they don't really make sense for structural terms (e.g., <code>MonadTrans</code>, <code>MonadWriter</code>, <code>MonadReader</code>, <code>MonadState</code>, <code>MonadError</code>, <code>MonadCont</code>). If you can come up with some compelling reason to want those instances, I can add them in the future.</p>
<p>Since the order of type parameters to <code>BindingMonad</code>, <code>UnificationFailure</code>, <code>Rank</code>, and <code>RankedBindingMonad</code> was based on analogy to the order for terms, I've also swapped the order in all of them for consistency.</p>
<p>I've removed the <code>eqVar</code> method of the <code>Variable</code> class, and instead added an <code>Eq</code> superclass constraint. Again, this should've happened with the re-kinding of variables back in version 0.6.0. A major benefit of this change is that now you can use all those library functions which require <code>Eq</code> (e.g., many of the set-theoretic operations on lists, like <code>(\\)</code> and <code>elem</code>).</p>
<p>I've added new functions: <code>getFreeVarsAll</code>, <code>applyBindingsAll</code>, <code>freshenAll</code>; which are like the versions without "All", except they're lifted to operate over <code>Foldable</code>/<code>Traversable</code> collections of terms. This is crucial for <code>freshenAll</code> because it allows you to retain sharing of variables among the collection of terms. Whereas it's merely an optimization for the others (saves time for <code>getFreeVarsAll</code>, saves space for <code>applyBindingsAll</code>).</p>
<p>The type of the <code>seenAs</code> function has also changed, to ensure that variables can only be seen as structure rather than as any <code>UTerm</code>.</p>
<p>Thanks to Roman Cheplyaka for suggesting many of these changes.</p>
<h3>Description</h3>
<p>The unification API is generic in the type of the structures being unified and in the implementation of unification variables, following the two-level types pearl of <a href="#20120319-sheard2001">Sheard (2001)</a>. This style mixes well with <a href="#20120319-swierstra">Swierstra (2008)</a>, though an implementation of the latter is not included in this package.</p>
<p>That is, all you have to do is define the functor whose fixed-point is the recursive type you're interested in:</p>
<code><pre> -- The non-recursive structure of terms
data S a = ...
-- The recursive term type
type PureTerm = Fix S</pre></code>
<p>And then provide an instance for <code>Unifiable</code>, where <code>zipMatch</code> performs one level of equality testing for terms and returns the one-level spine filled with pairs of subterms to be recursively checked (or <code>Nothing</code> if this level doesn't match).</p>
<code><pre> class (Traversable t) => Unifiable t where
zipMatch :: t a -> t b -> Maybe (t (a,b))</pre></code>
<p>The choice of which variable implementation to use is defined by similarly simple classes <code>Variable</code> and <code>BindingMonad</code>. We store the variable bindings in a monad, for obvious reasons. In case it's not obvious, see <a href="#20120319-dijkstra">Dijkstra et al. (2008)</a> for benchmarks demonstrating the cost of naively applying bindings eagerly.</p>
<p>There are currently two implementations of variables provided: one based on <code>STRef</code>s, and another based on a state monad carrying an <code>IntMap</code>. The former has the benefit of O(1) access time, but the latter is plenty fast and has the benefit of supporting backtracking. Backtracking itself is provided by the logict package and is described in <a href="#20120319-oleg">Kiselyov et al. (2005)</a>.</p>
<p>In addition to this modularity, unification-fd implements a number of optimizations over the algorithm presented in <a href="#20120319-sheard2001">Sheard (2001)</a>— which is also the algorithm presented in <a href="#20120319-cardelli">Cardelli (1987)</a>.</p>
<ul>
<li>Their implementation uses path compression, which we retain. Though we modify the compression algorithm in order to make sharing observable.</li>
<li>In addition, we perform aggressive opportunistic observable sharing, a potentially novel method of introducing even more sharing than is provided by the monadic bindings. Basically, we make it so that we can use the observable sharing provided by the modified path compression as much as possible (without introducing any new variables).</li>
<li>And we remove the notoriously expensive occurs-check, replacing it with visited-sets (which detect cyclic terms more lazily and without the asymptotic overhead of the occurs-check). A variant of unification which retains the occurs-check is also provided, in case you really need to fail fast.</li>
<li>Finally, a highly experimental branch of the API performs <i>weighted</i> path compression, which is asymptotically optimal. Unfortunately, the current implementation is quite a bit uglier than the unweighted version, and I haven't had a chance to perform benchmarks to see how the constant factors compare. Hence moving it to an experimental branch.</li>
</ul>
<p>These optimizations pass a test suite for detecting obvious errors. If you find any bugs, do be sure to let me know. Also, if you happen to have a test suite or benchmark suite for unification on hand, I'd love to get a copy.</p>
<h3>Notes and limitations</h3>
<p><a name="20120319-1"></a>[1] At present the library does not appear amenable for implementing higher-rank unification itself; i.e., for higher-ranked metavariables, or higher-ranked logic programming. To be fully general we'd have to abstract over which structural positions are co/contravariant, whether the unification variables should be predicative or impredicative, as well as the isomorphisms of moving quantifiers around. It's on my todo list, but it's certainly non-trivial. If you have any suggestions, feel free to contact me. <a href="#20120319-top">[back]</a></p>
<p><a name="20120319-2"></a>[2] At present it is only suitable for single-sorted (aka untyped) unification, à la Prolog. In the future I aim to support multi-sorted (aka typed) unification, however doing so is complicated by the fact that it can lead to the loss of MGUs; so it will likely be offered as an alternative to the single-sorted variant, similar to how the weighted path-compression is currently offered as an alternative. <a href="#20120319-top">[back]</a></p>
<p><a name="20120319-3"></a>[3] With the exception of fundeps which are notoriously difficult to implement. However, they are supported by Hugs and GHC 6.6, so I don't feel bad about requiring them. Once the API stabilizes a bit more I plan to release a unification-tf package which uses type families instead, for those who feel type families are easier to implement or use. There have been a couple requests for unification-tf, so I've bumped it up on my todo list. <a href="#20120319-top">[back]</a></p>
<h3>References</h3>
<dl>
<dt><a name="20120319-cardelli"></a>Luca Cardelli (1987)</dt>
<dd><i>Basic polymorphic typechecking</i>.
Science of Computer Programming, 8(2): 147–172.</dd>
<dt><a name="20120319-dijkstra"><a href="http://www.cs.uu.nl/research/techreps/repo/CS-2008/2008-027.pdf">Atze Dijkstra, Arie Middelkoop, S. Doaitse Swierstra (2008)</a></a></dt>
<dd><i>Efficient Functional Unification and Substitution</i>.
Technical Report UU-CS-2008-027, Utrecht University.</dd>
<dt><a name="20120319-spj"></a><a href="http://research.microsoft.com/en-us/um/people/simonpj/papers/higher-rank/putting.pdf">Simon Peyton Jones, Dimitrios Vytiniotis, Stephanie Weirich, Mark
Shields (2007)</a></dt>
<dd><i>Practical type inference for arbitrary-rank types</i>.
JFP 17(1). The online version has some minor corrections/clarifications.</dd>
<dt><a name="20120319-oleg"><a href="http://www.cs.rutgers.edu/~ccshan/logicprog/LogicT-icfp2005.pdf">Oleg Kiselyov, Chung-chieh Shan, Daniel P. Friedman, and Amr Sabry (2005)</a></a></dt>
<dd><i>Backtracking, Interleaving, and Terminating Monad Transformers</i>.
ICFP.</dd>
<dt><a name="20120319-sheard2001"><a href="http://web.cecs.pdx.edu/~sheard/papers/generic.ps">Tim Sheard (2001)</a></a></dt>
<dd><i>Generic Unification via Two-Level Types and Paramterized Modules</i>,
Functional Pearl. ICFP.</dd>
<dt><a name="20120319-sheard2004"><a href="http://web.cecs.pdx.edu/~sheard/papers/JfpPearl.ps">Tim Sheard and Emir Pasalic (2004)</a></a></dt>
<dd><i>Two-Level Types and Parameterized Modules</i>.
JFP 14(5): 547–587.
This is an expanded version of Sheard (2001) with new examples.</dd>
<dt><a name="20120319-swierstra"><a href="http://www.cs.ru.nl/~wouters/Publications/DataTypesALaCarte.pdf">Wouter Swierstra (2008)</a></a></dt>
<dd><i>Data types à la carte</i>,
Functional Pearl. JFP 18: 423–436.</dd>
</dl>
<h3>Links</h3>
<ul>
<li>Homepage: <a href="http://code.haskell.org/~wren/">http://code.haskell.org/~wren/</a></li>
<li>Hackage: <a href="http://hackage.haskell.org/package/unification-fd">http://hackage.haskell.org/package/unification-fd</a></li>
<li>Darcs: <a href="http://community.haskell.org/~wren/unification-fd">http://community.haskell.org/~wren/unification-fd</a></li>
<li>Haddock: <a href="http://community.haskell.org/~wren/unification-fd/dist/doc/html/unification-fd/">Darcs version</a></li>
</ul><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=78333" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/78333.htmlunificationcode releasehaskellpublic0http://winterkoninkje.dreamwidth.org/77021.htmlSun, 29 Jan 2012 05:00:06 GMTANN: bytestring-lexing 0.3.0
http://winterkoninkje.dreamwidth.org/77021.html
<h3>bytestring-lexing 0.3.0</h3>
<p>The bytestring-lexing package offers efficient reading and packing of common types like <code>Double</code> and <code>Integral</code> types.</p>
<h3>Administrative Changes (since 0.2.1)</h3>
<p>Change of maintainer. Don Stewart handed maintainership of the package over to myself when I voiced interest.</p>
<p>Change of repo type. The old repo for the package used Darcs-1 style patches. I've converted the repository to Darcs-2 hashed. This means that the new repository cannot exchange patches with the old Darcs-1 repo (or any other Darcs-2 conversions that may be floating around out there). So anyone who's interested in contributing should scrap their local copies and get the new repo.</p>
<h3>Code Changes (since 0.2.1)</h3>
<p>Added <a href="http://community.haskell.org/~wren/bytestring-lexing/dist/doc/html/bytestring-lexing/Data-ByteString-Lex-Integral.html"><code>Data.ByteString.Lex.Integral</code></a> which provides efficient implementations for reading and packing/showing integral types in ASCII-compatible formats including decimal, hexadecimal, and octal.
<p>The <code>readDecimal</code> function in particular has been highly optimized. The new version is wicked fast and perfectly suitable for hot code locations like parsing headers for HTTP servers like Warp. In addition, attention has been paid to ensuring that parsing is efficient for larger than native types like Int64 on 32-bit systems (including 64-bit OS X), as well as Integer. The optimization of this function was done in collaboration with Erik de Castro Lopo, Vincent Hanquez, and Christoph Breitkopf following a <a href="http://www.mega-nerd.com/erikd/Blog/CodeHacking/Haskell/read_int.html">blog post by Erik</a> and <a href="http://www.reddit.com/r/haskell/comments/otwxe/benchmarking_and_quickchecking_readint/">ensuing discussion on Reddit</a>.</p>
<p>A <a href="http://code.haskell.org/~wren/bytestring-lexing/test/bench/readDecimal.html">Criterion report</a> is available for 64-bit Intel OS X running 32-bit GHC 6.12.1. The benchmark is included in the repo and has also been run on 64-bit GHC 7 systems, which differ primarily in not showing slowdown for Int64 vs Int (naturally). If you're curious about the different implementations:</p>
<ul>
<li>readIntBS / readIntegerBS --- are the readInt and readInteger functions in Data.ByteString</li>
<li>readDecimalOrig (correct) --- was my original implementation, prior to collaboration with Erik, Vincent, and Christoph.</li>
<li>readIntegralMH (buggy) --- or rather a non-buggy version very much like it, is the implementation currently used in Warp.</li>
<li>readDecimal (current) --- is the current implementation used in this package.</li>
</ul>
<h3>Links</h3>
<ul>
<li>Homepage: <a href="http://code.haskell.org/~wren/">http://code.haskell.org/~wren/</a></li>
<li>Hackage: <a href="http://hackage.haskell.org/package/bytestring-lexing">http://hackage.haskell.org/package/bytestring-lexing</a></li>
<li>Darcs: <a href="http://community.haskell.org/~wren/bytestring-lexing">http://community.haskell.org/~wren/bytestring-lexing</a></li>
<li>Haddock: <a href="http://community.haskell.org/~wren/bytestring-lexing/dist/doc/html/bytestring-lexing/">Darcs version</a></li>
</ul></p><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=77021" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/77021.htmlhaskellcode releasepublic0http://winterkoninkje.dreamwidth.org/76675.htmlSun, 29 Jan 2012 04:54:15 GMTANN: exact-combinatorics
http://winterkoninkje.dreamwidth.org/76675.html
<h3>exact-combinatorics 0.2.0</h3>
<p>The exact-combinatorics package offers efficient <i>exact</i> computation of common combinatorial functions like the binomial coefficients and factorial. (For fast <i>approximations</i>, see the <a href="http://hackage.haskell.org/package/math-functions">math-functions</a> package instead.)</p>
<h3>Description</h3>
<dl>
<dt><a href="http://community.haskell.org/~wren/exact-combinatorics/dist/doc/html/exact-combinatorics/Math-Combinatorics-Exact-Primes.html"><code>Math.Combinatorics.Exact.Primes</code></a></dt>
<dd>Provides the prime numbers via Runciman's lazy wheel sieve algorithm. Provided here since efficient algorithms for combinatorial functions often require prime numbers. The current version memoizes the primes as an infinite list CAF, which could lead to memory leaks in long-running programs with irregular access to large primes. I'm looking into a GHC patch to allow resetting individual CAFs from within compiled programs so that you can explicitly decide when to un-memoize the primes. (In GHCi when you reload a module all the CAFs are reset. However, there's no way to access this feature from within compiled programs as yet.)</dd>
<dt><a href="http://community.haskell.org/~wren/exact-combinatorics/dist/doc/html/exact-combinatorics/Math-Combinatorics-Exact-Binomial.html"><code>Math.Combinatorics.Exact.Binomial</code></a></dt>
<dd>Offers a fast computation of the binomial coefficients based on the prime factorization of the result. As it turns out, it's easier to compute the prime factorization of the answer than it is to compute the answer directly! And you don't even need the factorial function to do so. Albeit, with a fast factorial function, the naive definition of binomial coefficients gives this algorithm a run for its money.</dd>
<dt><a href="http://community.haskell.org/~wren/exact-combinatorics/dist/doc/html/exact-combinatorics/Math-Combinatorics-Exact-Factorial.html"><code>Math.Combinatorics.Exact.Factorial</code></a></dt>
<dd>Offers a fast computation of factorial numbers. As <a href="http://www.luschny.de/math/factorial/FastFactorialFunctions.htm">Peter Luschny comments</a>, the factorial function is often shown as a classic example of recursive functions, like addition of Peano numbers, however that naive way of computing factorials is extremely inefficient and does a disservice to those learning about recursion. The current implementation uses the split-recursive algorithm which is more than sufficient for casual use. I'm working on implementing the parallel prime-swing algorithm, which should be a bit faster still.</dd>
</dl>
<h3>Links</h3>
<ul>
<li>Homepage: <a href="http://code.haskell.org/~wren/">http://code.haskell.org/~wren/</a></li>
<li>Hackage: <a href="http://hackage.haskell.org/package/exact-combinatorics">http://hackage.haskell.org/package/exact-combinatorics</a></li>
<li>Darcs: <a href="http://community.haskell.org/~wren/exact-combinatorics">http://community.haskell.org/~wren/exact-combinatorics</a></li>
<li>Haddock: <a href="http://community.haskell.org/~wren/exact-combinatorics/dist/doc/html/exact-combinatorics/">Darcs version</a></li>
</ul><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=76675" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/76675.htmlhaskellcode releasepublic0http://winterkoninkje.dreamwidth.org/76446.htmlSun, 29 Jan 2012 04:49:47 GMTANN: data-or
http://winterkoninkje.dreamwidth.org/76446.html
<h3>data-or 1.0.0</h3>
<p>The data-or package offers a data type for non-exclusive disjunction. This is helpful for things like a generic merge function on sets/maps which could be union, mutual difference, etc. based on which <code>Or</code> value a function argument returns. Also useful for non-truncating zips (cf. <code>zipOr</code>) and other cases where you sometimes want an <code>Either</code> and sometimes want a pair. </p>
<h3>Links</h3>
<ul>
<li>Homepage: <a href="http://code.haskell.org/~wren/">http://code.haskell.org/~wren/</a></li>
<li>Hackage: <a href="http://hackage.haskell.org/package/data-or">http://hackage.haskell.org/package/data-or</a></li>
<li>Darcs: <a href="http://community.haskell.org/~wren/data-or">http://community.haskell.org/~wren/data-or</a></li>
<li>Haddock: <a href="http://community.haskell.org/~wren/data-or/dist/doc/html/data-or/">Darcs version</a></li>
</ul><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=76446" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/76446.htmlcode releasehaskellpublic0http://winterkoninkje.dreamwidth.org/76245.htmlSun, 29 Jan 2012 04:47:25 GMTANN: pointless-fun
http://winterkoninkje.dreamwidth.org/76245.html
<h3>pointless-fun 1.1.0</h3>
<p>The pointless-fun package offers some common point-free combinators (common for me at least).</p>
<h3>Description</h3>
<p>Perhaps the most useful is that it packages up Matt Hellige's classic multicomposition trick[1]. These combinators allow you to easily modify the types of a many-argument function with syntax that looks like giving type signatures. For example,</p>
<code><pre> foo :: A -> B -> C
albert :: X -> A
beth :: Y -> B
carol :: C -> Z
bar :: X -> Y -> Z
bar = foo $:: albert ~> beth ~> carol</pre></code>
<p>I've found this to be especially helpful for defining non-derivable type class instances for newtypes since it both abstracts away the plumbing and also makes explicit what you mean.</p>
<p>Other prevalent combinators include, <code>(.:)</code> for binary composition:</p>
<code><pre> (f .: g) x y = f (g x y)
-- or,
f .: g = curry (f . uncurry g)</pre></code>
<p>This is the same as the common idiom <code>(f .) . g</code> but more easily extended to multiple uses, due to the fixity declaration.<p>
<p>And <code>(.!)</code> for function composition which calls the right-hand function eagerly; i.e., making the left-hand function strict in its first argument.</p>
<code><pre> (f .! g) x = f $! g x</pre></code>
<p>This defines the composition for the sub-category of strict Haskell functions. If the <code>Functor</code> class were parameterized by the domain and codomain categories (e.g., a regular <code>Functor f</code> would be <code>CFunctor (->) (->) f</code> instead) then this would allow us to define functors <code>CFunctor (->) (!->) f</code> where <code>fmap f . fmap g = fmap (f .! g)</code>
<p>[1] <a href="http://matt.immute.net/content/pointless-fun">http://matt.immute.net/content/pointless-fun</a></p>
<h3>Links</h3>
<ul>
<li>Homepage: <a href="http://code.haskell.org/~wren/">http://code.haskell.org/~wren/</a></li>
<li>Hackage: <a href="http://hackage.haskell.org/package/pointless-fun">http://hackage.haskell.org/package/pointless-fun</a></li>
<li>Darcs: <a href="http://community.haskell.org/~wren/pointless-fun">http://community.haskell.org/~wren/pointless-fun</a></li>
<li>Haddock: <a href="http://community.haskell.org/~wren/pointless-fun/dist/doc/html/pointless-fun/">Darcs version</a></li>
</ul></p></p></p><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=76245" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/76245.htmlcode releasehaskellpublic0http://winterkoninkje.dreamwidth.org/74798.htmlSun, 02 Oct 2011 00:15:10 GMTProbability Smoothing for NLP
http://winterkoninkje.dreamwidth.org/74798.html
<p>I finally got around to posting the slides for a talk I gave twice this summer: <a href="http://llama.freegeek.org/~wren/pubs/smoothing_ammcs2011.pdf">Probability Smoothing for NLP: A case study for functional programming and little languages</a>. The first version of the talk was presented at the <a href="http://www.cas.mcmaster.ca/~anand/DSL2011.html">McMaster Workshop on Domain Specific Lanaguages</a> (and Ed Kmett has posted <a href="http://www.reddit.com/r/haskell/comments/kxc4h/a_dsl_for_probability_smoothing/">a video of that version</a> on YouTube) with the presentation focused on EDSLs, with smoothing given as an example. The second version was presented at the <a href="http://www.ammcs2011.wlu.ca/SS-SSD.html">AMMCS minisymposium on Progress and Prospects in Model-Based Scientific Software Development</a>, where the focus was more on the domain itself and how the use of a DSL allows ensuring correctness, modularity, and maintainability of code for developing probability models. The slides are essentially the same for both talks, with the benchmarks updated a bit in the latter.</p>
<p>As you may have surmised, this is but a small facet of the Posta project I was working on last year. I had meant to submit it as a functional pearl for ICFP, but the timing didn't work out for that. After giving the McMaster version of the talk, Ed convinced me that I should publish the code for the smoothing DSL separately from the rest of Posta. So he's the one to blame about my being so slow in releasing the Posta code I promised this summer. Though seriously, I'd been considering breaking up and reorganizing the code anyways. Now that I'm back from ICFP and all my traveling over the summer, I hope to get that code pushed out soon. Sorry for the delay y'all.</p><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=74798" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/74798.htmlpos taggingdslhaskellnlpdesignpublic0http://winterkoninkje.dreamwidth.org/74628.htmlFri, 30 Sep 2011 03:52:47 GMTまだ東京が格好いい
http://winterkoninkje.dreamwidth.org/74628.html
<p>All last week I was in Tokyo to attend ICFP and associated workshops. It was nice to finally meet a bunch of people I've been talking with online for the last few years. And I met up with Ken Shan and Oleg Kiselyov again, which is always a pleasure. Unlike last time I was in Japan, I didn't get too much time to explore and go sightseeing. I got to explore Chiyoda, which I missed last time around, and I made sure to do two of the most important things: (1) eat some okonomiyaki, (2) visit Akihabara to buy some new manga.</p>
<p>My newest acquisition is <a href="http://ja.wikipedia.org/wiki/銃姫"><i>銃姫 ("Gun Princess") Phantom Pain</i></a>, which I'm rather enjoying so far. Anything that starts off with an execution, spell-casting based on Buddhist mantras, and a prolonged diatribe on why one of the characters is a good-for-nothing incompetent layabout, can't be half bad :) Unfortunately, I only got the first two volumes, so I'll finish them all too soon. So far it's proving easier to read than my previous acquisition (<a href="http://en.wikipedia.org/wiki/Peace_Maker_(manga)"><i>Peace Maker 鐵</i></a>), though I'm not sure if that's due to getting better at Japanese or because 鐵 is written in a particular style. I definitely noticed my deterioration in fluency since five years ago; grammar's fine, but my vocab is abysmal. I need to find a decent way to work on that.</p><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=74628" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/74628.htmltravelingjapangrad schoolhaskellSet Me On Fire ~ Pendulum (Immersion)public0http://winterkoninkje.dreamwidth.org/72842.htmlTue, 12 Jul 2011 10:53:56 GMTANN: unification-fd: simple generic unification
http://winterkoninkje.dreamwidth.org/72842.html
<h3>unification-fd 0.5.0</h3>
<p>The unification-fd package offers generic functions for first-order structural unification (think Prolog programming or Hindley–Milner type inference). I've had this laying around for a few years, so I figured I might as well publish it.</p>
<p>An effort has been made to try to make this package as portable as possible. However, because it uses the <code>ST</code> monad and the mtl-2 package it can't be H98 nor H2010. However, it only uses the following common extensions which should be well supported[1]:</p>
<blockquote><pre>Rank2Types
MultiParamTypeClasses
FunctionalDependencies
FlexibleContexts
FlexibleInstances
UndecidableInstances</pre></blockquote>
<p>[1] With the exception of fundeps which are notoriously difficult to implement. However, they are supported by Hugs and GHC 6.6, so I don't feel bad about requiring it. Once the API stabilizes a bit more I plan to release a unification-tf package which uses type families instead, for those who feel type families are easier to implement or use.</p>
<h3>Description</h3>
<p>The unification API is generic in the type of the structures being unified and in the implementation of unification variables, following the two-level types pearl of Sheard (2001). This style mixes well with Swierstra (2008), though an implementation of the latter is not included in this package.</p>
<p>That is, all you have to do is define the functor whose fixed-point is the recursive type you're interested in:</p>
<code><pre> -- The non-recursive structure of terms
data S a = ...
-- The recursive term type
type PureTerm = Fix S</pre></code>
<p>And then provide an instance for <code>Unifiable</code>, where <code>zipMatch</code> performs one level of equality testing for terms and returns the one-level spine filled with pairs of subterms to be recursively checked (or <code>Nothing</code> if this level doesn't match).</p>
<code><pre> class (Traversable t) => Unifiable t where
zipMatch :: t a -> t b -> Maybe (t (a,b))</pre></code>
<p>The choice of which variable implementation to use is defined by similarly simple classes <code>Variable</code> and <code>BindingMonad</code>. We store the variable bindings in a monad, for obvious reasons. In case it's not obvious, see Dijkstra et al. (2008) for benchmarks demonstrating the cost of naively applying bindings eagerly.</p>
<p>There are currently two implementations of variables provided: one based on <code>STRef</code>s, and another based on a state monad carrying an <code>IntMap</code>. The former has the benefit of O(1) access time, but the latter is plenty fast and has the benefit of supporting backtracking. Backtracking itself is provided by the logict package and is described in Kiselyov et al. (2005).</p>
<p>In addition to this modularity, unification-fd implements a number of optimizations over the algorithm presented in Sheard (2001)— which is also the algorithm presented in Cardelli (1987).</p>
<ul>
<li>Their implementation uses path compression, which we retain. Though we modify the compression algorithm in order to make sharing observable.</li>
<li>In addition, we perform aggressive opportunistic observable sharing, a potentially novel method of introducing even more sharing than is provided by the monadic bindings. Basically, we make it so that we can use the observable sharing provided by the previous optimization as much as possible (without introducing any new variables).</li>
<li>And we remove the notoriously expensive occurs-check, replacing it with visited-sets (which detect cyclic terms more lazily and without the asymptotic overhead of the occurs-check). A variant of unification which retains the occurs-check is also provided, in case you really need to fail fast for some reason.</li>
<li>Finally, a highly experimental branch of the API performs <i>weighted</i> path compression, which is asymptotically optimal. Unfortunately, the current implementation is quite a bit uglier than the unweighted version, and I haven't had a chance to perform benchmarks to see how the constant factors compare. Hence moving it to an experimental branch.</li>
</ul>
<p>I haven't had a chance to fully debug these optimizations, though they pass some of the obvious tests. If you find any bugs, do be sure to let me know. Also, if you happen to have a test suite or benchmark suite for unification on hand, I'd love to get a copy.</p>
<h3>References</h3>
<dl>
<dt>Luca Cardelli (1987)</dt>
<dd><i>Basic polymorphic typechecking</i>.
Science of Computer Programming, 8(2): 147–172.</dd>
<dt><a href="http://www.cs.uu.nl/research/techreps/repo/CS-2008/2008-027.pdf">Atze Dijkstra, Arie Middelkoop, S. Doaitse Swierstra (2008)</a></dt>
<dd><i>Efficient Functional Unification and Substitution</i>,
Technical Report UU-CS-2008-027, Utrecht University.</dd>
<dt><a href="http://www.cs.rutgers.edu/~ccshan/logicprog/LogicT-icfp2005.pdf">Oleg Kiselyov, Chung-chieh Shan, Daniel P. Friedman, and Amr Sabry (2005)</a></dt>
<dd><i>Backtracking, Interleaving, and Terminating Monad Transformers</i>,
ICFP.</dd>
<dt><a href="http://web.cecs.pdx.edu/~sheard/papers/generic.ps">Tim Sheard (2001)</a></dt>
<dd><i>Generic Unification via Two-Level Types and Paramterized Modules</i>,
Functional Pearl, ICFP.</dd>
<dt><a href="http://web.cecs.pdx.edu/~sheard/papers/JfpPearl.ps">Tim Sheard and Emir Pasalic (2004)</a></dt>
<dd><i>Two-Level Types and Parameterized Modules</i>.
JFP 14(5): 547–587.
This is an expanded version of Sheard (2001) with new examples.</dd>
<dt><a href="http://www.cs.ru.nl/~wouters/Publications/DataTypesALaCarte.pdf">Wouter Swierstra (2008)</a></dt>
<dd><i>Data types a la carte</i>,
Functional Pearl. JFP 18: 423–436.</dd>
</dl>
<h3>Links</h3>
<ul>
<li>Homepage: <a href="http://code.haskell.org/~wren/">http://code.haskell.org/~wren/</a></li>
<li>Hackage: <a href="http://hackage.haskell.org/package/unification-fd">http://hackage.haskell.org/package/unification-fd</a></li>
<li>Darcs: <a href="http://community.haskell.org/~wren/unification-fd">http://community.haskell.org/~wren/unification-fd</a></li>
<li>Haddock: <a href="http://community.haskell.org/~wren/unification-fd/dist/doc/html/unification-fd/">Darcs version</a></li>
</ul><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=72842" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/72842.htmlunificationcode releasehaskellpublic0http://winterkoninkje.dreamwidth.org/72459.htmlFri, 03 Jun 2011 12:25:52 GMTTo Canada!
http://winterkoninkje.dreamwidth.org/72459.html
<p>Hello all. This summer (that is, next week) I'm heading up to Canada to teach a DSL bootcamp at McMaster along with Edward Kmett, and staying afterwards for a couple months. Rather than dealing with the TSA's bullshit I've decided to take the train up, which is cheaper and only slightly slower once you account for all the nonsense. But, this means I'll be spending a good deal of time laying over in Chicago and Buffalo. I know a bunch of you have traveled around, and some of you may happen to be there when I am, so: anyone want to meet up while I'm there? or knows of some good places to eat and spend time when visiting?</p>
<p>I'll be in Chicago around 4pm–9:30pm on June 10, and 9:45am–3:20pm on July 29. And in Buffalo around 9am–3pm on June 11, and 1:30pm–midnight on July 28. So that's about five hours each stop, ten hours coming back through Buffalo. Amtrak being what it is, some of this time might get eaten up by poor track scheduling, but I figure I'll still have a good deal of time regardless.</p>
<p>Also, anyone have recommendations for places to eat in downtown Indianapolis? I have an hour to kill around noon when heading up.</p><br /><br /><img src="http://www.dreamwidth.org/tools/commentcount?user=winterkoninkje&ditemid=72459" width="30" height="12" alt="comment count unavailable" style="vertical-align: middle;"/> commentshttp://winterkoninkje.dreamwidth.org/72459.htmlhaskelltravelingpersonalpublic0